5 Digital Signatures
\[ \newcommand{\F}{\mathbb{F}} \newcommand{\G}{\mathbb{G}} \newcommand{\N}{\mathbb{N}} \newcommand{\R}{\mathbb{R}} \newcommand{\Z}{\mathbb{Z}} \newcommand{\Zq}{\Z_q} \newcommand{\zo}{\{0,1\}} \newcommand{\bin}{\zo} \newcommand{\zon}{\{0,1\}^n} \newcommand{\A}{\mathcal{A}} \newcommand{\B}{\mathcal{B}} \newcommand{\calA}{\mathcal{A}} \newcommand{\calB}{\mathcal{B}} \newcommand{\calC}{\mathcal{C}} \newcommand{\calD}{\mathcal{D}} \newcommand{\calK}{\mathcal{K}} \newcommand{\calM}{\mathcal{M}} \newcommand{\calT}{\mathcal{T}} \newcommand{\calX}{\mathcal{X}} \newcommand{\calY}{\mathcal{Y}} \newcommand{\xor}{\oplus} \newcommand{\cross}{\times} \newcommand{\deq}{\mathrel{\mathop:}=} \newcommand{\abs}[1]{\left| #1 \right|} \newcommand{\set}[1]{\left\{ #1 \right\}} \renewcommand{\vec}[1]{\mathbf{#1}} \newcommand{\poly}{\operatorname{poly}} \newcommand{\negl}{\operatorname{negl}} \newcommand{\classP}{\mathsf{P}} \newcommand{\classNP}{\mathsf{NP}} \newcommand{\INDCPA}{\text{IND-CPA}} \newcommand{\Enc}{\mathsf{Enc}} \newcommand{\Dec}{\mathsf{Dec}} \newcommand{\Gen}{\mathsf{Gen}} \newcommand{\Derive}{\mathsf{Derive}} \newcommand{\Sign}{\mathsf{Sign}} \newcommand{\Ver}{\mathsf{Ver}} \newcommand{\MAC}{\mathsf{MAC}} \newcommand{\MACSign}{\mathsf{MAC.Sign}} \newcommand{\MACVerify}{\mathsf{MAC.Verify}} \newcommand{\Funs}{\mathsf{Funs}} \newcommand{\ToString}{\mathsf{ToString}} \newcommand{\sk}{\mathsf{sk}} \newcommand{\pk}{\mathsf{pk}} \newcommand{\vk}{\mathsf{vk}} \newcommand{\sig}{\sigma} \newcommand{\rgets}{\mathrel{\overset{\scriptscriptstyle\mathrm{R}}{\leftarrow}}} \newcommand{\getsr}{\rgets} \]
In the last section, our strategy for authentication depended on two parties sharing a secret key. In that discussion, we completely left out of the picture how these parties should exchange this secret key. Our implication was that they went to some private room and exchanged the key in secret, but in many cases this is not practical: if they could whisper a key, why not just whisper the message?
Luckily, there is a way to get around this requirement for a shared secret using public-key cryptography.(Diffie and Hellman 1976)
The original Diffie-Hellman paper from 1976, which introduced public-key cryptography, is a fascinating read.
5.1 Definitions
The basic idea of public-key cryptography, applied to authentication, is that each party will generate two linked keys—a secret signing key and a public verification key. The verification key will be good enough to verify that a signature is valid, but not to generate new signatures.
Definition 5.1 (Signature Scheme) A signature scheme is associated with a message space \(\calM\) and three efficient algorithms \((\Gen, \Sign, \Ver)\).
In theoretical papers, people will write \(\Gen(1^\lambda)\) to indicate that the key-generation algorithm takes as input a length-\(\lambda\) string of ones. This is just a hack to make the input given to \(\Gen\) \(\lambda\) bits long so that the \(\Gen\) algorithm can run in time polynomial in its input length: \(\poly(\lambda)\). If we express \(\lambda\) in binary, then \(\Gen(\lambda)\) gets a \(\log_2 \lambda\)-bit input and can only run in time \(\poly(\log \lambda)\). This distinction is really unimportant, but if you see the \(1^\lambda\) notation, you will now know what it means.
- \(\Gen(\lambda) \to (\sk, \vk)\). The key-generation algorithm as input a security parameter \(\lambda \in \N\) and outputs a secret signing key \(\sk\) and public verification key \(\vk\). The algorithm \(\Gen\) runs in time \(\poly(\lambda)\).
- \(\Sign(\sk, m) \to \sigma\). The signing algorithm takes as input a secret key \(\sk\) and a message \(m \in \calM\), and outputs a signature \(\sigma\).
- \(\Ver(\vk, m, \sigma) \to \zo\). The signature-verification algorithm takes as input a public verification key \(\vk\), a message \(m \in \calM\), and a signature \(\sig\), and outputs \(\bin\), indicating acceptance or rejection.
For a signature scheme to be useful, a correct verifier must always accept messages from an honest signer. Formally, we have:
Definition 5.2 (Digital signatures: Correctness) A digital-signature scheme \((\Gen, \Sign, \Ver)\) is correct if, for all messages \(m \in \calM\): \[ \Pr\big[\Ver(\vk, m, \Sign(\sk, m)) = 1 \colon (\sk, \vk) \gets \Gen(\lambda) \big] = 1. \]
The standard security notion for digital signatures is very similar to that for MACs (Definition 4.1). The only difference here is that a digital-signature scheme splits the single secret MAC key into two keys: a secret signing key and a public verification key. Otherwise the definition is essentially identical.
Definition 5.3 (Digital signatures: Security – existential unforgeability under chosen message attack) A digital-signature scheme \((\Gen, \Sign, \Ver)\) is secure if all efficient adversaries win the following security game with only negligible probability:
- The challenger runs \((\sk, \vk) \gets \Gen(\lambda)\) and sends \(\vk\) to the adversary.
- For \(i = 1, 2, \dots\) (polynomially many times)
- The adversary sends a message \(m_i \in \calM\) to the challenger.
- The challenger replies with \(\sigma_i \gets \Sign(\sk, m_i)\).
- The adversary outputs a message-signature pair \((m^*, \sigma^*)\).
- The adversary wins if \(\Ver(\vk, m^*, \sigma^*) = 1\) and \(m^* \not \in \{m_1, m_2, \dots\}\).
Notice that this security definition does not guarantee that an attacker cannot forge a new signature on a message that it has already seen a signature of. Namely, given a valid message-signature pair \((m, \sigma)\) an adversary may be able to produce additional valid message-signature pairs on the same message: \((m, \sigma'), (m, \sigma''), \dots\).
In some applications, we want to prohibit an attacker from finding any new message-signature pair. We call this security notion “strong existential unforgeability under chosen message attack.” The definition is the same as in 1 except that we require the adversary to find a valid-message signature pair \((m^*, \sigma^*)\) such that \((m^*, \sigma^*) \not \in \{ (m_1, \sigma_1), (m_2, \sigma_2), \dots \}\). Standard digital-signature schemes, such as the elliptic-curve digital signature algorithm (EC-DSA) or the RSA algorithm with full-domain hashing (RSA-FDH), are believed to have this strong security property.
5.2 Constructing a Signature Scheme
In the following sections, we will show how to construct a digital-signature scheme from any one-way function (Definition 4.3).
We will generate a signature scheme that is secure, but that has relatively large signatures and public keys: to achieve security against attackers running in time \(2^\lambda\), this signature scheme has signatures of \(O(\lambda^2)\) bits. Widely used modern digital signature schemes (e.g., EC-DSA) have signatures of \(O(\lambda)\) bits.
One benefit of the signature scheme that we present here is that—unlike EC-DSA, RSA, DSA, and other widely used signature schemes—this one is plausibly secure even against quantum adversaries. There is ongoing work to standardize signature schemes secure against quantum adversaries; see https://csrc.nist.gov/projects/pqc-dig-sig
We will construct this scheme in three stages:
Construct a signature scheme for signing a single bit.
Construct a one-time secure signature scheme for signing a fixed length messages. With this scheme, an attacker who sees two signatures under the same signing key can forge signatures. In addition, the secret signing key for this scheme will be larger than the size of the message being signed.
Construct a one-time secure scheme for arbitrary-length messages. Here, we construct a one-time signature scheme whose secret signing key is independent of the length of the signed message.
Construct a many-time secure scheme (i.e., a fully secure one under
- for arbitrary-length messages. This last scheme is a fully secure and fully functional digital-signature scheme.
5.3 Constructing a Signature Scheme for Signing a Single Bit
This signature scheme is not useful on its own, and is given only as a step towards the final construction. It uses as a building block a one-way function \(f:\calX\rightarrow\calY\). Recall that \(f\) a one-way function if it is easy to compute but hard to invert; namely there is an efficient algorithm that given \(x\in\calX\) outputs \(f(x)\), and at the same time any efficient algorithm given \(y=f(x)\) for a random \(x\leftarrow \cal X\), finds an inverse \(x'\in\calX\) such that \(f(x')=f(x)\) with only negligible probability.
In this construction, we leave the security parameter \(\lambda\) implicit. To be fully formal, \(\Gen\) would take \(\lambda\) an input. The one-way function \(f\) and its domain \(\calX\) would both depend on \(\lambda\). So we would write \(f_\lambda\) and \(\calX_\lambda\).
- \(\Gen() \to (\sk, \vk)\). Choose two random elements \(x_0, x_1\) from \(\calX\) and let \((y_0,y_1)=(f(x_0),f(x_1))\). Output \(\sk=(x_0,x_1)\) and \(\vk=(y_0,y_1)\).
- \(\Sign(\sk, b) \to \sigma\). Parse \(\sk=(x_0,x_1)\) and output \(\sigma=x_b\),
- \(\Ver(\vk, b, \sig) \to \zo\). Parse \(\vk=(y_0,y_1)\) and output \(1\) if and only if \(f(\sig)=y_b\). (Otherwise, the signing routine rejects.)
5.4 One-time-secure Signatures (Lamport Signatures)
In this section we give a very simple and elegant construction of a one-time-secure digital signature scheme, due to Lamport.(Lamport 1979) The construction is a straightforward generalization of the signature scheme constructed above: Each message is signed bit-by-bit, where each bit is signed using a fresh and independently generated secret key.
Before giving the construction, we define one-time security for digital-signature schemes. This signature scheme is not generally useful on its own, but is useful as a building block.
Definition 5.4 (Digital signatures: One-Time Security) A digital-signature scheme \((\Gen, \Sign, \Ver)\) over message space \(\calM\) is one-time secure if all efficient adversaries win the following game with negligible probability:
- The challenger generates \((\sk, \vk) \leftarrow \Gen(\lambda)\) and sends \(\vk\) to the adversary.
- The adversary sends the challenger single message \(m \in \calM\).
- The challenger responds with \(\sig = \Sign(\sk, m)\).
- The adversary outputs \((m^*, \sig^*)\).
- The adversary wins the game if \(\Ver(\vk, m^*, \sig^*) = 1\) and \(m^* \neq m\).
Lamport signatures. We now construct a one-time secure signature scheme for messages in \(\bin^n\), for some fixed message length \(n \in \N\). To do this, we will define the following algorithms, which make use of a one-way function \(f \colon \calX \to \calY\):
- \(\Gen() \to (\sk, \vk)\). Choose \(2n\) random elements from \(\calX\), the domain of the one-way function \(f\). Arrange these values in to a \(2 \times n\) matrix, which forms the secret signing key \(\sk\). The public verification key just consists of the \(2n\) images of these values under the one-way function \(f\): \[ \sk \gets \begin{pmatrix} x_{10} & \ldots & x_{n0} \\ x_{11} & \ldots & x_{n1} \\ \end{pmatrix},\quad \vk \gets \begin{pmatrix} f(x_{10}) & \ldots & f(x_{n0}) \\ f(x_{11}) & \ldots & f(x_{n1}) \\ \end{pmatrix}.\]
- \(\Sign(\sk, m) \to \sigma\) outputs \((x_{1m_1}, \ldots x_{nm_n})\), where \(m_1 \dots m_n\) are the individual bits of the length-\(n\) message \(m \in \zo^n\).
- \(\Ver(\vk, m, \sig) \to \zo\) parses the the message into bits \(m = m_1\dots m_n \in \zon\) and the signature \(\sig\) into its individual symbols \(\sig = (x_1^*, \ldots x_n^*) \in \calX^n\). The signing routine accepts if, for all \(i \in \{1, \dots, n\}\): \[ f(x^*_i) = \vk_{i,m_i}. \tag{5.1}\] In other words, the routine accepts if applying the one-way function \(f\) to each symbol of the signature matches the corresponding value in the verification key. (Otherwise, the signing routine rejects.)
This signature scheme has relatively large keys: the verification key, in particular consists of \(2n\) values, where each is of length \(\Omega(\lambda)\) bits. So the total length is roughly \(2n\lambda\) bits—much longer than the \(n\)-bit message being signed.
In addition, notice that an adversary who sees signatures on even two messages can forge signatures on messages of its choice. In particular:
- The adversary first asks for a signature on the message \(m_0 = 0^n\). It receives \(\sig_0 = (x_{10}, \ldots, x_{n0})\).
- The adversary then then asks for a signature on the message \(m_1 = 1^n\). It receives \(\sig_1 = (x_{11}, \ldots, x_{n1})\).
- At this point, the adversary has the entire secret signing key!
However, we will show that this scheme is indeed one-time secure.
Claim. The Lamport signature scheme is one-time secure under the assumption that \(f\) is a one-way function.
Remember that if \(\classP = \classNP\), one-way functions, and also digital signature schemes, do not exist. So any proof of security of a digital-signature scheme will require some sort of cryptographic assumption.
In cryptography, we generally prove these security claims by reduction: we will show that if there exists an efficient adversary \(\calA\) that breaks the security of our scheme, then we can construct an efficient adversary \(\calB\) that breaks one of our assumptions. If we do this, we have reached a contradiction to one of our assumptions, so the first adversary cannot exist.
Proof. Suppose there exists an adversary \(\A\) that wins the one-time-security game of Definition 5.4 with non-negligible probability \(\epsilon\). That is, the adversary can produces \((m^*, \sig^*)\) such that \(\Ver(\vk, m^*, \sig^*) = 1\) and \(m \neq m^*\) given only \(\sig = \Sign(\sk, m)\). We can then construct an adversary \(\B\) that can use \(\A\) to invert the one-way function.
Lamport’s construction shows that if one-way functions exist, then so do digital signatures. Can you show that if digital signatures exist, then so do one-way functions?
In particular, our adversary \(\calB\) will use algorithm \(\calA\) as a subroutine to invert the one-way function. We will show that if \(\calA\) wins in the one-time signature security game often, then algorithm \(\calB\) will invert the one-way function often, which is a contradiction.
Assume our one-way function is of the form \(f \colon \calX \to \calY\) and that the Lamport signature scheme is on \(n\)-bit messages. The one-way-function adversary \(\calB\) operates as follows:
- The adversary \(\calB\) is given a point \(y \in \calY\) and its task is to produce a preimage of \(y\) under \(f\).
- The adversary \(\calB\) generates a signing keypair as follows:
- It runs the key-generation algorithm for the Lamport signature scheme \((\sk, \vk) \gets \Gen()\).
- The adversary chooses a random value \(i^* \getsr \{1, \dots, n\}\) and a random bit \(\beta^* \getsr \zo\).
- The adversary sets \(\vk_{i^*,\beta^*} \gets y\). That is, it inserts the one-way-function point it must invert into a random location in the verification key.
- The adversary then sends the verification key \(\vk\) to the Lamport-signature adversary \(\calA\).
- The adversary \(\calA\) asks for the signature on a message \(m = m_1 m_2 \dots m_n \in \zon\).
- If \(m_{i^*} = \beta^*\), then algorithm \(\calB\) cannot produce a valid signature on the message \(m\) and it outputs FAIL.
- Otherwise, the algorithm \(\calB\) returns the signature \(\sigma = (\sk_{1,m_1}, \dots, \sk_{n,m_n}) \in \calX^n\) to algorithm \(\calA\).
- Algorithm \(\calA\) then produces a forged message-signature pair \((m^*, \sigma^*)\), where \(m \neq m^*\).
- Algorithm \(\calB\) parses \(m^* = m^*_1 \dots m^*_n \in \zon\) and \(\sigma^* = \sigma^*_1 \dots \sigma^*_n \in \calX^n\). Then:
- If \(m_{i^*} = m_i\), algorithm \(\calB\) outputs FAIL.
- Otherwise, algorithm \(\calB\) outputs \(x \gets \sigma^*_{i^*} \in \calX\).
First, notice that whenever \((m^*, \sigma^*)\) is a valid message-signature pair and whenever algorithm \(\calB\) does not output FAIL, algorithm \(\calB\) outputs a preimage \(x \in \calX\) of point \(y \in \calY\) under the one-way function \(f\). That is because, by the verification relation (Equation 5.1) for Lamport signatures, \[f(x) = f(\sigma^*_{i^*}) = \vk_{i^*,m^*_{i^*}} = \vk_{i^*, 1 - m_i} = \vk_{i^*, \beta^*} = y.\]
Now, we must show that algorithm \(\calB\) does not output FAIL too often. Since algorithm \(\calB\) chooses the values \(i^*\) and \(\beta^*\) at random, and since the adversary \(\calA\) behavior is independent of these values, we can say:
- the probability of the first failure event is \(1/2\), since there are two possible choices of \(m_{i^*}\) and only one of these is bad, and
- the probability of the second failure event is at most \(1/n\), since \(m\) and \(m^*\) must differ in at least one of \(n\) bits, and there is a \(1/n\) probability that this differing bit is at index \(i^*\).
The events that \(\calA\) breaks the signature scheme and that either of these failures occur are all independent. Then if \(\calA\) breaks the one-time security of the Lamport signature scheme with probability \(\epsilon\), our one-way-function adversary \(\calB\) inverts the one-way function with probability \[ \epsilon_\text{one-way} = \epsilon \cdot \frac{1}{2} \cdot \frac{1}{n}.\]
The probability of either bad is at most \(1/2 + 1/n\), by the union bound. Therefore if algorithm \(\calA\) breaks one-time security of Lamport’s scheme with probability \(\epsilon\), If \(\epsilon\) is non-negligible, then \(\epsilon_\text{one-way} = \epsilon/2n\) is also non-negligible, and we have a contradiction.
5.5 A one-time signature scheme for arbitrary-length messages
In the Lamport signature scheme (Section 5.4), the length of the keys scales with the size of the message being signed. To adapt our scheme from above into a scheme that works on arbitrary-length messages without the key growing arbitrarily large, we will use a strategy called hash-and-sign. In essence, the signing algorithm will pass the message through a hash function to generate a fixed-size digest before applying a signature scheme that works only on fixed-length messages.
Essentially all signature schemes used in practice use this hash-and-sign construction.
This paradigm is called “hash and sign,” and is very common. In practice, hashing is computationally cheap operation while signing turns out to be computationally relatively expensive. So it is common to hash a message before signing it in order to reduce the size of the message that must be signed.
The following claim gives the general construction:
Claim (Hash-and-sign paradigm). Given a collision-resistant hash function \(h: \bin^* \rightarrow \bin^{n}\) and a signature scheme \((\Gen, \Sign, \Ver)\) for message space \(\calM = \bin^n\) (such as the one in Section 5.4), there exists a signature scheme \((\Gen', \Sign', \Ver')\) for \(\calM' = \bin^*\) as follows:
- \(\Gen' \deq \Gen\). The key-generation algorithm is unchanged.
- \(\Sign'(\sk, m) \deq \Sign(sk, h(m))\). We hash the message using the hash function \(h\) before passing the hashed message to the original signing function.
- \(\Ver'(\vk, m, \sig) \deq \Ver(\vk, h(m), \sig)\). We use the original \(\Ver\) to check that the tag matches hash of the original message.
Security Intuition. Suppose that there exists an efficiency adversary that breaks \((\Gen', \Sign', \Ver')\). In particular, given \(((m_1, \sig_1, \ldots, (m_t, \sig_t))\), the adversary is able to construct a valid message-signature pair \((m^*, \sig^*)\) such that \(m^* \notin \set{m_1, \ldots, m_t}\). There are then two cases:
- \(h(m^*) \in \set{h(m_1), \ldots, h(m_t)}\). If this is the case, there is some \(i \in [t]\) such that \(h(m^*) = h(m_i)\). However, \(h\) is collision-resistant as in the definition, so this is a contradiction!
- \(h(m^*) \notin \set{h(m_1), \ldots, h(m_t)}\). Since the message that we pass to the underlying signature scheme is \(h(m)\), this means that the adversary has found a valid signature for \(h(m)\) under the original scheme \((\Gen, \Sign, \Ver)\) after seeing only signatures of \(h(m_1)\neq h(m), \ldots, h(m_t)\neq h(m_t)\). This breaks the security of the underlying signature scheme, which is a contradiction!
Application to Lamport. We can apply the hash-and-sign paradigm to the Lamport signature scheme from Section 5.4. We can fix our input to the Lamport scheme at, for example, 256 bits, and then run messages through a hash function that outputs 256 bits before passing them to the Lamport scheme. This gives a one-time-secure signature scheme for messages of arbitrary length.
Security implications of hash and sign. In practice, hash-and-sign can actually increase the security of our signature scheme, in a certain sense. As shown in case 2 above, it is absolutely crucial that the hash function used is collision-resistant: if not, an adversary can find messages that cause collisions, and then a signature for one message will also be a valid signature for the other. However, in practice we often think of hash functions like SHA256 as behaving like random oracles. That is, for a hash function \(h \colon \zo^* \to \zo^\lambda\) and a string \(x \in \zo^*\) we think of the value \(h(x)\) as being an independently sampled and uniformly random value from the co-domain of the hash function, \(\zo^\lambda\). (Of course, a real-world hash function is never actually a random oracle. A random oracle from \(h \colon \zo^* \to \zo^\lambda\) would take infinitely many bits to describe, while real-world hash functions have finite size (and polynomial-size descriptions).)
Recall that we faced a similar problem in our MAC construction. Why not use hash and MAC there? The reason is that we used AES as our PRF, which takes input of \(\bin^{128}\). As explained by the birthday paradox, it is possible to find a collision in an output space of size \(2^{128}\) in only time \(2^{64}\)! This does not provide sufficient security for practical use, as it would be quite practical to find collisions. If we had a version of AES that outputted 256 bits, we could indeed apply hash and sign.
Another reason to not use hash and MAC is that MACs can be faster to compute than collision-resistant hash functions.
Recall that the standard security definition for digital signatures (1) allows the attacker to request signatures on messages of its choice. If we pass a message through a hash function before signing it using an underlying signature scheme scheme, however, we effectively randomize the message—the adversary can no longer control the input to the underlying signature scheme. This allows us to define another meaningful definition of security:
Definition 5.5 (Digital signatures: security against random message attacks) Any efficient adversary given the public verification key and a list of random message-signature pairs \(((m_1, \sig_1), \ldots, (m_t, \sig_t))\) cannot generate a forged message-signature pair \((m^*, \sig^*)\) such that \(\Ver(\vk, m^*, \sig^*) = 1\) and \(m^* \notin \set{m_1, \ldots, m_t}\).
Note that this definition is not good enough on its own—an adversary often does have the ability to generate signatures for messages of his choice. However, paired with a hash function modeled as a random oracle, this definition becomes very useful—if the inputs are passed through the hash function before they are passed to the signature scheme, they become effectively random. We can even relax the definition further without losing practicality: by the same logic, with hash function in front of the signature scheme, what the adversary needs to sign is really not a message of their choise, but is the hash of a message of their choice—effectively a random value.
Definition 5.6 (Digital signatures: random security against random message attacks) Any efficient adversary given \(\vk\) and a list of random message-signature pairs \(((m_1, \sig_1), \ldots, (m_t, \sig_t))\) and random \(m^* \notin \set{m_1, \ldots, m_t}\) cannot generate \(\sig^*\) such that \(\Ver(\vk, m^*, \sig^*) = 1\).
It is possible to formally argue that given a signature scheme satisfying Definition 5.6 and a random oracle, we can construct a scheme satisfying existential unforgeability under chosen message attacks.
5.6 From one-time security to many-time security
After applying the hash-and-sign strategy above to our Lamport scheme, we have a signature scheme that is one-time secure for messages of arbitrary length. In order for the scheme to be useful and satisfy our security definition, we need to be able to sign polynomially many messages with a single key pair. To do this, we will use a construction very similar to the Merkle tree construction we have seen before.
Informally, we will build up a binary tree of Lamport keys of depth 256. The signing key in each of the \(2^{256}\) leaf nodes will be used to actually sign messages; we will use a random leaf node to sign a message, so the fact that there are \(2^{256}\) of them means that the probability of accidentally choosing the same leaf twice is negligible (\(2^{-128}\)). The signing key in an intermediate nodes (and in the root) will be used to sign the (public) verification keys of the two corresponding child nodes in the tree. Figure 5.1 shows a sketch of this tree.
Importantly, every signing key in the tree will be used to sign only one message ever: the key at non-leaf nodes will only ever sign the pair of its children, and the key at leaf nodes will only ever be used to sign a single message.
The signature, in this scheme, will consist of the signatures and verification keys along the path from the root to the chosen leaf node. The signature will also include information about which child node was chosen.
This tree, of course, is impractically large, but we can solve that problem by lazily constructing it using a pseudorandom function. That is, instead of actually building up all of the leaves of the tree, we will build up the tree (i.e., generate the signing keys, verification keys, and signatures) on-demand, and furthermore, we will build it in a deterministic way using the pseudorandom function, so that we don’t have to remember what parts we might have already computed in the past.
To make the construction more precise, we will assume that we are given:
- a pseudorandom function \(f\) with keyspace \(\calK\), and
- a one-time secure signature scheme \((\Gen_0, \Sign_0, \Ver_0)\).
We will need the ability to run the (non-deterministic) \(\Gen_0\) algorithm on specific randomness, so as to make it deterministic. For a PRF key \(k \in \calK\) and string \(s\), we will write \(\Gen_0^{F(k, s)}()\) to indicate the process of running the key-generation algorithm \(\Gen_0\) using randomness derived from the output of the PRF \(F(k, s)\). We will assume the use of SHA256 (with a 256-bit digest length) as a collision-resistant hash function. Using these building blocks, we will construct a many-time secure signature scheme \((\Gen, \Sign, \Ver)\) for arbitrary-length messages (i.e., on message space \(\zo^\ast\)), where all of the algorithms are efficient (\(\poly(\cdot)\) running time).
Our construction is as follows:
5.7 Choosing Signature Schemes
The signature scheme we presented in Section 5.6 is not particularly efficient in terms of signature size.
| Algorithm | \(\vk\) size | \(\sig\) size | signatures/sec | verifications/sec |
|---|---|---|---|---|
| SPHINCS+-128 | 32 B | 8000 B | 5 | 750 |
| RSA 2048 | 256 B | 256 B | 2,000 | 50,000 |
| ECDSA256 | 32 B | 64 B | 42,000 | 14,000 |
Many deployed systems today use the ECDSA256 signature scheme. Legacy application still use RSA signatures, though because of their relatively large public-key and signature sizes, few new applications use these schemes.
Hashing is much, much faster than signing—the commonly used SHA256 hashing algorithm can compute around 10,000,000 hashes per second. This is one reason the hash-and-sign paradigm is so useful.