Digital Signatures
So far, we have not delved into any detail about digital signatures. In this section, we look at how digital signatures work and how they can be used to present proof of ownership of a private key without revealing that private key.
The Elliptic Curve Digital Signature Algorithm
The digital signature algorithm used in Ethereum is the Elliptic Curve Digital Signature Algorithm (ECDSA). It’s based on elliptic curve private–public key pairs, as described in [elliptic_curve].
A digital signature serves three purposes in Ethereum (see the following sidebar). First, the signature proves that the owner of the private key, who is by implication the owner of an Ethereum account, has authorized the spending of ether, or execution of a contract. Secondly, it guarantees non-repudiation: the proof of authorization is undeniable. Thirdly, the signature proves that the transaction data has not been and cannot be modified by anyone after the transaction has been signed.
Wikipedia’s Definition of a Digital Signature
A digital signature is a mathematical scheme for presenting the authenticity of digital messages or documents. A valid digital signature gives a recipient reason to believe that the message was created by a known sender (authentication), that the sender cannot deny having sent the message (non-repudiation), and that the message was not altered in transit (integrity).
Source: https://en.wikipedia.org/wiki/Digital_signature
How Digital Signatures Work
A digital signature is a mathematical scheme that consists of two parts. The first part is an algorithm for creating a signature, using a private key (the signing key), from a message (which in our case is the transaction). The second part is an algorithm that allows anyone to verify the signature by only using the message and a public key.
Creating a digital signature
In Ethereum’s implementation of ECDSA, the “message” being signed is the transaction, or more accurately, the Keccak-256 hash of the RLP-encoded data from the transaction. The signing key is the EOA’s private key. The result is the signature:
S i g = F sig ( F keccak256 ( m ) , k )
where:
k is the signing private key.
m is the RLP-encoded transaction.
F**keccak256 is the Keccak-256 hash function.
F**sig is the signing algorithm.
Sig is the resulting signature.
The function F**sig produces a signature Sig that is composed of two values, commonly referred to as r and s:
S i g = ( r , s )
Verifying the Signature
To verify the signature, one must have the signature (r and s), the serialized transaction, and the public key that corresponds to the private key used to create the signature. Essentially, verification of a signature means “only the owner of the private key that generated this public key could have produced this signature on this transaction.”
The signature verification algorithm takes the message (i.e., a hash of the transaction for our usage), the signer’s public key, and the signature (r and s values), and returns true if the signature is valid for this message and public key.
ECDSA Math
As mentioned previously, signatures are created by a mathematical function F**sig that produces a signature composed of two values, r and s. In this section we look at the function F**sig in more detail.
The signature algorithm first generates an ephemeral (temporary) private key in a cryptographically secure way. This temporary key is used in the calculation of the r and s values to ensure that the sender’s actual private key can’t be calculated by attackers watching signed transactions on the Ethereum network.
As we know from [pubkey], the ephemeral private key is used to derive the corresponding (ephemeral) public key, so we have:
A cryptographically secure random number q, which is used as the ephemeral private key
The corresponding ephemeral public key Q, generated from q and the elliptic curve generator point G
The r value of the digital signature is then the x coordinate of the ephemeral public key Q.
From there, the algorithm calculates the s value of the signature, such that:
- s ≡ q-1 (Keccak256(m) + r * k) (mod p)
where:
q is the ephemeral private key.
r is the x coordinate of the ephemeral public key.
k is the signing (EOA owner’s) private key.
m is the transaction data.
p is the prime order of the elliptic curve.
Verification is the inverse of the signature generation function, using the r and s values and the sender’s public key to calculate a value Q, which is a point on the elliptic curve (the ephemeral public key used in signature creation). The steps are as follows:
Check all inputs are correctly formed
Calculate w = s-1 mod p
Calculate u1 = Keccak256(m) * w mod p
Calculate u2 = r * w mod p
Finally, calculate the point on the elliptic curve Q ≡ u1 * G + u2 * K (mod p)
where:
r and s are the signature values.
K is the signer’s (EOA owner’s) public key.
m is the transaction data that was signed.
G is the elliptic curve generator point.
p is the prime order of the elliptic curve.
If the x coordinate of the calculated point Q is equal to r, then the verifier can conclude that the signature is valid.
Note that in verifying the signature, the private key is neither known nor revealed.
Tip | ECDSA is necessarily a fairly complicated piece of math; a full explanation is beyond the scope of this book. A number of great guides online take you through it step by step: search for “ECDSA explained” or try this one: http://bit.ly/2r0HhGB. |
Transaction Signing in Practice
To produce a valid transaction, the originator must digitally sign the message, using the Elliptic Curve Digital Signature Algorithm. When we say “sign the transaction” we actually mean “sign the Keccak-256 hash of the RLP-serialized transaction data.” The signature is applied to the hash of the transaction data, not the transaction itself.
To sign a transaction in Ethereum, the originator must:
Create a transaction data structure, containing nine fields: nonce, gasPrice, gasLimit, to, value, data, chainID, 0, 0.
Produce an RLP-encoded serialized message of the transaction data structure.
Compute the Keccak-256 hash of this serialized message.
Compute the ECDSA signature, signing the hash with the originating EOA’s private key.
Append the ECDSA signature’s computed v, r, and s values to the transaction.
The special signature variable v indicates two things: the chain ID and the recovery identifier to help the ECDSArecover function check the signature. It is calculated as either one of 27 or 28, or as the chain ID doubled plus 35 or 36. For more information on the chain ID, see Raw Transaction Creation with EIP-155. The recovery identifier (27 or 28 in the “old-style” signatures, or 35 or 36 in the full Spurious Dragon–style transactions) is used to indicate the parity of the y component of the public key (see The Signature Prefix Value (v) and Public Key Recovery for more details).
Note | At block #2,675,000 Ethereum implemented the “Spurious Dragon” hard fork, which, among other changes, introduced a new signing scheme that includes transaction replay protection (preventing transactions meant for one network being replayed on others). This new signing scheme is specified in EIP-155. This change affects the form of the transaction and its signature, so attention must be paid to the first of the three signature variables (i.e., v), which takes one of two forms and indicates the data fields included in the transaction message being hashed. |
Raw Transaction Creation and Signing
In this section we’ll create a raw transaction and sign it, using the ethereumjs-tx library, which can be installed with npm. This demonstrates the functions that would normally be used inside a wallet, or an application that signs transactions on behalf of a user. The source code for this example is in the file raw_tx_demo.js in the book’s GitHub repository:
link:code/web3js/raw_tx/raw_tx_demo.js[]
Running the example code produces the following results:
$ node raw_tx_demo.js
RLP-Encoded Tx: 0xe6808609184e72a0008303000094b0920c523d582040f2bcb1bd7fb1c7c1...
Tx Hash: 0xaa7f03f9f4e52fcf69f836a6d2bbc7706580adce0a068ff6525ba337218e6992
Signed Raw Transaction: 0xf866808609184e72a0008303000094b0920c523d582040f2bcb1...
Raw Transaction Creation with EIP-155
The EIP-155 “Simple Replay Attack Protection” standard specifies a replay-attack-protected transaction encoding, which includes a chain identifier inside the transaction data, prior to signing. This ensures that transactions created for one blockchain (e.g., the Ethereum main network) are invalid on another blockchain (e.g., Ethereum Classic or the Ropsten test network). Therefore, transactions broadcast on one network cannot be replayed on another, hence the name of the standard.
EIP-155 adds three fields to the main six fields of the transaction data structure, namely the chain identifier, 0, and 0. These three fields are added to the transaction data before it is encoded and hashed. They therefore change the transaction’s hash, to which the signature is later applied. By including the chain identifier in the data being signed, the transaction signature prevents any changes, as the signature is invalidated if the chain identifier is modified. Therefore, EIP-155 makes it impossible for a transaction to be replayed on another chain, because the signature’s validity depends on the chain identifier.
The chain identifier field takes a value according to the network the transaction is meant for, as outlined in Chain identifiers.
Table 1. Chain identifiers
Chain | Chain ID |
---|---|
Ethereum mainnet | 1 |
Morden (obsolete), Expanse | 2 |
Ropsten | 3 |
Rinkeby | 4 |
Rootstock mainnet | 30 |
Rootstock testnet | 31 |
Kovan | 42 |
Ethereum Classic mainnet | 61 |
Ethereum Classic testnet | 62 |
Geth private testnets | 1337 |
The resulting transaction structure is RLP-encoded, hashed, and signed. The signature algorithm is modified slightly to encode the chain identifier in the v prefix too.
For more details, see the EIP-155 specification.