Support for compressed public key format

This commit is contained in:
Yuriy Glukhov 2019-11-20 13:45:12 +02:00 committed by zah
parent bf96e13d41
commit 10e35d3def
2 changed files with 55 additions and 35 deletions

View File

@ -12,6 +12,7 @@ import secp256k1, nimcrypto/sysrand, nimcrypto/utils
const const
KeyLength* = 256 div 8 KeyLength* = 256 div 8
CompressedPubKeyLength* = 33
RawSignatureSize* = KeyLength * 2 + 1 RawSignatureSize* = KeyLength * 2 + 1
RawPublicKeySize* = KeyLength * 2 RawPublicKeySize* = KeyLength * 2
InvalidPrivateKey = "Invalid private key!" InvalidPrivateKey = "Invalid private key!"
@ -154,16 +155,24 @@ proc recoverPublicKey*(data: openarray[byte],
## Unserialize public key from `data`. ## Unserialize public key from `data`.
let ctx = getSecpContext() let ctx = getSecpContext()
let length = len(data) let length = len(data)
if length < RawPublicKeySize: if length >= RawPublicKeySize:
var rawkey: array[RawPublicKeySize + 1, byte]
rawkey[0] = 0x04'u8 # mark key with UNCOMPRESSED flag
copyMem(addr rawkey[1], unsafeAddr data[0], RawPublicKeySize)
if secp256k1_ec_pubkey_parse(ctx, addr pubkey,
cast[ptr cuchar](addr rawkey),
RawPublicKeySize + 1) != 1:
return(EthKeysStatus.Error)
elif length == CompressedPubKeyLength:
# Compressed format
if secp256k1_ec_pubkey_parse(ctx, addr pubkey,
cast[ptr cuchar](unsafeAddr data),
length) != 1:
return(EthKeysStatus.Error)
else:
setErrorMsg(InvalidPublicKey) setErrorMsg(InvalidPublicKey)
return(EthKeysStatus.Error) return(EthKeysStatus.Error)
var rawkey: array[RawPublicKeySize + 1, byte]
rawkey[0] = 0x04'u8 # mark key with UNCOMPRESSED flag
copyMem(addr rawkey[1], unsafeAddr data[0], RawPublicKeySize)
if secp256k1_ec_pubkey_parse(ctx, addr pubkey,
cast[ptr cuchar](addr rawkey),
RawPublicKeySize + 1) != 1:
return(EthKeysStatus.Error)
result = EthKeysStatus.Success result = EthKeysStatus.Success
proc recoverSignature*(data: openarray[byte], proc recoverSignature*(data: openarray[byte],
@ -184,8 +193,6 @@ proc recoverSignature*(data: openarray[byte],
proc initPublicKey*(hexstr: string): PublicKey = proc initPublicKey*(hexstr: string): PublicKey =
## Create new public key from hexadecimal string representation. ## Create new public key from hexadecimal string representation.
var o = fromHex(stripSpaces(hexstr)) var o = fromHex(stripSpaces(hexstr))
if len(o) < RawPublicKeySize:
raise newException(EthKeysException, InvalidPublicKey)
if recoverPublicKey(o, result) != EthKeysStatus.Success: if recoverPublicKey(o, result) != EthKeysStatus.Success:
raise newException(EthKeysException, InvalidPublicKey) raise newException(EthKeysException, InvalidPublicKey)
@ -217,32 +224,36 @@ proc ecdhAgree*(seckey: PrivateKey, pubkey: PublicKey,
copyMem(addr secret, addr res[1], KeyLength) copyMem(addr secret, addr res[1], KeyLength)
return(EthKeysStatus.Success) return(EthKeysStatus.Success)
proc getRaw*(pubkey: PublicKey): array[RawPublicKeySize, byte] {.noinit.} = proc toRaw*(pubkey: PublicKey, data: var openarray[byte], compressed = false) =
## Converts public key `pubkey` to serialized form.
var key: array[RawPublicKeySize + 1, byte]
var length = csize(sizeof(key))
let ctx = getSecpContext()
if secp256k1_ec_pubkey_serialize(ctx, cast[ptr cuchar](addr key),
addr length, unsafeAddr pubkey,
SECP256K1_EC_UNCOMPRESSED) != 1:
raiseSecp256k1Error()
doAssert(length == RawPublicKeySize + 1)
doAssert(key[0] == 0x04'u8)
copyMem(addr result[0], addr key[1], RawPublicKeySize)
proc toRaw*(pubkey: PublicKey, data: var openarray[byte]) =
## Converts public key `pubkey` to serialized form and store it in `data`. ## Converts public key `pubkey` to serialized form and store it in `data`.
var key: array[RawPublicKeySize + 1, byte] if compressed:
doAssert(len(data) >= RawPublicKeySize) var length = len(data)
var length = csize(sizeof(key)) doAssert(length >= CompressedPubKeyLength)
let ctx = getSecpContext() let ctx = getSecpContext()
if secp256k1_ec_pubkey_serialize(ctx, cast[ptr cuchar](addr key), if secp256k1_ec_pubkey_serialize(ctx, cast[ptr cuchar](addr data[0]),
addr length, unsafeAddr pubkey, addr length, unsafeAddr pubkey,
SECP256K1_EC_UNCOMPRESSED) != 1: SECP256K1_EC_COMPRESSED) != 1:
raiseSecp256k1Error() raiseSecp256k1Error()
doAssert(length == RawPublicKeySize + 1) else:
doAssert(key[0] == 0x04'u8) var key: array[RawPublicKeySize + 1, byte]
copyMem(addr data[0], addr key[1], RawPublicKeySize) doAssert(len(data) >= RawPublicKeySize)
var length = csize(sizeof(key))
let ctx = getSecpContext()
if secp256k1_ec_pubkey_serialize(ctx, cast[ptr cuchar](addr key),
addr length, unsafeAddr pubkey,
SECP256K1_EC_UNCOMPRESSED) != 1:
raiseSecp256k1Error()
doAssert(length == RawPublicKeySize + 1)
doAssert(key[0] == 0x04'u8)
copyMem(addr data[0], addr key[1], RawPublicKeySize)
proc getRaw*(pubkey: PublicKey): array[RawPublicKeySize, byte] {.noinit, inline.} =
## Converts public key `pubkey` to serialized form.
pubkey.toRaw(result)
proc getRawCompressed*(pubkey: PublicKey): array[CompressedPubKeyLength, byte] {.noinit, inline.} =
## Converts public key `pubkey` to serialized form.
pubkey.toRaw(result, true)
proc getRaw*(s: Signature): array[RawSignatureSize, byte] {.noinit.} = proc getRaw*(s: Signature): array[RawSignatureSize, byte] {.noinit.} =
## Converts signature `s` to serialized form. ## Converts signature `s` to serialized form.

View File

@ -266,3 +266,12 @@ suite "ECC/ECDSA/ECDHE tests suite":
pubkey1.isZeroKey() == true pubkey1.isZeroKey() == true
seckey2.isZeroKey() == false seckey2.isZeroKey() == false
pubkey2.isZeroKey() == false pubkey2.isZeroKey() == false
test "Compressed public keys":
let pubkeyCompressed = "03CA634CAE0D49ACB401D8A4C6B6FE8C55B70D115BF400769CC1400F3258CD3138"
let s = initPublicKey(pubkeyCompressed)
check:
s.getRaw.toHex == """CA634CAE0D49ACB401D8A4C6B6FE8C55B70D115BF400769CC1400F3258
CD31387574077F301B421BC84DF7266C44E9E6D569FC56BE00812904767BF5CCD1FC7F""".stripSpaces
s.getRawCompressed.toHex == pubkeyCompressed