Cryptography - SkycoinProject/ledger-nano GitHub Wiki
Keypair generation
In skycoin for all EC cryptography we are using secp256k1 curve. In general, skycoin cryptography is very close to bitcoin, so you can read more here or here. To generate keypair we use cx_ecfp_init_public_key
function from the ledger SDK.
Compressed public key
As the public key is just a combination of x and y on the elliptic curve, so to recover it, we need just x coordinate. But we can get two options, so depending of parity of y, so we need an additional prefix to recover y lately.
Skycoin addresses and its generation
The address is generated from the compressed public key. For the hashing, we are using SHA256 and RIPMD160, which are implemented in the Ledger SDK, as cx_ripemd160 and cx_sha256.
Firstly, we need to find RIPMD160(SHA256(SHA256(public key))), which will be the first 20 bytes of address. Next, there is a version byte and 4 bytes of checksum. To find checksum, we need to find SHA256, of the previous 20 bytes and take first 4.
After that, the address is transformed to base58.
Signature
A digital signature is a mathematical technique used to validate the authenticity and integrity of a message, software or digital document. The digital equivalent of a handwritten signature or stamped seal, a digital signature offers far more inherent security, and it is intended to solve the problem of tampering and impersonation in digital communications.
In ledger we are using it, for example, to sign(authorize) transaction and as the result send money form one wallet(account) to another. So, the blockchain will know that the transaction was made exactly by the owner of a wallet and can confirm it.
We are using the Elliptic Curve Digital Signature Algorithm (ECDSA) to work with signatures/signing. More about it
But there is a problem with ledger SDK, which provides a functionality to work with signatures - cx_ecdsa_sign
. This function returns a signature in DER format, which is not compatible with skycoin.
DER format consists of:
- DER encoded signature data, consisting of:
- 1-byte type 0x30 "Compound object" (the tuple of (R,S) values)
- 1-byte length of the compound object
- The signature's R value, consisting of:
- 1-byte type 0x02 "Integer"
- 1-byte length of the integer
- variable-length R value's bytes
- The signature's S value, consisting of:
- 1-byte type 0x02 "Integer"
- 1-byte length of the integer
- variable-length S value's bytes
NOTE: in practice the length of R and S in DER representation can be bigger then 32bytes, but you can just skip first bytes(they would be zeros). More about it
NOTE: DER(common term) and TLV(used in the ledger SDK documentation) means and are the same thing
To use this signature inside skycoin we have to transform it to the following format: R + S + recovery byte
R and S value we can easily take from DER signature format. Length of R and S is 32 bytes, so the general size of a signature will be 65 bytes.
Recovery byte(recovery id) - special byte, which is needed to recover the public key from a signature. More info: 1, 2
To compute it:
- Let,
RecId = 0
RecId = (if R.Y.IsEven then 0 else 1)
RecId += if(R.X > curve.N) then 2 else 0)
So, theoretically it can take values 0, 1, 2 or 3. Although, in practice we've tested 10k+ signature(with different keypairs) and did not get the value higher than 1
Signed public key
In the real world, we assume that any channel could be insecure, so even if you got the public key from someone you are not 100% sure that it was not compromised. To avoid such situation we have a signed public key, which gives you the ability to check whether the received public key is reliable. (also check this)
Signing transactions
The raw skycoin transaction has next form:
Length | Type | Inner hash | Sigs num | Sigs per input | Input num | Input | Output num | Output |
---|---|---|---|---|---|---|---|---|
4 | 1 | 32 | 4 | 64 (signature) + 1 (key recovery) | 4 | 32 | 4 | 21 (address) + 8 (num of coins) + 8 (num of hours) |
Inner hash is SHA256 of the input and output Signature is signed inner hash + input
As to find signatures, we need to know inner hash, so while getting data using APDU calls, we are firstly calculating inner hash using forwarding, so we don't need to save data in memory. But as for signatures, we need as well inputs, so we need to save them in memory (but not outputs). After jointing inner_hash with inputs, they are signing as mentioned here.