Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
638 lines (493 sloc) 21.8 KB
OpenCoin Project Category: Draft
Status of this Memo
This draft is work in heavy progress. Do not consider it's content
stable in any sense as long as this note is present. Get in touch with [1] and fetch a recent copy [TBD].
Copyright Notice
Copyright (2012) N. Toedtmann, J. H. Baach, J. K. Muennich, J. Suhr.
This document describes the OpenCoin protocol which seeks to implement
David Chaum's concept of digital cash [3].
Table of Contents
1. Introduction
1.1 Entities
Opencoin consists of the following entities:
* _Currency_ is defined by an issuer and it can be used for various
use cases (e.g. digital cash, balloting, vouchers)
* _Issuer_ defines a currency and provides the following
* _Certificate Authority (CA)_: masterkey, CDD ("currency description
* _Mint_ is a service which signs blinded payload hashes and creates
blinded signatures
* _Double Spending Data Base (DSDB)_ stores the serials (and
potentially signatures) of all renewed coins in order to
prevent its double spending. WHO IS QUERING THE DSDB? THE
* Issuer services are:
* _Information_ provides information of the issuer and
currency (e.g. CDDs, mint key certificates, certificate
revocation list (CRL))
* _Validate_ is used by clients to request coins without
having coins already. Usually the validate service may ask
the Authorizer whether to approve or reject particular
requests. This service is usually used to convert value of
another payment system to opencoin.
* _Renew_ is used by clients to renew existing coins. The
client needs to provide valid coins and blinds of the same
total value. The Renew service validates against the DSDB
and in the valid case mints the provided blinds. Usually
clients who received coins during a transaction are
supposed to call this service to ensure the received coins
are valid (and not spend before).
* _Invalidate_ converts valid coins into a different payment
* _Authorizer_ verifies validation requests and responds to the
mint with an authorization or rejection. This component may
interface with other payment/account systems in order to
convert value from another payment system to the particular
* _Client_ is the application of a user which interacts with the
other entities. It manages the wallet.
* _Tokens_ are payloads, blinds, and coins. Their lifecycle is:
* Client: creates yet unsigned payload ("blank")
* Client: ==[padded hash]==> payload hash
* Client: ==[blind]==> blinded payload hash ("blind")
* Mint: ==[sign]==> signature of blinded payload hash
a.k.a blind signature of payload hash
a.k.a ("blind signature")
* Client: ==[unblind]==> signature of payload hash
* Client: ==[combine with unsigned payload]==> signed payload
a.k.a. "coin"
* _Payload_ is prepared by the client (and while it doesn't have a
signature it's called "blank")
* Blind is the blinded padded hash of a payload. It is send from
the client to the mint.
* Coin consists of a payload and the mint's signature of the
particular payload.
* Wallet is a file which contains tokens (e.g. coins) and is managed
by the client.
2. The OpenCoin protocol
2.1 Containers
2.1.1 Generic format:
struct <name> = {
type: '<type name>',
<type-dependent field> [, <type-dependent field>[...]]
2.1.2 General Conditions
The following limits are defined to allow clients handling data more easily.
* Maximum list length = 2^16 entries. Lists are not sorted unless
mentioned explicitly. It is used for denominations and service URLs for instance.
* signed (?) integers (weights, denomination, display factor) =
4 bytes (32 bit)
* String length = 2^16 bytes
* Date: UTC
* date: ISO8601, extended format, always UTC,
e.g. 2009-01-01T12:00:00Z
* Integer?
* crypto_random_number: 128 bit, high entropy random number
Container signing:
* signature = sign(padded_hash(bencode_serialized(container)))
signed_container = {type:<type>,
* UTF-8 encoding. Tokens and gwop are additionally base64 encoded
(RFC 4648).
* exact all fields are required but some may be empty if mentioned
in the spec. All field names are unique [[we don't want double
fields, or unlimited additional fields. People who want to "abuse"
the CDD should use additional info field]]
2.1.2 The currency description document (CDD)
For simplicity reasons, the terminology defines a CDD as containing a
struct: CDD
* struct: signed data
* type = 'cdd'
* protocol version = URL:
[[An URL to the protocol standard being used for the coin.]]
* cdd location = URL: )
[[This is not an identifier. This URL is required to be
embedded in payloads. DOES IT NEED TO BE UNIQUE FOR EACH CDD
* cdd serial number = positive integer, incremental [[Purpose is
to distinguish different versions of a CDD]]
* cdd signing date = date
* cdd expiry date = date or None [[we want to allow a controlled
rollover, temporary systems. This is the expiry date of the
* currency name = string: [[e.g. Open Cent]]
* currency divisor = positive integer
[[value/display divisor == display value in unit display name;
For instance a divisor of 100 to express cent values for Euro
or Dollar ]]
* info service = weighted_list_of_URLs
* CDDs
* mint key certificates
* certificate revocation list (CRL)
* icons and other funky shiny stuff
* [[Weighted to also allows round-robin, but also proper preference]]
* validation service = weighted_list_of_URLs:
(1, xmpp:// ]
* renewal service = weighted_list_of_URLs
* invalidation service = weighted_list_of_URLs
* denominations = list_of_integers: 1, 2, 5, 10, 20, 50, 100,
200, 500, 1000 [[negative denominations may be allowed but
clients may not be able to handle it and reject currency]]
* issuer cipher suite = string: HASH-SIGN-BLINDING
* example: SHA512-RSA-CHAUM83
* issuer public master key = JSON dict construct, depending on
used crypto algorithm. 7bit_serialize(sign-key: pM))
* [[it seems that the only valid identifier of the currency is
the master key. A shorter hash of this key may be displayed to
allow its manual verification by users. This key MUST be
changed if hash or signing algorithm changes.]]
* additional info = string or None [[there might be additions]]
* 7bit_serialize(sign(sM,hash(serialize(signed data))))
A complete CDD serialized into json may look like this:
{'type' : 'cdd',
'protocol_version' : '',
'cdd_location' : '',
'issuer_public_master_key' : 'fdslllfdsajfd9s9ekfjlfsdajlkjfds',
'issuer_cipher_suite' : 'SHA512-RSA-CHAUM83',
'cdd_serial' : 1,
'cdd_signing_date' : '2012-06-01T18:30',
'cdd_expiry_date' : '2012-06-01T18:30',
'currency_name' : 'My Currency',
'currency_divisor' : 100,
'info_service' : [[30,'http://some.server'],
'validation_service' : [[10,'http://some.server'],
'renewal_service' : [[20,'http://other.server']],
'invalidation_service' : [[20,'http://some.server']],
'denominations' : [1,2,5,10],
'additional_info' : 'happy day'}
structure of cdd with signature:
{'type': 'cdd certificate',
'cdd': cdd,
'signature' : signature(cdd)}
2.1.3 The minting keys
For simplicity reasons, the terminology defines a mint key as containing
a signature/certificate.
struct: mint key = {
type = 'mint key'
id = base64(hash(public mint key)) [[MUST be verified when
receiving a mint_key. Why? Isn't the signature sufficient ?]]
issuer_id = base64(hash(public master key))
cdd_serial = integer [[Allows unique relation to CDD
version but may be ignored by clients for
public_mint_key = JSON dict construct, depending on used crypto
algorithm. base64(public mint key)
denomination = integer [[The actual denomination is calculated
by dividing this denomination with the
currency_divisor of the CDD.]]
sign_coins_not_before = DATE TIME
sign_coins_not_after = DATE TIME
coins_expiry_date = DATE TIME
struct: mint key certificate {
type = 'mint key certificate'
mint_key = mint key,
signature = base64(signature) The mint key is signed with the issuer
master key.
Public Key
This struct is used for public RSA keys used within CDDs and mint keys.
public key = {
modulus = Base64
public_exponent = Base64
2.1.4 tokens
struct: payload {
type = 'payload'
protocol_version =
issuer_id = base64(hash(public master key))
cdd_location = [[Hint to
download the CDD if not available anyway.
Useful for clients to "bootstrap" a yet
unknown currency.]]
denomination = integer [[Only a hint, not verified value.
Denomination MUST be verified by checking the
mint key's denomination.]]
mint_key_id = base64(hash(public mint key)) [[The hex encoded
SHA-256 hash of the issuer's public key. It may
differ depending on denomination, validity
period and currency.]]
serial = base64(128bit random number) [[This random
value is generated by clients. It is used to
identify coins and prevent double spending. Once
the coin is spent, the serial will be stored
in the issuer's DSDB. Because of its sufficient
long length it is supposed to be unique for each
coin. A high entropy (crypto grade quality) is
struct: blind = {
type = 'blinded payload hash'
reference = integer [[To be chosen by client in order to reference
between blinded payload and blind signature. Can be
random or incremental but should be unique within one gwop.
MUST NOT be derived from serial number or blinded factor.]]
blinded_payload_hash = base64(blind(prepare_signing(hash(serialize(payload)))))
mint_key_id = base64(hash(public mint key)) [[The client should
select a random mint key (for the appropriate
denomination) to prevent the issuer from smuggling in
tracking information by using a particular key.]]
struct: blind signature = {
type = 'blind signature'
reference = integer
blind_signature = base64(signature(base64_decode(blinded_payload_hash)))
struct: coin = {
type = 'coin',
payload = payload,
signature = base64(unblind(base64decode(blind_signature))) [[A hex encoded RSA
signature from the issuer (it's private key) over the SHA-256 hash
of the payload.]]
2.2.1 Message Types
struct: request_cdd_serial {
type: 'request cdd serial' [[Request the serial of latest CDD]]
message_reference: integer,
struct: response_cdd_serial {
type: 'response cdd serial'
message_reference: integer,
status_code: integer,
status_description: string,
cdd_serial: integer
struct: request_cdd {
type: 'request cdd',
message_reference: integer,
cdd_serial: integer [[not null to fetch specific cdd version]]
struct: response_cdd {
type: 'response cdd',
message_reference: integer,
status_code: integer,
status_description: string,
cdd: cdd
struct: request_mint_keys{
type: 'request mint keys',
message_reference: integer,
mint_key_ids: [mint_key_id,mint_key_id], //for specific keys
denominations:[d,d,d] //for most recent version
[[If both fields are empty, all latest mint keys will be responded.
If both are provided, all current keys for the particular
denominations as well as the mint keys with the specific ID will
be provided.]]
struct: response_mint_keys{
type: 'response mint keys',
message_reference: integer,
status_code: integer,
status_description: string,
keys: [mint_key_certificate,mint_key_certificate]
struct: request_validation {
type: 'request validation',
message_reference: integer,
transaction_reference: crypto_random_number,
authorization_info: string,
blinds: [n blinds]
struct: request_renewal {
type: 'request renewal',
message_reference: integer,
transaction_reference: crypto_random_number,
coins: [n coins],
blinds: [n blinds]
struct: response_minting {
type: 'response minting',
message_reference: integer,
status_code: integer,
status_description: string,
retry_after: DATETIME [[not empty if status 3XX / timestamp instead
of for asynchronous communication]]
blind_signatures: [blind_signature,...] [[not empty if status 2XX]]
struct: request_resume {
type: 'request resume',
message_reference: integer,
transaction_reference: crypto_random_number
struct: request_invalidation {
type: 'request invalidation',
message_reference: integer,
authorization_info: string,
coins: [n coins]
struct: response_invalidation {
type: 'response invalidation'
message_reference: integer,
status_code: integer,
status_description: string
struct: send_coins {
type: 'send coins',
message_reference: integer, [[Message ID to allow a reference of
response message.]]
subject: string, [[Information for recipient]]
coins: [n coins]
struct: received_coins {
type: 'received coins',
message_reference: integer,
status_code: integer,
status_description: string
Mandatory: Container encryption DRAFT
struct encrypted {
type: 'encrypted', [[encypt + sign]]
reference: string,
cipher: 'aes',
data: crypt(container)
There are RFC drafts with the same goal. These might be worth
* OCSP/CRL of mint key certificates. Should be empty in normal case.
* wallet
* Glue certificates
2.3.1 Order of actions/messages
* Alice should fetch and verify CDD [revocation check of master key]
* Alice should fetch and verify current Mintkeys [revocation check]
* Alice creates new payloads ("blanks")
* Alice requests validation [authorization might happen here]
* Mint responds with signatures [or delay]
* Alice unblinds and verifies signatures
* Alice attaches signatures to payloads (results in coins)
* Alice sends coins to Bob
* (Bob might respond "received" to Alice. Trusted case.)
* If not cached, Bob fetches and verify CDD. [revocation check of
master key]
* Bob selects preferred denominations for new coins.
* Bob should fetch and verify mint key certificates associated with
selected denominations and received coins, if not cached.
[revocation check] To mitigate traffic analysis, client may want
to fetch more keys than required.
* Bob verifies coins using mint key
* Bob creates new payloads
* Bob requests renewal
* Mint signs blinds
* Mint writes old coins in dsdb
* Instant response:
* Mint responds with signatures
* Bob unblinds and verifies signatures
* Bob attaches signatures to payloads (->coins)
* (Bob might respond OK to Alice)
* Delayed response:
* Mint responds with 'delayed'
* (Bob might respond OK to Alice)
* Bob requests resume
* Mint responds with signatures
* Bob unblinds and verifies signatures
* Bob attaches signatures to payloads (->coins)
* (Bob might respond OK to Alice)
* Bob requests invalidate (with authorizer or payment (?) message)
* Issuer stores old coins in dsdb
* Issuer responds with OK (and does whatever is required)
* get cdd -> cdd certificate
* get mintkey -> mint key certificates
* get crl/ocsp
* validate: (blinds, + authorizer information) -> delayed/(blind
* renew: (blinds, coins) -> delayed/(blind signatures)
* invalidate (coins, + authorizer information)
* resume: transactionid -> delayed / (blind signatures)
* transaction: (coins A, coins B + optional cdd, messagestring)
Potential extensions:
* Mandatory: Fees
* Payment requests
* "Internet ATM"
* escrow (trusted third party) (fair exchanges without third party)
* currency exchanges
* receipts
* info service = weighted_list_of_URLs
* CDDs
* mint key certificates
* certificate revocation list (CRL)
* fees: A list of fees e.g. {[1-1000, 2, 'coin'],[>1000, 1,
* icons and other funky shiny stuff
* [[Weighted to also allows round-robin, but also proper
* validation service = weighted_list_of_URLs:
(1, xmpp://]
* renewal service = weighted_list_of_URLs
* invalidation service = weighted_list_of_URLs
* hash: sha512
* padding: ??
* public key = (bigint: e, bigint: n)
* 7 bit serialization: HAS TO BE DEFINED
* Examples:
* base64(json(list(bigint:e, bigint:n)))
* json(list(base64(bigint:e),base64(bigint:n)))
* signature = bigint
* 7 bit serialization: HAS TO BE DEFINED
* prepare_signing()
* ECC public key = (5 koordinaten, elements of a binary field)
* ECC signature = (tupel of field elements)
Open Questions:
* Should the Public Key structure contain the cipher suite or should
the cipher suite remain defined outside, next to the public key?
* Self referencing field names: Change name of cdd_location,
cdd_serial, cdd_signing_date, cdd_expiry_date and remove the
"cdd_" from their names?
* Should we allow negative denominations? -> JS: For the issuer
implementation I assume "no".
* Master key rollover. Following options:
* "Rollover" records in old CDD
* (rollover CDD info service = weighted_list_of_URLs
* rollover CDD cipher suite = string
* rollover CDD public master key = )
* 2nd signature (by old masterkey) in new CDD
* Glue certificates
* Protocol rollover?
* Move cdd_location from token to gwop?
* Alice marks token for bob
* How often should the CDD be fetched? Is this security related?
* OCSP or CRL? CRL may be cheaper.
* should the issuer enforce the order in which renewal requests are processed (order or
requests = order of responses) regarding competing requests containing same coins
* Shall the "hold-back time" be mentioned into the CDD
* Does the client need the option to say "Do *not* send me a DELAY"?
[1] The OpenCoin project <>
[2] David Chaum, "Blind signatures for untraceable payments", Advances in Cryptology - Crypto '82,
Springer-Verlag (1983), 199-203.