Introduction
This document is WORK IN PROGRESS. While the document
has this warning, check with your local cryptographer
whether the final solution is correct for your
particular use case.
This document forms the IOG Cryptography Handbook, which aims to provide a go-to for engineers having to make a choice on which implementations to use for given cryptographic primitives. This document contains a specification of all the cryptographic primitives being used in production, or in testing phase, and at least a reference to the ongoing efforts of other cryptographic primitives.
The target audience for the Handbook is everyone working on a project associated with an IOG product, or within the Cardano ecosystem. This is also the set of target contributors for the Handbook! Anyone can submit a change proposal as a PR (see 'Contributing').
Purpose
The purpose of this handbook is both descriptive and to list existing implementations:
- Firstly, it lists particular implementations for given cryptographic primitives that are being used, or have been reviewed, by IOG matter experts.
- Secondly, it describes the cryptographic primitives in detail (or points interested reader to further material), to allow engineers to choose alternative implementation which are compatible with the primitives used by IOG.
Goals
The goals of the Handbook are to:
- Provide a place to record all cryptographic libraries being used in IOG projects.
- Present a sufficient level of detail of the cryptographic primitives to allow engineers to make independent decisions without having to contact matter experts or look into the source code.
- Encourage consistency across the organization in order to minimize cognitive overheads from unnecessary differences.
- Avoid implementing of primitives that already exist and/or are being used in IOG.
- List common mistakes when handling with these type of libraries.
Non-goals
The following are explicitly not goals of the Handbook:
- The recommended libraries are by no means flawless. As in any piece of software, the libraries hereby listed can have bugs.
- Provide an exhaustive list of all available implementations in all languages. We only have a limited number of cryptographers, and this list will grow as we look into existing implementation of used primitives.
The Handbook is very new and will gradually acquire more content over time. We hope to improve all of this over time!
Contributing
If a project needs a particular primitive, which is not present in this document, the mechanism to include is simple. Add an issue to the repo, specifying why such a primitive is of interest, and how it would be used. To make the discussion more effective, it is also of interest to link references to the specification, or available implementations.
Ed25519
Ed25519 is the signature scheme being used in Cardano, and supported in Plutus. Ed25519 is an instantiation of the EdDSA over the Edwards25519 elliptic curve.
Implementation and bindings
We currently relly in the implementation available in version 1.0.18 of libsodium. We have analysed, and recommend, bindings in the following languages:
- Haskell: Available in cardano-base
- Rust: Made available by the original author of libsodium
- Javascript: Made available by the original author of libsodium (among others)
There also exist other implementations that offer a compatible signing algorithm which do not require the full usage of libsodium. The list we have analaysed and recommend is the following:
- Rust: ed25519-dalek, using the
verify_strict()
function. - Javascript: tweetnacl-js, though the verification criteria of this library is more permissive, meaning that a valid signature for tweetnacl-js might not be valid for Cardano.
Common mistakes
An ed25519 signature consists of two values , where is an elliptic curve point and is a scalar. A common mistake when deserialising the scalar is to compute it modulo in case that is larger than . This must be avoided, specifically in consensus critical scenarios.
Another common mistake is what criteria to use when deserialising an elliptic curve point. If none of the libraries listed above are used, the engineer should carefully read the sections below to understand the details, and the verification criteria used in Cardano.
KES
In Cardano, nodes use Key Evolving Signatures (KES). This is another asymmetric key cryptographic scheme, also relying on the use of public and private key pairs. These signature schemes provide forward cryptographic security, meaning that a compromised key does not make it easier for an adversary to forge a signature that allegedly had been signed in the past.
In KES, the public verification key stays constant, but the corresponding private key evolves incrementally. For this reason, KES signing keys are indexed by integers representing the step in the key's evolution. Since the private key evolves incrementally in a KES scheme, the ledger rules require the pool operators to evolve their keys every time a certain number of slots have passed. The details of when these keys are evolved are out of the scope of this document, and the reader is directed to the ledger spec.
Implementation and bindings
The Cardano compatible KES implementations are
- Haskell: Available in cardano-base
- Rust: Available in crates.io
The particular instantiation used in Cardano is of depth 6. An improvement on the representation of signatures is being worked on, and planned to be included in the next HF. This compact representation is available in both the Haskell library and the rust counterpart.
Note: The secret key representations of both libraries are not compatible. A padding strategy can be made to use Haskell generated data in Rust, but not vice-versa. To see this padding trick, you can look in the interoperability tests of the rust library.
Common mistakes
The main building block of KES is its underlying signature scheme, Ed25519. We refer the reader to the common mistakes section of Ed25519.
BIP32-ed25519
BIP32-ed25519 is an adaptation of the BIP32 proposal for deterministic key generation for the Ed25519 curve which has non-linear key space.
Implementation and bindings
We currently have two implementations of BIP32:
- C: Available in cardano-crypto
- Rust: Implemented by Vincent Hanquez, here.
The preferred implementation is that of Rust, but both are safe to use.
For the C implementation, we have bindings in the following languages:
- Haskell: Available in cardano-crypto
Common mistakes
As the underlying signature algorithm is ed25519, we refer the reader to common mistakes of that section.
ECDSA
ECDSA is one of signature schemes supported natively in Plutus, together with ed25519 and Schnorr. In particular, the natively supported ECDSA algorithm works over curve SECP256k1, enabling support for Bitcoin's and Ethereum's native signatures.
Implementation and bindings
We currently relly in Bitcoin's implementation available in secp256k1. We have analysed, and recommend, bindings in the following languages:
- Haskell: Available in cardano-base
Common mistakes
An ECDSA signature consists of two values , where are scalars. A problem of using ECDSA in consensus critical contexts, is that the signature algorithm (as defined in the standard) is malleable. Specifically, given a valid signature , the tuple is also a valid signature. To avoid problems resulting from this malleability, the implementation we use checks that the , where is the order of the prime order group. More details of such a check in the spec section.
Another common problem of verifying ECDSA signatures is that the hashing of the message must be performed by the verifier itself, rather than accepting a hashed message. This can be dangerous. Therefore, the verifier, before proceeding with verification, hashes the message.
Schnorr
Schnorr is one of signature schemes supported natively in Plutus, together with ed25519 and ECDSA. In particular, the natively supported ECDSA algorithm works over curve SECP256k1, enabling support for Bitcoin's and Ethereum's native signatures. Schnorr signature scheme is also adopted by MuSig2 (Simple Two-Round Schnorr Multisignatures) which will be integrated to Hydra.
Implementation and binding
We currently relly in Bitcoin's implementation available in secp256k1. We have analysed, and recommend, bindings in the following languages:
- Haskell: Available in cardano-base
MuSig2
This API also relies on secp256k1 that is compatible with BIP-340 producing 64-byte Schnorr signatures.
- C: Available in musig2. Note that this API is still in progress.
VRF-ed25519
Verifiable Random Functions () allow key-pair owners, , to evaluate a pseudorandom function in a provable way given a randomness seed. Any party with access to the verification key, , the randomness seed, the proof and the generated randomness can indeed verify that the value is computed as expected. The VRF specification has changed overtime in Cardano, and the nodes use a different algorith, pre-Babbage and post-Babbage (if there exists a reference to the HF, we should point it out). We expose both specifications, and the motivations for such a change.
Implementation and bindings
We currently rely in the implementation available in the fork of libsodium. We have analysed, and recommend, bindings in the following languages:
- Haskell: Available in cardano-base
Notation
In this section we introduce some generic notation used throughout the spec. The primitive-specific notation is introduced in the respective sections.
We denote by the finite abelian group based on an elliptic curve over a finite prime-order field (note that we simplify the notation and drop the explicit dependency on and security parameter ). Most importantly, we assume the order of the group to be of the form for some small cofactor (sometimes equal to 1) and large prime number , and that the (hence) unique (sub)group of order is generated by a known base point , i.e., , in which the computational Diffie-Hellman (CDH) problem is believed to be hard. We use to denote a cryptographically safe hash function, modeled as a random oracle, for any value of (we tried using but the katex compiler was not happy with it).
An elliptic curve point can be determined using the y-coordinate and the sign of the -coordinate, meaning that an elliptic curve point can be represented using bits. All elements in or are serialised in little-endian form. Elliptic curve points are encoded using their -coordinate followed by the sign bit of the coordinate. An element in is negative if the enconding of is lexicographically larger than the encoding of
Ed25519
Ed25519 is the signature scheme being used in Cardano, and supported in Plutus. Ed25519 is an instantiation of the EdDSA over the Edwards25519 elliptic curve.
Generalised specification
This section presents the generalized signature system EdDSA, and in the parameter section, we present the specific parameters used in Cardano. EdDSA is parametrized with the following parameters. An integer , a cryptographic hash function producing a -bit output, and a finite abelian group based on an elliptic curve. An EdDSA, signature consists of the following three algorithms:
- takes as input the security parameter and returns a key-pair . First, it chooses . Next, let , and compute the signing key, . Finally, compute , and return .
- takes as input a keypair and a message , and returns a signature . Let , and interpret the result as a little-endian integer in . Let , and . Return .
- takes as input a message , a verification key and a signature , and returns depending on whether the signature is valid or not. The algorithm returns if the following equation holds and otherwise:
Parameters of instantiation
In this section we set to describe the concrete instantiation of the algorithm presented above. Not only we describe the Curves and Hash functions used, but we also specify how deserialization happens, as this results in important differences of the acceptance criteria of valid signatures. The algorithm we use is Ed255191. However, our implementation slightly differs in the deserialization criteria. The details are as follows:
- Parameter : We set
- Curve: We define the curve, and by consequence the finite prime order field, security parameter, cofactor, prime order subgroup and generator. In particular, we use Edwards25519 which is birationally equivalent to Curve255192.
- Hash: As a hashing algorithm we use SHA512.
- Deserialization: A signature is represented by 64 bytes: the first 32 bytes, , represent the
point , and the final 32 bytes, , represent the scalar . A public key is also represented as 32
bytes, . Deserialization is valid only if:
- , read as a little-endian integer, is smaller than .
- does not represent a low order point (by checking against a precomputed blacklist of size ).
- does not represent a low order point (by checking against a precomputed blacklist of size ), and when read as a little-endian integer, it is smaller than .
Bernstein, Duif, Lange, Schwabe and Yang, High-Speed High-Security Signatures
Bernstein, Curve25519: New Diffie-Hellman Speed Records
KES
In Cardano, nodes use Key Evolving Signatures (KES). This is another asymmetric key cryptographic scheme, also relying on the use of public and private key pairs. These signature schemes provide forward cryptographic security, meaning that a compromised key does not make it easier for an adversary to forge a signature that allegedly had been signed in the past.
In KES, the public verification key stays constant, but the corresponding private key evolves incrementally. For this reason, KES signing keys are indexed by integers representing the step in the key's evolution. Since the private key evolves incrementally in a KES scheme, the ledger rules require the pool operators to evolve their keys every time a certain number of slots have passed. The details of when these keys are evolved are out of the scope of this document, and the reader is directed to the ledger spec.
Generalised specification
We use the iterated sum construction from Section 4.3 of MMM1. A KES signature algorithm is parametrized by four algorithms, and . The sum construction depends on two signing algorithms, and , which are forward-secure with and time periods respectively. As specified in the paper, any digital signature algorithm can be considered as a forward-secure signature algorithm with 1 time period. We use two specialized versions of the key generation, one to generate the public part, and another to generate the secret part, respectively. The KES algorithm uses a length doubling pseudorandom generator, , where given a random seed, , returns two random seeds of the same size . The sum construction begins by generating a signing algorithm with two periods by merging two instances of the base signature, and proceeds recursively until the desired level of periods is reached.
The Chang hard fork will bring optimizations to the KES signature size, and we therefore have
different verification criteria pre and post-Chang. Both instances use
a tree of depth 6. For ease of exposure, we compare both constructions using a simple binary tree
of depth 3 (see figure below). Consider each node as a KES instance with periods, where is
the height of the node with respect to the leafs. For instance, A
is a KES instance with periods,
while node D
is a KES instance with periods. Note that each KES instance is created by two child
instances with half the periods.
The only relevant details for compatibility on how KES algorithm works is signature generation and signature
verification, however we provide a description of the other functions. The details on how the keys are managed are
details of implementation which will not be covered here.
digraph keys {
{
A, F, G, J, K, L, M, N, O, t [ shape = box, color = "black" ];
B, D [ shape = box, color = "black", style = "filled", fillcolor = "green" ];
C, E, H, I [ shape = box, color = "red", style = "filled", fillcolor = "green" ];
};
A -> B;
A -> C;
B -> D;
B -> E;
C -> F;
C -> G;
D -> H;
D -> I;
E -> J;
E -> K;
F -> L;
F -> M;
G -> N;
G -> O;
H -> t
}
In green, we have the nodes for which we need to store the public key in a signature during pre-babbage eras. With red borders we have the nodes for which we need to store the public keys in post-babbage eras.
Pre-Chang
The signing instances of nodes H
-O
are single period KES instances, and nodes
A
-G
are defined by recursively calling on the algorithms presented above.
In pre-Chang eras the signatures are computed in a naive manner, meaning
that a signature is represented (recursively) as the underlying signature and the two public keys,
.
So for instance, the signature in node A
of period 0 contains the signature of node B
,
, the public key of node B
(which is a hash of and ) and the public key
of node C
(which is a hash of and ). Signature in turn contains
signature of node D
, the public key of node D
(which is a hash of
and ) and public key of node E
(which is a hash of and ). The signature of
node D
, contains in turn the signature of node H
, , the public key
of node H
, and the public key of node I
. Verification of the signature works in
the naive recursive manner. We check that , and , and
, and finally that .
More specifically:
- takes as input a random seed. It then extends the seed into two parts, , and uses each seed to generate the key material of the next layer. In particular and . Finally, it computes the pair's public key and returns . \item takes as input a time period , a signing key and a message. If , then it computes the signature using the first signature algorithm, , otherwise it uses the other . Finally, returns .
- takes as input a time period , and a signing key . If , then . Otherwise, it checks if its changing the key generation algorithm. Specifically, if , then and sets the seed to zero . Otherwise, .
- takes as input a verification key, , a message, , a signature, , and a time period, . First, it checks that . If that is not the case, it returns . Otherwise, if then , else . If verification fails, it returns , otherwise it returns .
Post-Chang
The naive definition of the
signature used in Pre-Chang results in poor performance with respect to the signature size. We
don't need to verify the hash equality at each level, and we simply need to do so at the root. In the Chang
hardfork we introduced such an optimization. In particular, instead of storing both public keys in each signature, we
only store the one of the branch that we are not in. For the case of the KES instances with 1 period, the KES signature
contains not only the underlying signature, but also the public key, which allows us to re-walk the merkle path.
Again, assume we are in period 0. Then, the signature in node
A
of period 0 contains the signature of node B
, , and the public key of node
C
(which is a hash of and ). Signature in turn contains signature of
node D
and public key of node E
(which is a hash of and ).
The signature of node D
, contains in turn the signature of node H
, , and the
public key of node I
. In this case, , where is
the underlying signature. Verification of the signature
works going up the tree, rather than down. We check . Then we compute the
expected key of node D
, . Then we use that to compute the expected key of node
B
, . Finally, we check that the leaf indeed is part of the merkle tree by
checking that . To derive the missing public key, we introduce a new function, .
Specifically, post-Chang KES signature algorithm modifies the and algorithms, and introduces as follows:
- takes as input a random seed. It then extends the seed into two parts, , and uses each seed to generate the key material of the next layer. In particular and . Finally, it computes the pair's public key and returns .
- takes as input a time period , a signing key and a message. If , then it computes the signature using the first signature algorithm, and lets , otherwise it uses the other , and lets . Finally, returns .
- takes as input a time period , and a signing key . If , then . Otherwise, it checks if its changing the key generation algorithm. Specifically, if , then and sets the seed to zero . Otherwise, .
- takes as input a signature and a period . If , then and return , otherwise and return .
- takes as input a verification key, , a message, , a signature, , and a time period, . First, it computes , and then, if , check that , otherwise, check that . If verification fails, it returns , otherwise it returns .
For this recursive explanation to be complete, we need to define what happens when we call on a leaf signature. Recall that the signature of a leaf contains not only the underlying signature, but also the public key with which it is signed. The derive function at the leaf takes as input . It proceeds by verifying the leaf signature. Parse , and compute the result . If , then return , else return .
Parameters of instantiation
The instantiation of both eras is the same. The underlying signature scheme is Ed25519. Regarding and , in Cardano, these functions simply call a seeded version of Ed25519's and extract the public or private part. As a hashing function we use Blake2b2. Defining the pseudo random function is not required for compatibility purposes, as it is only used for private key material. However, for sake of completeness, we specify that the cardano node uses Blake2b as a length doubling pseudorandom generator.
Malkin, Micciancio and Miner, Composition and Efficiency Tradeoffs for Forward-Secure Digital Signatures
Aumasson, The BLAKE2 Cryptographic Hash and Message Authentication Code (MAC)
ECDSA
ECDSA, together with Schnorr, were two new signature algorithms introduced in Plutus during Cardano's Valentine upgrade.
Generalised specification
This section presents the generalized signature system ECDSA, and in the parameter section, we present the specific parameters used in Cardano. ECDSA is parametrized with the following parameters. An ECDSA signature consists of the following three algorithms:
- takes as input the security parameter and returns a key-pair . First, it chooses . Finally, compute , and return .
- takes as input a keypair and a message , and returns a signature . Let . Compute and interpret the result as its two individual coordinates . Next, compute . If , generate a new and start over. Finally compute . If , generate a new and start over. Otherwise, return .
- takes as input a message , a verification key and a signature
, and returns depending on whether the signature is valid or not. The algorithm
returns if the following conditions hold and otherwise:
- and are between and .
- Compute and . Compute , and ensure it is not equal to the point at infinity. Checks that .
Parameters of instantiation
The above is the standard definition, and in cardano we instantiate it over curve SECP256k1. However, such a signature algorithm enables malleability. Given a valid signature , if one sets , then the pair is also a valid signature, as a point and its negative share the same x-coordinate. This can be problematic in consensus critical contexts. To that end, we follow Bitcoin's modification of only accepting signatures with small values of , i.e. with .
The signatures generated using Haskell's bindings (see here) use hash function SHA256. However, Plutus built-in function takes as input the hashed message, meaning that the signer and verifier can agree which hash function to use in their protocol.
While we follow the verification criteria used in Bitcoin, we do not follow their serialisation convention, DER-encoding, which results in variable sized signatures up to 72 bytes (instead of the 64 byte encoding we describe in this document).
More precisely, the following is what is the specification used in the plutus built-in functions:
- The verification key must correspond to the (x, y) coordinates of a point on the SECP256k1 curve, where x, y are unsigned integers in big-endian form.
- The verification key must correspond to a result produced by
secp256k1_ec_pubkey_serialize
, when given a length argument of 33, and theSECP256K1_EC_COMPRESSED
flag. This implies all of the following:- The verification key is 33 bytes long.
- The first byte corresponds to the parity of the y coordinate; this is
0x02
if y is even, and0x03
otherwise. - The remaining 32 bytes are the bytes of the x coordinate.
- The input to verify must be a 32-byte hash of the message to be checked. We
assume that the caller of
verifyEcdsaSecp256k1Signature
receives the message and hashes it, rather than accepting a hash directly: doing so can be dangerous. Typically, the hashing function used would be SHA256; however, this is not required, as only the length is checked. - The signature must correspond to two unsigned integers in big-endian form; henceforth r and s.
- The signature must correspond to a result produced by
secp256k1_ecdsa_serialize_compact
. This implies all of the following:- The signature is 64 bytes long.
- The first 32 bytes are the bytes of r.
- The last 32 bytes are the bytes of s.
┏━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━┓ ┃ r <32 bytes> │ s <32 bytes> ┃ ┗━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┛ <--------- signature ---------->
Schnorr
Schnorr, together with ECDSA, were two new signature algorithms introduced in Plutus during Cardano's Valentine upgrade. MuSig2 is a two-round multi-signature scheme will be integrated to Hydra.
Generalised specification
This section presents the generalized signature system ECDSA and MuSig2. In the [parameter] (#parameters-of-instantiation) section, we present the specific parameters used in Cardano and MuSig2 C implementation.
A Schnorr signature consists of the following three algorithms:
- takes as input the security parameter and returns a key-pair . First, it chooses . Finally, compute , and return .
- takes as input a keypair and a message , and returns a signature . Let . Compute , then compute , and finally compute . The signature is .
- takes as input a message , a verification key and a signature , and returns depending on whether the signature is valid or not. The algorithm returns if
Let be the number of signers and be the number of nonces. A two-round multi-signature scheme MuSig2 that outputs an ordinary Schnorr signature includes the following steps:
-
takes as input the security parameter and returns a key-pair . First, it chooses . Finally, compute , and return .
-
takes as input the security parameter and returns key-pairs . First, it chooses the nonces . Finally, computes the commitments , and returns the list including tuples of nonces and commitments.
-
takes the list of public keys and the signer's public key, returns the aggregate public key and . First creates . Computes for . Finally, generates the aggregate public key by and the signer keeps her own .
-
takes the list of all signers' commitment lists, returns the list of aggregate commitments. .
-
takes as input the keypair of the signer, a message , aggregate public key, the list of commitments and the list of nonces of the signer. Returns a signature . First, creates . Computes and . The single signature is calculated as .
-
takes the list of all signers' single signatures and aggregate commitment, returns the aggregate signature where .
-
takes as input a message, aggregate verification key, aggregate signature, and aggregate commitment. Computes and accepts the signature if .
Parameters of instantiation
The above is the standard definition, and in cardano we instantiate it over curve SECP256k1. Moreover, we follow BIP-0340. The following are the two specifications of the above algorithm required for compatibility. Citing literally BIP-0340:
- We use implicit
Y
coordinates. We encode an elliptic curve point with 32 bytes, and we implicitly choose theY
coordinate that is even[6]. - We use tagged hashed of SHA256 for the challenge computation. More precisely, in order to compute
H(R, vk, m)
, one computesSHA256(SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge") || R || vk || m)
.
More precisely, the following is what is the specification used in the plutus built-in functions:
- The verification key must correspond to the
(x, y)
coordinates of a point on the SECP256k1 curve, wherex, y
are unsigned integers in big-endian form. - The verification key must correspond to a result produced by
secp256k1_xonly_pubkey_serialize
. This implies all of the following:- The verification key is 32 bytes long.
- The bytes of the signature correspond to the
x
coordinate.
- The input to verify is the message to be checked; this can be of any length, and can contain any bytes in any position.
- The signature must correspond to a point
R
on the SECP256k1 curve, and an unsigned integers
in big-endian form. - The signature must follow the BIP-340 standard for encoding. This implies all the following:
- The signature is 64 bytes long.
- The first 32 bytes are the bytes of the
x
coordinate ofR
, as a big-endian unsigned integer. - The last 32 bytes are the bytes of
s
.
┏━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━┓ ┃ R <32 bytes> │ s <32 bytes> ┃ ┗━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┛ <--------- signature ---------->
VRF-ed25519
Verifiable Random Functions () allow key-pair owners, , to evaluate a pseudorandom function in a provable way given a randomness seed. Any party with access to the verification key, , the randomness seed, the proof and the generated randomness can indeed verify that the value is computed as expected. The VRF specification has changed overtime in Cardano, and the nodes use a different algorith, pre-Babbage and post-Babbage (if there exists a reference to the HF, we should point it out). We expose both specifications, and the motivations for such a change.
Generalised specification
We use an additional hash function to that introduced in the Notation section. In particular one that maps a binary input to an element of the group , .
Pre Babbage
A function, in pre-Babbage eras, is defined by the following three algorithms:
- follows the exact same procedure as described in ed25519. Output key pair . We refer to the signing key in the Ed25519 section as .
- takes as input a keypair and a message , and returns the randomness together with a proof . Use to derive . Let . Let . Compute as defined in procedure as in ed25519. Let . Compute . Finally, return the proof and the randomness .
- takes as input a message , a verification key and a vrf proof , and returns or . It parses the proof as , and computes . Let and . Compute the challenge . If , then return , otherwise, return .
Post Babbage
A function, in post-Babbage eras, is defined by the following three algorithms:
- follows the exact same procedure as described in ed25519. Output key pair . We refer to the signing key in the Ed25519 section as .
- takes as input a keypair and a message , and returns the randomness together with a proof . Use to derive . Let . Let . Compute as defined in procedure as in ed25519. Let . Compute . Finally, return the proof and the randomness .
- takes as input a message , a verification key and a vrf proof , and returns or . It parses the proof as , and computes . Next, compute . Finally, if and , then return , otherwise, return .
This change allows for batch verification of proofs, which achieve up to a times two improvement in verification time, in exchange of a larger proof.
Parameters of instantiation
Some of the concrete parameter instantiations also differ between pre-Babbage and post-Babbage eras. We begin by describing those which coincide, and follow with a separate description for the ones that differ.
- Parameter and suite : We set . We choose the suite , as defined in the standard draft1. This sets the parameter as and the following parameters.
- Curve: We define the curve, and by consequence the finite prime order field, security parameter, cofactor, prime order subgroup and generator, as described in the ed25519 paper2. In particular, we use Edwards25519 which is birationally equivalent to Curve25519.
- Hash: As a hashing algorithm we use SHA512.
Pre Babbage
We proceed with the specifications on pre-Babbage eras.
- Draft version: pre-Babbage eras are build on top of Version 03 of the standards draft3.
- Deserialization: A proof is represented by 80 bytes: the first 32 bytes, , represent the
point , the following 16 bytes, , represent the scalar , and the final 32 bytes,
, represent the scalar . A public key is also represented as 32 bytes, .
Deserialization is valid only if:
- when read as a little-endian integer, it is smaller than .
- does not represent a low order point (by checking against a precomputed blacklist of size , and when read as a little-endian integer, it is smaller than .
- Hash to curve : Elligator mapping, over a scalar computed by hashing . The mechanism is described in Section~5.4.1.2 of version 3 of the draft. Note that for implementing the mechanism as described in the draft, the sign bit is cleared before calling the elligator function, meaning that the latter always works with sign = 0 (see function). See Appendix A.4 of version 3 of the draft for test vectors.
Post Babbage
We finalize with the specifications of post-Babbage eras.
- Draft version: post-Babbage eras are build on top of a batch-compatible version of Version 10 of the standards draft4. The specific construction is described in the technical spec studying the security of batch compatibility5.
- Deserialization: A proof is represented by 128 bytes: the first 32 bytes, , represent the
point , the following 32 bytes, , represent the point , the following 32 bytes,
, represent the point , and the final 32 bytes, , represent the scalar . A
public key is also represented as 32 bytes, . Deserialization is valid only if:
- when read as a little-endian integer, it is smaller than .
- when read as a little-endian integer, is smaller than .
- does not represent a low order point (by checking against a precomputed blacklist of size , and when read as a little-endian integer, it is smaller than .
- Hash to curve : Hash to curve algorithm as defined in the hash to curve standard6, version 12, Section 6.8.2 (non uniform version). For test vectors, one can use those presented in Appendix J.5.2 of that same document. Reference implementation can be found in the libsodium fork.
Goldberg, Reyzin, Papadopoulos and Včelák, Verifiable Random Functions (VRFs); version 15
Bernstein, Duif, Lange, Schwabe and Yang, High-Speed High-Security Signatures
Goldberg, Reyzin, Papadopoulos and Včelák, Verifiable Random Functions (VRFs); version 03
Badertscher, Gazi, Querejeta-Azurmendi and Russell, On UC-Secure Range Extension and Batch Verification for ECVRF
Faz-Hernández, Scott, Sullivan, Wahby and Wood, Hashing to Elliptic Curves