Add secp256k1 curve support and tests for it.
This commit is contained in:
parent
15a7136ad1
commit
968e635746
|
@ -8,6 +8,7 @@ license = "MIT"
|
||||||
skipDirs = @["tests", "examples", "Nim"]
|
skipDirs = @["tests", "examples", "Nim"]
|
||||||
|
|
||||||
requires "nim > 0.18.0",
|
requires "nim > 0.18.0",
|
||||||
|
"secp256k1",
|
||||||
"nimcrypto >= 0.3.9",
|
"nimcrypto >= 0.3.9",
|
||||||
"chronos"
|
"chronos"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
## This module implements Public Key and Private Key interface for libp2p.
|
## This module implements Public Key and Private Key interface for libp2p.
|
||||||
import rsa, ecnist, ed25519/ed25519
|
import rsa, ecnist, ed25519/ed25519, secp
|
||||||
import ../protobuf/minprotobuf, ../vbuffer
|
import ../protobuf/minprotobuf, ../vbuffer
|
||||||
import nimcrypto/[rijndael, blowfish, sha, sha2, hash, hmac, utils]
|
import nimcrypto/[rijndael, blowfish, sha, sha2, hash, hmac, utils]
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ type
|
||||||
of Ed25519:
|
of Ed25519:
|
||||||
edkey*: EdPublicKey
|
edkey*: EdPublicKey
|
||||||
of Secp256k1:
|
of Secp256k1:
|
||||||
discard
|
skkey*: SkPublicKey
|
||||||
of ECDSA:
|
of ECDSA:
|
||||||
eckey*: EcPublicKey
|
eckey*: EcPublicKey
|
||||||
of NoSupport:
|
of NoSupport:
|
||||||
|
@ -55,7 +55,7 @@ type
|
||||||
of Ed25519:
|
of Ed25519:
|
||||||
edkey*: EdPrivateKey
|
edkey*: EdPrivateKey
|
||||||
of Secp256k1:
|
of Secp256k1:
|
||||||
discard
|
skkey*: SkPrivateKey
|
||||||
of ECDSA:
|
of ECDSA:
|
||||||
eckey*: EcPrivateKey
|
eckey*: EcPrivateKey
|
||||||
of NoSupport:
|
of NoSupport:
|
||||||
|
@ -78,8 +78,9 @@ type
|
||||||
P2pSigError* = object of CatchableError
|
P2pSigError* = object of CatchableError
|
||||||
|
|
||||||
const
|
const
|
||||||
SupportedSchemes* = {RSA, Ed25519, ECDSA}
|
SupportedSchemes* = {RSA, Ed25519, Secp256k1, ECDSA}
|
||||||
SupportedSchemesInt* = {int8(RSA), int8(Ed25519), int8(ECDSA)}
|
SupportedSchemesInt* = {int8(RSA), int8(Ed25519), int8(Secp256k1),
|
||||||
|
int8(ECDSA)}
|
||||||
|
|
||||||
proc random*(t: typedesc[PrivateKey], scheme: PKScheme,
|
proc random*(t: typedesc[PrivateKey], scheme: PKScheme,
|
||||||
bits = DefaultKeySize): PrivateKey =
|
bits = DefaultKeySize): PrivateKey =
|
||||||
|
@ -95,6 +96,8 @@ proc random*(t: typedesc[PrivateKey], scheme: PKScheme,
|
||||||
result.edkey = EdPrivateKey.random()
|
result.edkey = EdPrivateKey.random()
|
||||||
elif scheme == ECDSA:
|
elif scheme == ECDSA:
|
||||||
result.eckey = EcPrivateKey.random(Secp256r1)
|
result.eckey = EcPrivateKey.random(Secp256r1)
|
||||||
|
elif scheme == Secp256k1:
|
||||||
|
result.skkey = SkPrivateKey.random()
|
||||||
|
|
||||||
proc random*(t: typedesc[KeyPair], scheme: PKScheme,
|
proc random*(t: typedesc[KeyPair], scheme: PKScheme,
|
||||||
bits = DefaultKeySize): KeyPair =
|
bits = DefaultKeySize): KeyPair =
|
||||||
|
@ -117,6 +120,10 @@ proc random*(t: typedesc[KeyPair], scheme: PKScheme,
|
||||||
var pair = EcKeyPair.random(Secp256r1)
|
var pair = EcKeyPair.random(Secp256r1)
|
||||||
result.seckey.eckey = pair.seckey
|
result.seckey.eckey = pair.seckey
|
||||||
result.pubkey.eckey = pair.pubkey
|
result.pubkey.eckey = pair.pubkey
|
||||||
|
elif scheme == Secp256k1:
|
||||||
|
var pair = SkKeyPair.random()
|
||||||
|
result.seckey.eckey = pair.seckey
|
||||||
|
result.pubkey.eckey = pair.pubkey
|
||||||
|
|
||||||
proc getKey*(key: PrivateKey): PublicKey =
|
proc getKey*(key: PrivateKey): PublicKey =
|
||||||
## Get public key from corresponding private key ``key``.
|
## Get public key from corresponding private key ``key``.
|
||||||
|
@ -127,6 +134,8 @@ proc getKey*(key: PrivateKey): PublicKey =
|
||||||
result.edkey = key.edkey.getKey()
|
result.edkey = key.edkey.getKey()
|
||||||
elif key.scheme == ECDSA:
|
elif key.scheme == ECDSA:
|
||||||
result.eckey = key.eckey.getKey()
|
result.eckey = key.eckey.getKey()
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
result.skkey = key.skkey.getKey()
|
||||||
|
|
||||||
proc toRawBytes*(key: PrivateKey, data: var openarray[byte]): int =
|
proc toRawBytes*(key: PrivateKey, data: var openarray[byte]): int =
|
||||||
## Serialize private key ``key`` (using scheme's own serialization) and store
|
## Serialize private key ``key`` (using scheme's own serialization) and store
|
||||||
|
@ -139,6 +148,8 @@ proc toRawBytes*(key: PrivateKey, data: var openarray[byte]): int =
|
||||||
result = key.edkey.toBytes(data)
|
result = key.edkey.toBytes(data)
|
||||||
elif key.scheme == ECDSA:
|
elif key.scheme == ECDSA:
|
||||||
result = key.eckey.toBytes(data)
|
result = key.eckey.toBytes(data)
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
result = key.skkey.toBytes(data)
|
||||||
|
|
||||||
proc toRawBytes*(key: PublicKey, data: var openarray[byte]): int =
|
proc toRawBytes*(key: PublicKey, data: var openarray[byte]): int =
|
||||||
## Serialize public key ``key`` (using scheme's own serialization) and store
|
## Serialize public key ``key`` (using scheme's own serialization) and store
|
||||||
|
@ -151,6 +162,8 @@ proc toRawBytes*(key: PublicKey, data: var openarray[byte]): int =
|
||||||
result = key.edkey.toBytes(data)
|
result = key.edkey.toBytes(data)
|
||||||
elif key.scheme == ECDSA:
|
elif key.scheme == ECDSA:
|
||||||
result = key.eckey.toBytes(data)
|
result = key.eckey.toBytes(data)
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
result = key.skkey.toBytes(data)
|
||||||
|
|
||||||
proc getRawBytes*(key: PrivateKey): seq[byte] =
|
proc getRawBytes*(key: PrivateKey): seq[byte] =
|
||||||
## Return private key ``key`` in binary form (using scheme's own
|
## Return private key ``key`` in binary form (using scheme's own
|
||||||
|
@ -161,6 +174,8 @@ proc getRawBytes*(key: PrivateKey): seq[byte] =
|
||||||
result = key.edkey.getBytes()
|
result = key.edkey.getBytes()
|
||||||
elif key.scheme == ECDSA:
|
elif key.scheme == ECDSA:
|
||||||
result = key.eckey.getBytes()
|
result = key.eckey.getBytes()
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
result = key.skkey.getBytes()
|
||||||
|
|
||||||
proc getRawBytes*(key: PublicKey): seq[byte] =
|
proc getRawBytes*(key: PublicKey): seq[byte] =
|
||||||
## Return public key ``key`` in binary form (using scheme's own
|
## Return public key ``key`` in binary form (using scheme's own
|
||||||
|
@ -171,6 +186,8 @@ proc getRawBytes*(key: PublicKey): seq[byte] =
|
||||||
result = key.edkey.getBytes()
|
result = key.edkey.getBytes()
|
||||||
elif key.scheme == ECDSA:
|
elif key.scheme == ECDSA:
|
||||||
result = key.eckey.getBytes()
|
result = key.eckey.getBytes()
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
result = key.skkey.getBytes()
|
||||||
|
|
||||||
proc toBytes*(key: PrivateKey, data: var openarray[byte]): int =
|
proc toBytes*(key: PrivateKey, data: var openarray[byte]): int =
|
||||||
## Serialize private key ``key`` (using libp2p protobuf scheme) and store
|
## Serialize private key ``key`` (using libp2p protobuf scheme) and store
|
||||||
|
@ -254,6 +271,10 @@ proc init*(key: var PrivateKey, data: openarray[byte]): bool =
|
||||||
if init(nkey.eckey, buffer) == Asn1Status.Success:
|
if init(nkey.eckey, buffer) == Asn1Status.Success:
|
||||||
key = nkey
|
key = nkey
|
||||||
result = true
|
result = true
|
||||||
|
elif scheme == Secp256k1:
|
||||||
|
if init(nkey.skkey, buffer):
|
||||||
|
key = nkey
|
||||||
|
result = true
|
||||||
|
|
||||||
proc init*(key: var PublicKey, data: openarray[byte]): bool =
|
proc init*(key: var PublicKey, data: openarray[byte]): bool =
|
||||||
## Initialize public key ``key`` from libp2p's protobuf serialized raw
|
## Initialize public key ``key`` from libp2p's protobuf serialized raw
|
||||||
|
@ -281,6 +302,10 @@ proc init*(key: var PublicKey, data: openarray[byte]): bool =
|
||||||
if init(nkey.eckey, buffer) == Asn1Status.Success:
|
if init(nkey.eckey, buffer) == Asn1Status.Success:
|
||||||
key = nkey
|
key = nkey
|
||||||
result = true
|
result = true
|
||||||
|
elif scheme == Secp256k1:
|
||||||
|
if init(nkey.skkey, buffer):
|
||||||
|
key = nkey
|
||||||
|
result = true
|
||||||
|
|
||||||
proc init*(sig: var Signature, data: openarray[byte]): bool =
|
proc init*(sig: var Signature, data: openarray[byte]): bool =
|
||||||
## Initialize signature ``sig`` from raw binary form.
|
## Initialize signature ``sig`` from raw binary form.
|
||||||
|
@ -374,6 +399,10 @@ proc `$`*(key: PrivateKey): string =
|
||||||
result = "Secp256r1 key ("
|
result = "Secp256r1 key ("
|
||||||
result.add($(key.eckey))
|
result.add($(key.eckey))
|
||||||
result.add(")")
|
result.add(")")
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
result = "Secp256k1 key ("
|
||||||
|
result.add($(key.skkey))
|
||||||
|
result.add(")")
|
||||||
|
|
||||||
proc `$`*(key: PublicKey): string =
|
proc `$`*(key: PublicKey): string =
|
||||||
## Get string representation of public key ``key``.
|
## Get string representation of public key ``key``.
|
||||||
|
@ -387,6 +416,10 @@ proc `$`*(key: PublicKey): string =
|
||||||
result = "Secp256r1 key ("
|
result = "Secp256r1 key ("
|
||||||
result.add($(key.eckey))
|
result.add($(key.eckey))
|
||||||
result.add(")")
|
result.add(")")
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
result = "Secp256k1 key ("
|
||||||
|
result.add($(key.skkey))
|
||||||
|
result.add(")")
|
||||||
|
|
||||||
proc `$`*(sig: Signature): string =
|
proc `$`*(sig: Signature): string =
|
||||||
## Get string representation of signature ``sig``.
|
## Get string representation of signature ``sig``.
|
||||||
|
@ -404,6 +437,9 @@ proc sign*(key: PrivateKey, data: openarray[byte]): Signature =
|
||||||
elif key.scheme == ECDSA:
|
elif key.scheme == ECDSA:
|
||||||
var sig = key.eckey.sign(data)
|
var sig = key.eckey.sign(data)
|
||||||
result.data = sig.getBytes()
|
result.data = sig.getBytes()
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
var sig = key.skkey.sign(data)
|
||||||
|
result.data = sig.getBytes()
|
||||||
|
|
||||||
proc verify*(sig: Signature, message: openarray[byte],
|
proc verify*(sig: Signature, message: openarray[byte],
|
||||||
key: PublicKey): bool =
|
key: PublicKey): bool =
|
||||||
|
@ -421,6 +457,10 @@ proc verify*(sig: Signature, message: openarray[byte],
|
||||||
var signature: EcSignature
|
var signature: EcSignature
|
||||||
if signature.init(sig.data) == Asn1Status.Success:
|
if signature.init(sig.data) == Asn1Status.Success:
|
||||||
result = signature.verify(message, key.eckey)
|
result = signature.verify(message, key.eckey)
|
||||||
|
elif key.scheme == Secp256k1:
|
||||||
|
var signature: SkSignature
|
||||||
|
if signature.init(sig.data):
|
||||||
|
result = signature.verify(message, key.skkey)
|
||||||
|
|
||||||
template makeSecret(buffer, hmactype, secret, seed) =
|
template makeSecret(buffer, hmactype, secret, seed) =
|
||||||
var ctx: hmactype
|
var ctx: hmactype
|
||||||
|
|
|
@ -0,0 +1,381 @@
|
||||||
|
## Nim-Libp2p
|
||||||
|
## Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
## Licensed under either of
|
||||||
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
## at your option.
|
||||||
|
## This file may not be copied, modified, or distributed except according to
|
||||||
|
## those terms.
|
||||||
|
import strutils
|
||||||
|
import secp256k1, nimcrypto/sysrand, nimcrypto/utils, nimcrypto/hash,
|
||||||
|
nimcrypto/sha2
|
||||||
|
export sha2
|
||||||
|
|
||||||
|
const
|
||||||
|
SkRawPrivateKeySize* = 256 div 8
|
||||||
|
## Size of private key in octets (bytes)
|
||||||
|
SkRawSignatureSize* = SkRawPrivateKeySize * 2 + 1
|
||||||
|
## Size of signature in octets (bytes)
|
||||||
|
SkRawPublicKeySize* = SkRawPrivateKeySize + 1
|
||||||
|
## Size of public key in octets (bytes)
|
||||||
|
|
||||||
|
type
|
||||||
|
SkPublicKey* = secp256k1_pubkey
|
||||||
|
## Representation of public key.
|
||||||
|
|
||||||
|
SkPrivateKey* = object
|
||||||
|
## Representation of secret key.
|
||||||
|
data*: array[SkRawPrivateKeySize, byte]
|
||||||
|
|
||||||
|
SkKeyPair* = object
|
||||||
|
## Representation of private/public keys pair.
|
||||||
|
seckey*: SkPrivateKey
|
||||||
|
pubkey*: SkPublicKey
|
||||||
|
|
||||||
|
SkSignature* = secp256k1_ecdsa_recoverable_signature
|
||||||
|
## Representation of signature.
|
||||||
|
|
||||||
|
SkContext* = ref object
|
||||||
|
## Representation of Secp256k1 context object.
|
||||||
|
context: ptr secp256k1_context
|
||||||
|
error: string
|
||||||
|
|
||||||
|
Secp256k1Error* = object of CatchableError
|
||||||
|
## Exceptions generated by `libsecp256k1`
|
||||||
|
|
||||||
|
##
|
||||||
|
## Private procedures interface
|
||||||
|
##
|
||||||
|
|
||||||
|
var secpContext {.threadvar.}: SkContext
|
||||||
|
## Thread local variable which holds current context
|
||||||
|
|
||||||
|
proc illegalCallback(message: cstring, data: pointer) {.cdecl.} =
|
||||||
|
let ctx = cast[SkContext](data)
|
||||||
|
ctx.error = $message
|
||||||
|
|
||||||
|
proc errorCallback(message: cstring, data: pointer) {.cdecl.} =
|
||||||
|
let ctx = cast[SkContext](data)
|
||||||
|
ctx.error = $message
|
||||||
|
|
||||||
|
proc shutdownLibsecp256k1(ctx: SkContext) =
|
||||||
|
# TODO: use destructor when finalizer are deprecated for destructors
|
||||||
|
if not(isNil(ctx.context)):
|
||||||
|
secp256k1_context_destroy(ctx.context)
|
||||||
|
|
||||||
|
proc newSkContext(): SkContext =
|
||||||
|
## Create new Secp256k1 context object.
|
||||||
|
new(result, shutdownLibsecp256k1)
|
||||||
|
let flags = cuint(SECP256K1_CONTEXT_VERIFY or SECP256K1_CONTEXT_SIGN)
|
||||||
|
result.context = secp256k1_context_create(flags)
|
||||||
|
secp256k1_context_set_illegal_callback(result.context, illegalCallback,
|
||||||
|
cast[pointer](result))
|
||||||
|
secp256k1_context_set_error_callback(result.context, errorCallback,
|
||||||
|
cast[pointer](result))
|
||||||
|
result.error = ""
|
||||||
|
|
||||||
|
proc getContext(): SkContext =
|
||||||
|
## Get current `EccContext`
|
||||||
|
if isNil(secpContext):
|
||||||
|
secpContext = newSkContext()
|
||||||
|
result = secpContext
|
||||||
|
|
||||||
|
template raiseSecp256k1Error() =
|
||||||
|
## Raises `libsecp256k1` error as exception
|
||||||
|
let mctx = getContext()
|
||||||
|
if len(mctx.error) > 0:
|
||||||
|
let msg = mctx.error
|
||||||
|
mctx.error.setLen(0)
|
||||||
|
raise newException(Secp256k1Error, msg)
|
||||||
|
else:
|
||||||
|
raise newException(Secp256k1Error, "")
|
||||||
|
|
||||||
|
proc init*(key: var SkPrivateKey, data: openarray[byte]): bool =
|
||||||
|
## Initialize Secp256k1 `private key` ``key`` from raw binary
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns ``true`` on success.
|
||||||
|
let ctx = getContext()
|
||||||
|
if len(data) >= SkRawPrivateKeySize:
|
||||||
|
let res = secp256k1_ec_seckey_verify(ctx.context,
|
||||||
|
cast[ptr cuchar](unsafeAddr data[0]))
|
||||||
|
result = (res == 1) and (len(ctx.error) == 0)
|
||||||
|
if result:
|
||||||
|
copyMem(addr key.data[0], unsafeAddr data[0], SkRawPrivateKeySize)
|
||||||
|
|
||||||
|
proc init*(key: var SkPrivateKey, data: string): bool {.inline.} =
|
||||||
|
## Initialize Secp256k1 `private key` ``key`` from hexadecimal string
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns ``true`` on success.
|
||||||
|
var buffer: seq[byte]
|
||||||
|
try:
|
||||||
|
buffer = fromHex(stripSpaces(data))
|
||||||
|
except:
|
||||||
|
return false
|
||||||
|
result = init(key, buffer)
|
||||||
|
|
||||||
|
proc init*(key: var SkPublicKey, data: openarray[byte]): bool =
|
||||||
|
## Initialize Secp256k1 `public key` ``key`` from raw binary
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns ``true`` on success.
|
||||||
|
let ctx = getContext()
|
||||||
|
var length = 0
|
||||||
|
if len(data) > 0:
|
||||||
|
if data[0] == 0x02'u8 or data[0] == 0x03'u8:
|
||||||
|
length = min(len(data), 33)
|
||||||
|
elif data[0] == 0x04'u8 or data[0] == 0x06'u8 or data[0] == 0x07'u8:
|
||||||
|
length = min(len(data), 65)
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
let res = secp256k1_ec_pubkey_parse(ctx.context, addr key,
|
||||||
|
cast[ptr cuchar](unsafeAddr data[0]),
|
||||||
|
length)
|
||||||
|
result = (res == 1) and (len(ctx.error) == 0)
|
||||||
|
|
||||||
|
proc init*(key: var SkPublicKey, data: string): bool =
|
||||||
|
## Initialize Secp256k1 `public key` ``key`` from hexadecimal string
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns ``true`` on success.
|
||||||
|
var buffer: seq[byte]
|
||||||
|
try:
|
||||||
|
buffer = fromHex(stripSpaces(data))
|
||||||
|
except:
|
||||||
|
return false
|
||||||
|
result = init(key, buffer)
|
||||||
|
|
||||||
|
proc init*(sig: var SkSignature, data: openarray[byte]): bool =
|
||||||
|
## Initialize Secp256k1 `signature` ``sig`` from raw binary
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns ``true`` on success.
|
||||||
|
let ctx = getContext()
|
||||||
|
let length = len(data)
|
||||||
|
if length >= SkRawSignatureSize:
|
||||||
|
var recid = cint(data[SkRawPrivateKeySize * 2])
|
||||||
|
let res = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx.context,
|
||||||
|
addr sig, cast[ptr cuchar](unsafeAddr data[0]), recid)
|
||||||
|
result = (res == 1) and (len(ctx.error) == 0)
|
||||||
|
|
||||||
|
proc init*(sig: var SkSignature, data: string): bool =
|
||||||
|
## Initialize Secp256k1 `signature` ``sig`` from hexadecimal string
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns ``true`` on success.
|
||||||
|
var buffer: seq[byte]
|
||||||
|
try:
|
||||||
|
buffer = fromHex(stripSpaces(data))
|
||||||
|
except:
|
||||||
|
return false
|
||||||
|
result = init(sig, buffer)
|
||||||
|
|
||||||
|
proc init*(t: typedesc[SkPrivateKey],
|
||||||
|
data: openarray[byte]): SkPrivateKey {.inline.} =
|
||||||
|
## Initialize Secp256k1 `private key` from raw binary
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns `private key` on success.
|
||||||
|
if not init(result, data):
|
||||||
|
raise newException(Secp256k1Error, "Incorrect binary form")
|
||||||
|
|
||||||
|
proc init*(t: typedesc[SkPrivateKey],
|
||||||
|
data: string): SkPrivateKey {.inline.} =
|
||||||
|
## Initialize Secp256k1 `private key` from hexadecimal string
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns `private key` on success.
|
||||||
|
if not init(result, data):
|
||||||
|
raise newException(Secp256k1Error, "Incorrect binary form")
|
||||||
|
|
||||||
|
proc init*(t: typedesc[SkPublicKey],
|
||||||
|
data: openarray[byte]): SkPublicKey {.inline.} =
|
||||||
|
## Initialize Secp256k1 `public key` from raw binary
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns `public key` on success.
|
||||||
|
if not init(result, data):
|
||||||
|
raise newException(Secp256k1Error, "Incorrect binary form")
|
||||||
|
|
||||||
|
proc init*(t: typedesc[SkPublicKey],
|
||||||
|
data: string): SkPublicKey {.inline.} =
|
||||||
|
## Initialize Secp256k1 `public key` from hexadecimal string
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns `public key` on success.
|
||||||
|
if not init(result, data):
|
||||||
|
raise newException(Secp256k1Error, "Incorrect binary form")
|
||||||
|
|
||||||
|
proc init*(t: typedesc[SkSignature],
|
||||||
|
data: openarray[byte]): SkSignature {.inline.} =
|
||||||
|
## Initialize Secp256k1 `signature` from raw binary
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns `signature` on success.
|
||||||
|
if not init(result, data):
|
||||||
|
raise newException(Secp256k1Error, "Incorrect binary form")
|
||||||
|
|
||||||
|
proc init*(t: typedesc[SkSignature],
|
||||||
|
data: string): SkSignature {.inline.} =
|
||||||
|
## Initialize Secp256k1 `signature` from hexadecimal string
|
||||||
|
## representation ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns `signature` on success.
|
||||||
|
if not init(result, data):
|
||||||
|
raise newException(Secp256k1Error, "Incorrect binary form")
|
||||||
|
|
||||||
|
proc getKey*(key: SkPrivateKey): SkPublicKey =
|
||||||
|
## Calculate and return Secp256k1 `public key` from `private key` ``key``.
|
||||||
|
let ctx = getContext()
|
||||||
|
let res = secp256k1_ec_pubkey_create(ctx.context, addr result,
|
||||||
|
cast[ptr cuchar](unsafeAddr key))
|
||||||
|
if (res != 1) or (len(ctx.error) != 0):
|
||||||
|
raiseSecp256k1Error()
|
||||||
|
|
||||||
|
proc random*(t: typedesc[SkPrivateKey]): SkPrivateKey =
|
||||||
|
## Generates new random private key.
|
||||||
|
let ctx = getContext()
|
||||||
|
while true:
|
||||||
|
if randomBytes(result.data) == SkRawPrivateKeySize:
|
||||||
|
let res = secp256k1_ec_seckey_verify(ctx.context,
|
||||||
|
cast[ptr cuchar](addr result.data[0]))
|
||||||
|
if (res == 1) and (len(ctx.error) == 0):
|
||||||
|
break
|
||||||
|
|
||||||
|
proc random*(t: typedesc[SkKeyPair]): SkKeyPair {.inline.} =
|
||||||
|
## Generates new random key pair.
|
||||||
|
result.seckey = SkPrivateKey.random()
|
||||||
|
result.pubkey = result.seckey.getKey()
|
||||||
|
|
||||||
|
proc toBytes*(key: SkPrivateKey, data: var openarray[byte]): int =
|
||||||
|
## Serialize Secp256k1 `private key` ``key`` to raw binary form and store it
|
||||||
|
## to ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns number of bytes (octets) needed to store
|
||||||
|
## Secp256k1 private key.
|
||||||
|
result = SkRawPrivateKeySize
|
||||||
|
if len(data) >= SkRawPrivateKeySize:
|
||||||
|
copyMem(addr data[0], unsafeAddr key.data[0], SkRawPrivateKeySize)
|
||||||
|
|
||||||
|
proc toBytes*(key: SkPublicKey, data: var openarray[byte]): int =
|
||||||
|
## Serialize Secp256k1 `public key` ``key`` to raw binary form and store it
|
||||||
|
## to ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns number of bytes (octets) needed to store
|
||||||
|
## Secp256k1 public key.
|
||||||
|
let ctx = getContext()
|
||||||
|
var length = csize(len(data))
|
||||||
|
result = SkRawPublicKeySize
|
||||||
|
if len(data) >= SkRawPublicKeySize:
|
||||||
|
let res = secp256k1_ec_pubkey_serialize(ctx.context,
|
||||||
|
cast[ptr cuchar](addr data[0]),
|
||||||
|
addr length, unsafeAddr key,
|
||||||
|
SECP256K1_EC_COMPRESSED)
|
||||||
|
|
||||||
|
proc toBytes*(sig: SkSignature, data: var openarray[byte]): int =
|
||||||
|
## Serialize Secp256k1 `signature` ``sig`` to raw binary form and store it
|
||||||
|
## to ``data``.
|
||||||
|
##
|
||||||
|
## Procedure returns number of bytes (octets) needed to store
|
||||||
|
## Secp256k1 signature.
|
||||||
|
let ctx = getContext()
|
||||||
|
var recid = cint(0)
|
||||||
|
result = SkRawSignatureSize
|
||||||
|
if len(data) >= SkRawSignatureSize:
|
||||||
|
let res = secp256k1_ecdsa_recoverable_signature_serialize_compact(
|
||||||
|
ctx.context, cast[ptr cuchar](unsafeAddr data[0]),
|
||||||
|
addr recid, unsafeAddr sig)
|
||||||
|
if (res == 1) and (len(ctx.error) == 0):
|
||||||
|
data[64] = uint8(recid)
|
||||||
|
|
||||||
|
proc getBytes*(key: SkPrivateKey): seq[byte] {.inline.} =
|
||||||
|
## Serialize Secp256k1 `private key` and return it.
|
||||||
|
result = @(key.data)
|
||||||
|
|
||||||
|
proc getBytes*(key: SkPublicKey): seq[byte] {.inline.} =
|
||||||
|
## Serialize Secp256k1 `public key` and return it.
|
||||||
|
result = newSeq[byte](SkRawPublicKeySize)
|
||||||
|
discard toBytes(key, result)
|
||||||
|
|
||||||
|
proc getBytes*(sig: SkSignature): seq[byte] {.inline.} =
|
||||||
|
## Serialize Secp256k1 `signature` and return it.
|
||||||
|
result = newSeq[byte](SkRawSignatureSize)
|
||||||
|
discard toBytes(sig, result)
|
||||||
|
|
||||||
|
proc `==`*(ska, skb: SkPrivateKey): bool =
|
||||||
|
## Compare Secp256k1 `private key` objects for equality.
|
||||||
|
result = (ska.data == skb.data)
|
||||||
|
|
||||||
|
proc `==`*(pka, pkb: SkPublicKey): bool =
|
||||||
|
## Compare Secp256k1 `public key` objects for equality.
|
||||||
|
var
|
||||||
|
akey: array[SkRawPublicKeySize, byte]
|
||||||
|
bkey: array[SkRawPublicKeySize, byte]
|
||||||
|
discard pka.toBytes(akey)
|
||||||
|
discard pkb.toBytes(bkey)
|
||||||
|
result = (akey == bkey)
|
||||||
|
|
||||||
|
proc `==`*(sia, sib: SkSignature): bool =
|
||||||
|
## Compare Secp256k1 `signature` objects for equality.
|
||||||
|
var
|
||||||
|
asig: array[SkRawSignatureSize, byte]
|
||||||
|
bsig: array[SkRawSignatureSize, byte]
|
||||||
|
discard sia.toBytes(asig)
|
||||||
|
discard sib.toBytes(bsig)
|
||||||
|
result = (asig == bsig)
|
||||||
|
|
||||||
|
proc `$`*(key: SkPrivateKey): string = toHex(key.data)
|
||||||
|
## Return string representation of Secp256k1 `private key`.
|
||||||
|
|
||||||
|
proc `$`*(key: SkPublicKey): string =
|
||||||
|
## Return string representation of Secp256k1 `private key`.s
|
||||||
|
var spub: array[SkRawPublicKeySize, byte]
|
||||||
|
discard key.toBytes(spub)
|
||||||
|
result = toHex(spub)
|
||||||
|
|
||||||
|
proc `$`*(sig: SkSignature): string =
|
||||||
|
## Return string representation of Secp256k1 `signature`.s
|
||||||
|
var ssig: array[SkRawSignatureSize, byte]
|
||||||
|
discard sig.toBytes(ssig)
|
||||||
|
result = toHex(ssig)
|
||||||
|
|
||||||
|
proc sign*[T: byte|char](key: SkPrivateKey, msg: openarray[T]): SkSignature =
|
||||||
|
## Sign message `msg` using private key `key` and return signature object.
|
||||||
|
let ctx = getContext()
|
||||||
|
var hash = sha256.digest(msg)
|
||||||
|
let res = secp256k1_ecdsa_sign_recoverable(ctx.context, addr result,
|
||||||
|
cast[ptr cuchar](addr hash.data[0]),
|
||||||
|
cast[ptr cuchar](unsafeAddr key),
|
||||||
|
nil, nil)
|
||||||
|
if (res != 1) or (len(ctx.error) != 0):
|
||||||
|
raiseSecp256k1Error()
|
||||||
|
|
||||||
|
proc verify*[T: byte|char](sig: SkSignature, msg: openarray[T],
|
||||||
|
key: SkPublicKey): bool =
|
||||||
|
var pubkey: SkPublicKey
|
||||||
|
let ctx = getContext()
|
||||||
|
var hash = sha256.digest(msg)
|
||||||
|
let res = secp256k1_ecdsa_recover(ctx.context, addr pubkey, unsafeAddr sig,
|
||||||
|
cast[ptr cuchar](addr hash.data[0]))
|
||||||
|
if (res == 1) and (len(ctx.error) == 0):
|
||||||
|
if key == pubkey:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc clear*(key: var SkPrivateKey) {.inline.} =
|
||||||
|
## Wipe and clear memory of Secp256k1 `private key`.
|
||||||
|
burnMem(key.data)
|
||||||
|
|
||||||
|
proc clear*(key: var SkPublicKey) {.inline.} =
|
||||||
|
## Wipe and clear memory of Secp256k1 `public key`.
|
||||||
|
burnMem(addr key, SkRawPrivateKeySize * 2)
|
||||||
|
|
||||||
|
proc clear*(sig: var SkSignature) {.inline.} =
|
||||||
|
## Wipe and clear memory of Secp256k1 `signature`.
|
||||||
|
# Internal memory representation size of signature object is 64 bytes.
|
||||||
|
burnMem(addr sig, SkRawPrivateKeySize * 2)
|
||||||
|
|
||||||
|
proc clear*(pair: var SkKeyPair) {.inline.} =
|
||||||
|
## Wipe and clear memory of Secp256k1 `key pair`.
|
||||||
|
pair.seckey.clear()
|
||||||
|
pair.pubkey.clear()
|
|
@ -1,4 +1,4 @@
|
||||||
import unittest
|
import unittest
|
||||||
import testvarint, testbase32, testbase58, testbase64
|
import testvarint, testbase32, testbase58, testbase64
|
||||||
import testrsa, testecnist, tested25519, testcrypto
|
import testrsa, testecnist, tested25519, testsecp256k1, testcrypto
|
||||||
import testmultibase, testmultihash, testmultiaddress, testcid, testpeer
|
import testmultibase, testmultihash, testmultiaddress, testcid, testpeer
|
||||||
|
|
|
@ -76,9 +76,9 @@ const
|
||||||
50BEB59FEAAC43389ABC490E11172750A94A01D155FE553DA9F559CE6687CDF
|
50BEB59FEAAC43389ABC490E11172750A94A01D155FE553DA9F559CE6687CDF
|
||||||
6160B6C11BDD02F58D5E28A2BB1C59F991CE52A49618185C82E750A044979""",
|
6160B6C11BDD02F58D5E28A2BB1C59F991CE52A49618185C82E750A044979""",
|
||||||
# Secp256k1 keys
|
# Secp256k1 keys
|
||||||
# "0802122053DADF1D5A164D6B4ACDB15E24AA4C5B1D3461BDBD42ABEDB0A4404D56CED8FB",
|
"0802122053DADF1D5A164D6B4ACDB15E24AA4C5B1D3461BDBD42ABEDB0A4404D56CED8FB",
|
||||||
# "08021220FD659951E2ED440CC7ECE436357D123D4C8B3CF1056E3F1607FF3641FB578A1B",
|
"08021220FD659951E2ED440CC7ECE436357D123D4C8B3CF1056E3F1607FF3641FB578A1B",
|
||||||
# "08021220B333BE3E843339E0E2CE9E083ABC119BE05C7B65B8665ADE19E172D47BF91305"
|
"08021220B333BE3E843339E0E2CE9E083ABC119BE05C7B65B8665ADE19E172D47BF91305"
|
||||||
]
|
]
|
||||||
|
|
||||||
PeerIDs = [
|
PeerIDs = [
|
||||||
|
@ -91,10 +91,9 @@ const
|
||||||
"QmVMT29id3TUASyfZZ6k9hmNyc2nYabCo4uMSpDw4zrgDk",
|
"QmVMT29id3TUASyfZZ6k9hmNyc2nYabCo4uMSpDw4zrgDk",
|
||||||
"QmXz4wPSQqYF33qB7JRdSExETu56HgWRpE9bsf75HgeXL5",
|
"QmXz4wPSQqYF33qB7JRdSExETu56HgWRpE9bsf75HgeXL5",
|
||||||
"Qmcfz2MaPjw44RfVpHKFgXwhW3uFBRBxByVEkgPhefKCJW",
|
"Qmcfz2MaPjw44RfVpHKFgXwhW3uFBRBxByVEkgPhefKCJW",
|
||||||
# Secp256k1 peer ids
|
"16Uiu2HAmLhLvBoYaoZfaMUKuibM6ac163GwKY74c5kiSLg5KvLpY",
|
||||||
# "16Uiu2HAmLhLvBoYaoZfaMUKuibM6ac163GwKY74c5kiSLg5KvLpY",
|
"16Uiu2HAmRRrT319h5upVoC3E8vs1Qej4UF3vPPnLgrhbpHhUb2Av",
|
||||||
# "16Uiu2HAmRRrT319h5upVoC3E8vs1Qej4UF3vPPnLgrhbpHhUb2Av",
|
"16Uiu2HAmDrDaty3uYPgqSr1h5Cup32S2UdYo46rhqZfXPjJMABZL"
|
||||||
# "16Uiu2HAmDrDaty3uYPgqSr1h5Cup32S2UdYo46rhqZfXPjJMABZL"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
suite "Peer testing suite":
|
suite "Peer testing suite":
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
## Nim-Libp2p
|
||||||
|
## Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
## Licensed under either of
|
||||||
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
## at your option.
|
||||||
|
## This file may not be copied, modified, or distributed except according to
|
||||||
|
## those terms.
|
||||||
|
import unittest
|
||||||
|
import ../libp2p/crypto/secp
|
||||||
|
import nimcrypto/utils
|
||||||
|
|
||||||
|
suite "Secp256k1 testing suite":
|
||||||
|
const TestsCount = 20
|
||||||
|
test "Private key serialize/deserialize test":
|
||||||
|
for i in 0..<TestsCount:
|
||||||
|
var rkey1, rkey2: SkPrivateKey
|
||||||
|
var skey2 = newSeq[byte](256)
|
||||||
|
var key = SkPrivateKey.random()
|
||||||
|
var skey1 = key.getBytes()
|
||||||
|
check:
|
||||||
|
key.toBytes(skey2) > 0
|
||||||
|
check:
|
||||||
|
rkey1.init(skey1) == true
|
||||||
|
rkey2.init(skey2) == true
|
||||||
|
var rkey3 = SkPrivateKey.init(skey1)
|
||||||
|
var rkey4 = SkPrivateKey.init(skey2)
|
||||||
|
check:
|
||||||
|
rkey1 == key
|
||||||
|
rkey2 == key
|
||||||
|
rkey3 == key
|
||||||
|
rkey4 == key
|
||||||
|
rkey1.clear()
|
||||||
|
rkey2.clear()
|
||||||
|
check:
|
||||||
|
isFullZero(rkey1.data) == true
|
||||||
|
isFullZero(rkey2.data) == true
|
||||||
|
test "Public key serialize/deserialize test":
|
||||||
|
for i in 0..<TestsCount:
|
||||||
|
var rkey1, rkey2: SkPublicKey
|
||||||
|
var skey2 = newSeq[byte](256)
|
||||||
|
var pair = SkKeyPair.random()
|
||||||
|
var skey1 = pair.pubkey.getBytes()
|
||||||
|
check:
|
||||||
|
pair.pubkey.toBytes(skey2) > 0
|
||||||
|
rkey1.init(skey1) == true
|
||||||
|
rkey2.init(skey2) == true
|
||||||
|
var rkey3 = SkPublicKey.init(skey1)
|
||||||
|
var rkey4 = SkPublicKey.init(skey2)
|
||||||
|
check:
|
||||||
|
rkey1 == pair.pubkey
|
||||||
|
rkey2 == pair.pubkey
|
||||||
|
rkey3 == pair.pubkey
|
||||||
|
rkey4 == pair.pubkey
|
||||||
|
rkey1.clear()
|
||||||
|
rkey2.clear()
|
||||||
|
test "Generate/Sign/Serialize/Deserialize/Verify test":
|
||||||
|
var message = "message to sign"
|
||||||
|
for i in 0..<TestsCount:
|
||||||
|
var kp = SkKeyPair.random()
|
||||||
|
var sig = kp.seckey.sign(message)
|
||||||
|
var sersk = kp.seckey.getBytes()
|
||||||
|
var serpk = kp.pubkey.getBytes()
|
||||||
|
var sersig = sig.getBytes()
|
||||||
|
var seckey = SkPrivateKey.init(sersk)
|
||||||
|
var pubkey = SkPublicKey.init(serpk)
|
||||||
|
var csig = SkSignature.init(sersig)
|
||||||
|
check csig.verify(message, pubkey) == true
|
||||||
|
let error = len(csig.data) - 1
|
||||||
|
csig.data[error] = not(csig.data[error])
|
||||||
|
check csig.verify(message, pubkey) == false
|
Loading…
Reference in New Issue