I am using the python ecdsa library for signing messages for blockchain transactions. In the blockchain specification it says, for secp256r1 that the signature should have a length of 65 bytes where:
The signature must be of length 65 bytes in the form of [r, s, v] where the first 32 bytes are r, the second 32 bytes are s and the last byte is v.
and
The v represents the recovery ID, which must be normalized to 0, 1, 2 or 3. Note that unlike EIP-155 chain ID is not used to calculate the v value
I consistently get the first 64 bytes using:
sign_deterministic(data, hashfunc=hashlib.sha256)
But not sure where ecdsa holds, or I can compute, the v byte?
I see in the source code (Rust fastcrypto) used on the blockchain it is doing:
// Compute recovery id and normalize signature
let is_r_odd = y.is_odd();
let is_s_high = sig.s().is_high();
let is_y_odd = is_r_odd ^ is_s_high;
let sig_low = sig.normalize_s().unwrap_or(sig);
let recovery_id = RecoveryId::new(is_y_odd.into(), false);
But in ecdsa all that is hidden behind the signing function noted above.
Signing with ECDSA is explained e.g. here. In the following, the terms used in this post are applied:
The ecdsa library does not support the additional determination of the recovery ID when creating the signature. However, it is possible to determine the recovery ID subsequently. To do this, the
kvalue used when creating the signature must first be determined, which is possible since you are using deterministic ECDSA according to RFC 6979. Herekdepends defined on the hash, the message and the private signing key.When running
sign_deterministic(data, hashfunc=hashlib.sha256), the following logic is executed to determinek:The corresponding code location can be found here. Note that the
sign_deterministic(data, hashfunc=hashlib.sha256)call uses the default value for the extra entropy (extra_entropy=b"") and allows too large hashes to be truncated (allow_truncate=True).With
k,Rcan be determined as the product ofkand generator pointG. Formally,kcan be considered as a raw private key andRas a raw public key, so the existing functionalities for keys can be used andRcan be easily determined as follows:For known
R, the recovery ID can be derived as follows:For an explanation of the last logic, see here (including the comments). Note that the probability for recovery IDs 2 and 3 is vanishingly small.
A signature including recovery ID can then be generated with the ecdsa library, e.g. as follows:
To verify the results and also to demonstrate that there are more convenient libraries for generating a signature with recovery ID, the following pycoin code is used, which requires only the library function function
sign_with_recid(), which returns a tuple withr,sand the recovery ID. By default, this method internally uses thekderivation from RFC6979 (i.e. the logic also applied in the ecdsa example):When using the same signing key, the same message and the same digest, the values are identical.
The current question is about deterministic ECDSA. But even for non-deterministic ECDSA, the ecdsa library allows the recovery ID to be determined (and much more easily than in the case of deterministic ECDSA). Since the random
kused in signing cannot be accessed, the generation ofkis simply moved outside and then the signature is generated with thisk(which is supported, seesign()):For known
k,Rand finally the recovery ID can be determined as above.