D3.6/views/implicit_certificate.md

12 KiB
Raw Blame History

Implicit Certificates

This section will illustrate a Zencode implementation of the Elliptic Curve Qu-Vanstone implicit certificate scheme (ECQV) as described by the Standards for Efficient Cryptography 4 (SEC4, 2014).

The ECQV implicit certificate scheme is intended as a general purpose certificate scheme for applications within computer and communications systems. It is particularly well suited for application environments where resources such as bandwidth, computing power and storage are limited. ECQV provides a more efficient alternative to traditional certificates.

The ECQV is identifiable as a simple yet important building block within DECODE, as it permits the efficient creation of certificates that contain only the public reconstruction data instead of the subjects public key and the CAs signature, also resulting into a smaller payload than traditional certificates.

ECQV relates well to those DECODE pilots in need to authenticate participants according to signed credentials, where the issuance of a public key is subject to the verification of certain conditions by a Certificate Authority (CA) capable of verifying and signing those conditions. This scenarios applies well to the pilot experimentations ongoing in Amsterdam for the DECODE project, where a certificate (and a keypair) is issued based on attributes that are certified by the municipal register and then used for authentication procedures operated by third parties and based on those attributes.

The limit of this implementation is the lack of threshold certification, a problem that will be solved by the Coconut [cit] implementation in Zencode language, which is still a work in progress. However it should be noted that only one pilot in DECODE (Amsterdam's Gebiedonline) may benefit from this feature, which is however not vital to the deployment.

Differences with traditional certificates

To justify the implementation and adoption of ECQV in place of traditional certificates, here are quickly listed three salient characteristics, closely referring to the documentation offered by the SEC4-1.0 document.

With traditional certificates, when an entity U requests a traditional certificate for a public key, U should prove to the CA it knows the corresponding private key. This is to prevent U from choosing an arbitrary public key, that may already belong to another user, and have it certified. This situation is clearly undesirable (and may even lead to security problems). With implicit certificates this proof is unnecessary, as there is no public key before the certificate is issued. Further, U has no control over the final value of his public key, due to the CAs contribution, making it impossible for U to cause the confusion described above.

Unlike traditional certificates, an implicit certificate does not contain a digital signature. In fact, one could simply choose an arbitrary identity I and a random value to form a certificate. Together with the public key of a CA, this generates a public key for the entity identified by I. However, if one constructs an implicit certificate in such a way, i.e., without interacting with the CA, it is infeasible to compute the private key that corresponds to the public key generated by the certificate.

Another difference between traditional certificates and implicit certificates is that when presented with a valid traditional certificate, one knows that the certificate belongs to someone. A valid certificate containing the certificate data string IU is a proof that the CA signed this certificate for U , and also that U knows the private key corresponding to the public key included in the certificate. One does not have this guarantee with implicit certificates, satisfying certain privacy conditions made evident by the GDPR.

Zencode Implementation

This section will demonstrate the Zencode implementation in four steps, covering all the transformations into a human-readable language from the mathematical formula to the implementation capable of being executed in the Zenroom VM without any external dependency.

The first step is the mathematical formula for ECQV as explained in the SEC4 document.

Mathematical formulation of the ECQV implicit certificate scheme

The second step is the implementation of this formula into the machine language executed by the Zenroom VM (a dialect of LUA).

-- Zenroom 0.8.0
-- setup
random = RNG.new()
order = ECP.order()
G = ECP.generator()
-- make a request for certification
ku = INT.new(random, order)
Ru = G * ku
-- keypair for CA
dCA = INT.new(random, order) -- private
QCA = G * dCA       -- public (known to Alice)
-- from here the CA has received the request
k = INT.new(random, order)
kG = G * k
-- public key reconstruction data
Pu = Ru + kG
declaration = { public = Pu:octet(),
				requester = str("Alice"),
				statement = str("I am stuck in Wonderland.") }
declhash = sha256(OCTET.serialize(declaration))
hash = INT.new(declhash, order)
-- private key reconstruction data
r = (hash * k + dCA) % order
-- verified by the requester, receiving r,Certu
du = (r + hash * ku) % order
Qu = Pu * hash + QCA
assert(Qu == G * du)

The third step is the improvement of the previous implementation using meaningful variable and function names.

-- Zenroom 0.8.1
-- setup
random = RNG.new()
order = ECP.order()
G = ECP.generator()
-- typical EC key generation on G1
function keygen(rng,modulo)
   local key = INT.new(rng,modulo)
   return { private = key,
			public = key * G }
end
-- generate the certification request
certreq = keygen(random,order)
-- certreq.private is preserved in a safe place
-- certreq.public is sent to the CA along with a declaration
declaration = { requester = str("Alice"),
				statement = str("I am stuck in Wonderland") }
-- Requester sends to CA -->
-- ... once upon a time ...
-- --> CA receives from Requester
-- keypair for CA (known to everyone as the Mad Hatter)
CA = keygen(random,order)
-- from here the CA has received the request
certkey = keygen(random,order)
-- certkey.private is sent to requester
-- certkey.public is broadcasted
-- public key reconstruction data
certpub = certreq.public + certkey.public
-- the certification is serialized (could use ASN-1 or X509)
certification = { public = certpub,
				  requester = declaration.requester,
				  statement = declaration.statement,
				  certifier = str("Mad Hatter") }
CERT = sha256(OCTET.serialize(certification))
CERThash = INT.new(CERT, order)
-- private key reconstruction data
certpriv = (CERThash * certkey.private + CA.private) % order
-- CA sends to Requester certpriv and CERThash
-- eventually CA broadcasts certpub and CERThash
-- ... on the other side of the mirror ...
-- Alice has received from the CA the certpriv and CERT
-- which can be used to create a new CERTprivate key
CERTprivate = (certpriv + CERThash * certreq.private) % order
-- Anyone may receive the certpub and CERThash and, knowing the CA
-- public key, can recover the same CERTpublic key from them
CERTpublic  = certpub * CERThash + CA.public
-- As a proof here we generate the public key in a standard way,
-- multiplying it by the curve generator point, then check equality
assert(CERTpublic == G * CERTprivate)
print "Certified keypair:"
I.print({ private = CERTprivate:octet():base64(),
		  public  =  CERTpublic:octet():base64()    })

At last, the implementation in Zencode follows, clearly showing the simplification made possible by Zenroom for the ECQV implicit certificate cryptographic scheme. Each of the following "scenarios" are blocks of code that can be executed independently from one another, taking validated input and output data structures.

-- Zenroom 0.9

Scenario 'keygen': $scenario
	Given that I am known as 'MadHatter'
	When I create my new keypair
	Then print my keyring

Scenario 'request': Make my declaration and request certificate
	Given that I introduce myself as 'Alice'
    and I have the 'public' key 'MadHatter' in keyring
    When I declare to 'MadHatter' that I am 'lost in Wonderland'
    and I issue my implicit certificate request 'declaration'
    Then print all data

Scenario 'keygen': $scenario
    Given that I am known as 'Alice'
    and I have a 'declaration_public' 'from' 'Alice'
    Then print data 'declaration_public'

Scenario 'keygen': $scenario
    Given that I am known as 'Alice'
    and I have a 'declaration_keypair'
    Then print data 'declaration_keypair'

Scenario 'issue': Receive a declaration request and issue a certificate
    Given that I am known as 'MadHatter'
    and I have a 'declaration_public' 'from' 'Alice'
    and I have my 'private' key in keyring
    When I issue an implicit certificate for 'declaration_public'
    Then print all data

Scenario 'split': Print the public section of the certificate
    Given I have a 'certificate_public' 'from' 'MadHatter'
    When possible
    Then print data 'certificate_public'

Scenario 'split': Print the private section of the certificate
    Given I have a 'certificate_private'
    When possible
    Then print data 'certificate_private'

Scenario 'save': Receive a certificate of a declaration and save it
  	Given I have a 'certificate_private' 'from' 'MadHatter'
	and I have the 'private' key 'declaration_keypair' in keyring
	When I verify the implicit certificate 'certificate_private'
	Then I print data 'declaration'

Scenario 'keygen': $scenario
    Given that I am known as 'Bob'
    When I create my new keypair
    Then print my keyring

Scenario 'challenge': Receive a certificate of a declaration and use it to encrypt a message
    Given that I am known as 'Bob'
    and I have my 'private' key in keyring
  	and that 'Alice' declares to be 'lost in Wonderland'
	and I have a 'certificate' 'from' 'MadHatter'
    When I draft the text 'Hey Alice! can you read me?'
    and I use 'certificate' key to encrypt the text into 'ciphertext'
	Then I print data 'ciphertext'

Scenario 'respond': Alice receives an encrypted message, decrypts it and sends an encrypted answer back to sender
    Given that I am known as 'Alice'
    and I have my 'private' key in keyring
	When I decrypt the 'ciphertext' to 'decoded'
	and I use 'certificate' key to encrypt 'decoded' into 'answer'
	Then I print data 'answer'

The Zencode language is a DSL enforcing a strong declarative behavior underneath and all base data structures are checked against a validation scheme upon input and output. The checks are also of cryptographic nature, for instance public keys are checked to make sure they are actual points on the elliptic curve in use. Here below the data validation schemes so far in use:

_G['schemas'] = {

   -- packets encoded with AES GCM
   AES-GCM = S.record {
	  checksum = S.hex,
	  iv = S.hex,
	  schema = S.Optional(S.string),
	  text = S.hex,
	  zenroom = S.Optional(S.string),
	  encoding = S.string,
	  curve = S.string,
	  pubkey = S.ecp
   },

   -- zencode_keypair
   keypair = S.record {
	  schema = S.Optional(S.string),
	  private = S.Optional(S.hex),
	  public = S.ecp
   },

   -- zencode_ecqv
   certificate = S.record {
	  schema = S.Optional(S.string),
	  private = S.Optional(S.big),
	  public = S.ecp,
	  hash = S.big,
	  from = S.string,
	  authkey = S.ecp
   },

   certificate_hash = S.Record {
	  schema = S.Optional(S.string),
	  public = S.ecp,
	  requester = S.string,
	  statement = S.string,
	  certifier = S.string
   },

   declaration = S.record {
	  schema = S.Optional(S.string),
	  from = S.string,
	  to = S.string,
	  statement = S.string,
	  public = S.ecp
   },

   declaration_keypair = S.record {
	  schema = S.Optional(S.string),
	  requester = S.string,
	  statement = S.string,
	  public = S.ecp,
	  private = S.hex
   }

}