Remove ecc.nim.

Remove testecc.nim.
Switch auth/ecies to use eth_keys instead of ecc.nim.
Fix tests according to new API.
This commit is contained in:
cheatfate 2018-04-11 00:29:46 +03:00
parent f8ba94b39e
commit d7bd43c334
7 changed files with 63 additions and 521 deletions

View File

@ -18,7 +18,6 @@ requires "nim > 0.18.0",
proc runTest(name: string, lang = "c") = exec "nim " & lang & " -r tests/" & name proc runTest(name: string, lang = "c") = exec "nim " & lang & " -r tests/" & name
task test, "Runs the test suite": task test, "Runs the test suite":
runTest "testecc"
runTest "testecies" runTest "testecies"
runTest "testauth" runTest "testauth"

View File

@ -10,7 +10,7 @@
## This module implements Ethereum authentication ## This module implements Ethereum authentication
import endians import endians
import ecc, ecies, rlp import eth_keys, ecies, rlp
import nimcrypto/sysrand, nimcrypto/hash, nimcrypto/utils, nimcrypto/hmac import nimcrypto/sysrand, nimcrypto/hash, nimcrypto/utils, nimcrypto/hmac
import nimcrypto/rijndael, nimcrypto/keccak, nimcrypto/sha2 import nimcrypto/rijndael, nimcrypto/keccak, nimcrypto/sha2
@ -30,15 +30,17 @@ const
AckMessageMaxEIP8* = AckMessageEIP8Length + 255 AckMessageMaxEIP8* = AckMessageEIP8Length + 255
type type
Nonce* = array[KeyLength, byte]
AuthMessageV4* = object {.packed.} AuthMessageV4* = object {.packed.}
signature: RawSignature signature: array[RawSignatureSize, byte]
keyhash: array[keccak256.sizeDigest, byte] keyhash: array[keccak256.sizeDigest, byte]
pubkey: PublicKey pubkey: PublicKey
nonce: array[keccak256.sizeDigest, byte] nonce: array[keccak256.sizeDigest, byte]
flag: byte flag: byte
AckMessageV4* = object {.packed.} AckMessageV4* = object {.packed.}
pubkey: PublicKey pubkey: array[RawPublicKeySize, byte]
nonce: array[keccak256.sizeDigest, byte] nonce: array[keccak256.sizeDigest, byte]
flag: byte flag: byte
@ -115,16 +117,17 @@ proc authMessagePreEIP8(h: var Handshake,
outlen = 0 outlen = 0
flagb = byte(flag) flagb = byte(flag)
header = cast[ptr AuthMessageV4](addr buffer[0]) header = cast[ptr AuthMessageV4](addr buffer[0])
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success: if ecdhAgree(h.host.seckey, pubkey, secret) != EthKeysStatus.Success:
return(EcdhError) return(EcdhError)
var xornonce = h.initiatorNonce var xornonce = h.initiatorNonce
xornonce.sxor(secret) xornonce.sxor(secret.data)
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success: if signRawMessage(xornonce, h.ephemeral.seckey,
signature) != EthKeysStatus.Success:
return(SignatureError) return(SignatureError)
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
header.signature = signature.getRaw() header.signature = signature.getRaw()
header.keyhash = keccak256.digest(h.ephemeral.pubkey.getRaw().data).data header.keyhash = keccak256.digest(h.ephemeral.pubkey.getRaw()).data
header.pubkey = cast[PublicKey](h.host.pubkey.getRaw().data) header.pubkey = cast[PublicKey](h.host.pubkey.getRaw())
header.nonce = h.initiatorNonce header.nonce = h.initiatorNonce
header.flag = flagb header.flag = flagb
if encrypt: if encrypt:
@ -156,15 +159,16 @@ proc authMessageEIP8(h: var Handshake,
assert(EIP8 in h.flags) assert(EIP8 in h.flags)
outlen = 0 outlen = 0
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success: if ecdhAgree(h.host.seckey, pubkey, secret) != EthKeysStatus.Success:
return(EcdhError) return(EcdhError)
var xornonce = h.initiatorNonce var xornonce = h.initiatorNonce
xornonce.sxor(secret) xornonce.sxor(secret.data)
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success: if signRawMessage(xornonce, h.ephemeral.seckey,
signature) != EthKeysStatus.Success:
return(SignatureError) return(SignatureError)
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
var payload = rlp.encodeList(signature.getRaw().data, var payload = rlp.encodeList(signature.getRaw(),
h.host.pubkey.getRaw().data, h.host.pubkey.getRaw(),
h.initiatorNonce, h.initiatorNonce,
[byte(h.version)]) [byte(h.version)])
assert(len(payload) == PlainAuthMessageEIP8Length) assert(len(payload) == PlainAuthMessageEIP8Length)
@ -208,7 +212,7 @@ proc ackMessagePreEIP8(h: var Handshake,
var buffer: array[PlainAckMessageV4Length, byte] var buffer: array[PlainAckMessageV4Length, byte]
outlen = 0 outlen = 0
var header = cast[ptr AckMessageV4](addr buffer[0]) var header = cast[ptr AckMessageV4](addr buffer[0])
header.pubkey = cast[PublicKey](h.ephemeral.pubkey.getRaw().data) header.pubkey = h.ephemeral.pubkey.getRaw()
header.nonce = h.responderNonce header.nonce = h.responderNonce
header.flag = byte(flag) header.flag = byte(flag)
if encrypt: if encrypt:
@ -234,7 +238,7 @@ proc ackMessageEIP8(h: var Handshake,
buffer: array[PlainAckMessageMaxEIP8, byte] buffer: array[PlainAckMessageMaxEIP8, byte]
padsize: byte padsize: byte
assert(EIP8 in h.flags) assert(EIP8 in h.flags)
var payload = rlp.encodeList(h.ephemeral.pubkey.getRaw().data, var payload = rlp.encodeList(h.ephemeral.pubkey.getRaw(),
h.responderNonce, h.responderNonce,
[byte(h.version)]) [byte(h.version)])
assert(len(payload) == PlainAckMessageEIP8Length) assert(len(payload) == PlainAckMessageEIP8Length)
@ -316,14 +320,14 @@ proc decodeAuthMessageV4(h: var Handshake, m: openarray[byte]): AuthStatus =
if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success: if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success:
return(EciesError) return(EciesError)
var header = cast[ptr AuthMessageV4](addr buffer[0]) var header = cast[ptr AuthMessageV4](addr buffer[0])
if recoverPublicKey(header.pubkey.data, pubkey) != EccStatus.Success: if recoverPublicKey(header.pubkey.data, pubkey) != EthKeysStatus.Success:
return(InvalidPubKey) return(InvalidPubKey)
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success: if ecdhAgree(h.host.seckey, pubkey, secret) != EthKeysStatus.Success:
return(EcdhError) return(EcdhError)
var xornonce = header.nonce var xornonce = header.nonce
xornonce.sxor(secret) xornonce.sxor(secret.data)
if recoverSignatureKey(header.signature.data, xornonce, if recoverSignatureKey(header.signature, xornonce,
h.remoteEPubkey) != EccStatus.Success: h.remoteEPubkey) != EthKeysStatus.Success:
return(SignatureError) return(SignatureError)
h.initiatorNonce = header.nonce h.initiatorNonce = header.nonce
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
@ -347,9 +351,9 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
var reader = rlpFromBytes(buffer.toRange()) var reader = rlpFromBytes(buffer.toRange())
if not reader.isList() or reader.listLen() < 4: if not reader.isList() or reader.listLen() < 4:
return(InvalidAuth) return(InvalidAuth)
if reader.listElem(0).blobLen != SignatureLength: if reader.listElem(0).blobLen != RawSignatureSize:
return(InvalidAuth) return(InvalidAuth)
if reader.listElem(1).blobLen != PublicKeyLength: if reader.listElem(1).blobLen != RawPublicKeySize:
return(InvalidAuth) return(InvalidAuth)
if reader.listElem(2).blobLen != KeyLength: if reader.listElem(2).blobLen != KeyLength:
return(InvalidAuth) return(InvalidAuth)
@ -359,17 +363,17 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
var pubkeyBr = reader.listElem(1).toBytes() var pubkeyBr = reader.listElem(1).toBytes()
var nonceBr = reader.listElem(2).toBytes() var nonceBr = reader.listElem(2).toBytes()
var versionBr = reader.listElem(3).toBytes() var versionBr = reader.listElem(3).toBytes()
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength, if recoverPublicKey(pubkeyBr.toOpenArray(),
pubkey) != EccStatus.Success: pubkey) != EthKeysStatus.Success:
return(InvalidPubKey) return(InvalidPubKey)
copyMem(addr nonce[0], nonceBr.baseAddr, KeyLength) copyMem(addr nonce[0], nonceBr.baseAddr, KeyLength)
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success: if ecdhAgree(h.host.seckey, pubkey, secret) != EthKeysStatus.Success:
return(EcdhError) return(EcdhError)
var xornonce = nonce var xornonce = nonce
xornonce.sxor(secret) xornonce.sxor(secret.data)
if recoverSignatureKey(signatureBr.baseAddr, SignatureLength, if recoverSignatureKey(signatureBr.toOpenArray(),
addr xornonce[0], xornonce,
h.remoteEPubkey) != EccStatus.Success: h.remoteEPubkey) != EthKeysStatus.Success:
return(SignatureError) return(SignatureError)
h.initiatorNonce = nonce h.initiatorNonce = nonce
h.remoteHPubkey = pubkey h.remoteHPubkey = pubkey
@ -393,7 +397,7 @@ proc decodeAckMessageEip8*(h: var Handshake, m: openarray[byte]): AuthStatus =
var reader = rlpFromBytes(buffer.toRange()) var reader = rlpFromBytes(buffer.toRange())
if not reader.isList() or reader.listLen() < 3: if not reader.isList() or reader.listLen() < 3:
return(InvalidAck) return(InvalidAck)
if reader.listElem(0).blobLen != PublicKeyLength: if reader.listElem(0).blobLen != RawPublicKeySize:
return(InvalidAck) return(InvalidAck)
if reader.listElem(1).blobLen != KeyLength: if reader.listElem(1).blobLen != KeyLength:
return(InvalidAck) return(InvalidAck)
@ -402,8 +406,8 @@ proc decodeAckMessageEip8*(h: var Handshake, m: openarray[byte]): AuthStatus =
let pubkeyBr = reader.listElem(0).toBytes() let pubkeyBr = reader.listElem(0).toBytes()
let nonceBr = reader.listElem(1).toBytes() let nonceBr = reader.listElem(1).toBytes()
let versionBr = reader.listElem(2).toBytes() let versionBr = reader.listElem(2).toBytes()
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength, if recoverPublicKey(pubkeyBr.toOpenArray(),
h.remoteEPubkey) != EccStatus.Success: h.remoteEPubkey) != EthKeysStatus.Success:
return(InvalidPubKey) return(InvalidPubKey)
copyMem(addr h.responderNonce[0], nonceBr.baseAddr, KeyLength) copyMem(addr h.responderNonce[0], nonceBr.baseAddr, KeyLength)
h.version = cast[ptr byte](versionBr.baseAddr)[] h.version = cast[ptr byte](versionBr.baseAddr)[]
@ -419,7 +423,7 @@ proc decodeAckMessageV4(h: var Handshake, m: openarray[byte]): AuthStatus =
if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success: if eciesDecrypt(m, buffer, h.host.seckey) != EciesStatus.Success:
return(EciesError) return(EciesError)
var header = cast[ptr AckMessageV4](addr buffer[0]) var header = cast[ptr AckMessageV4](addr buffer[0])
if recoverPublicKey(header.pubkey.data, h.remoteEPubkey) != EccStatus.Success: if recoverPublicKey(header.pubkey, h.remoteEPubkey) != EthKeysStatus.Success:
return(InvalidPubKey) return(InvalidPubKey)
h.responderNonce = header.nonce h.responderNonce = header.nonce
result = Success result = Success
@ -476,7 +480,8 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
xornonce: Nonce xornonce: Nonce
# ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk) # ecdhe-secret = ecdh.agree(ephemeral-privkey, remote-ephemeral-pubk)
if ecdhAgree(h.ephemeral.seckey, h.remoteEPubkey, shsec) != EccStatus.Success: if ecdhAgree(h.ephemeral.seckey, h.remoteEPubkey,
shsec) != EthKeysStatus.Success:
return(EcdhError) return(EcdhError)
# shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce)) # shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce))
@ -486,19 +491,19 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
ctx1.update(h.initiatorNonce) ctx1.update(h.initiatorNonce)
mac1 = ctx1.finish() mac1 = ctx1.finish()
ctx1.clear() ctx1.clear()
ctx0.update(shsec) ctx0.update(shsec.data)
ctx0.update(mac1.data) ctx0.update(mac1.data)
mac1 = ctx0.finish() mac1 = ctx0.finish()
# aes-secret = keccak(ecdhe-secret || shared-secret) # aes-secret = keccak(ecdhe-secret || shared-secret)
ctx0.init() ctx0.init()
ctx0.update(shsec) ctx0.update(shsec.data)
ctx0.update(mac1.data) ctx0.update(mac1.data)
mac1 = ctx0.finish() mac1 = ctx0.finish()
# mac-secret = keccak(ecdhe-secret || aes-secret) # mac-secret = keccak(ecdhe-secret || aes-secret)
ctx0.init() ctx0.init()
ctx0.update(shsec) ctx0.update(shsec.data)
ctx0.update(mac1.data) ctx0.update(mac1.data)
secret.aesKey = mac1.data secret.aesKey = mac1.data
mac1 = ctx0.finish() mac1 = ctx0.finish()

View File

@ -1,323 +0,0 @@
#
# Ethereum P2P
# (c) Copyright 2018
# Status Research & Development GmbH
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
#
## This module implements `libsecp256k1` ECC/ECDH functions
import secp256k1, hexdump, nimcrypto/sysrand, nimcrypto/utils
const
KeyLength* = 32
PublicKeyLength* = 64
SignatureLength* = 65
type
EccContext* = ref object of RootRef
context*: ptr secp256k1_context
error*: string
EccStatus* = enum
Success, ## Operation was successful
Error ## Operation failed
PublicKey* = secp256k1_pubkey
## Representation of public key
PrivateKey* = array[KeyLength, byte]
## Representation of secret key
SharedSecret* = array[KeyLength, byte]
## Representation of ECDH shared secret
Nonce* = array[KeyLength, byte]
## Representation of nonce
RawPublickey* = object
## Representation of serialized public key
header*: byte
data*: array[KeyLength * 2, byte]
KeyPair* = object
## Representation of private/public keys pair
seckey*: PrivateKey
pubkey*: PublicKey
Signature* = secp256k1_ecdsa_recoverable_signature
## Representation of signature
RawSignature* = object
## Representation of serialized signature
data*: array[KeyLength * 2 + 1, byte]
Secp256k1Exception* = object of Exception
## Exceptions generated by `libsecp256k1`
EccException* = object of Exception
## Exception generated by this module
var eccContext* {.threadvar.}: EccContext
## Thread local variable which holds current context
proc illegalCallback(message: cstring; data: pointer) {.cdecl.} =
let ctx = cast[EccContext](data)
ctx.error = $message
proc errorCallback(message: cstring, data: pointer) {.cdecl.} =
let ctx = cast[EccContext](data)
ctx.error = $message
proc newEccContext*(): EccContext =
## Create new `EccContext`.
result = new EccContext
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 getSecpContext*(): ptr secp256k1_context =
## Get current `secp256k1_context`
if isNil(eccContext):
eccContext = newEccContext()
result = eccContext.context
proc getEccContext*(): EccContext =
## Get current `EccContext`
if isNil(eccContext):
eccContext = newEccContext()
result = eccContext
template raiseSecp256k1Error*() =
## Raises `libsecp256k1` error as exception
let mctx = getEccContext()
if len(mctx.error) > 0:
var msg = mctx.error
mctx.error.setLen(0)
raise newException(Secp256k1Exception, msg)
proc eccErrorMsg*(): string =
let mctx = getEccContext()
result = mctx.error
proc setErrorMsg*(m: string) =
let mctx = getEccContext()
mctx.error = m
proc getRaw*(pubkey: PublicKey): RawPublickey =
## Converts public key `pubkey` to serialized form of `secp256k1_pubkey`.
var length = csize(sizeof(RawPublickey))
let ctx = getSecpContext()
if secp256k1_ec_pubkey_serialize(ctx, cast[ptr cuchar](addr result),
addr length, unsafeAddr pubkey,
SECP256K1_EC_UNCOMPRESSED) != 1:
raiseSecp256k1Error()
if length != 65:
raise newException(EccException, "Invalid public key length!")
if result.header != 0x04'u8:
raise newException(EccException, "Invalid public key header!")
proc getRaw*(s: Signature): RawSignature =
## Converts signature `s` to serialized form.
let ctx = getSecpContext()
var recid = cint(0)
if secp256k1_ecdsa_recoverable_signature_serialize_compact(
ctx, cast[ptr cuchar](unsafeAddr result), addr recid, unsafeAddr s) != 1:
raiseSecp256k1Error()
result.data[64] = uint8(recid)
proc signMessage*(seckey: PrivateKey, data: ptr byte, length: int,
sig: var Signature): EccStatus =
## Sign message pointed by `data` with size `length` and save signature to
## `sig`.
let ctx = getSecpContext()
if secp256k1_ecdsa_sign_recoverable(ctx, addr sig,
cast[ptr cuchar](data),
cast[ptr cuchar](unsafeAddr seckey[0]),
nil, nil) != 1:
return(Error)
return(Success)
proc signMessage*[T](seckey: PrivateKey, data: openarray[T],
sig: var Signature, ostart: int = 0,
ofinish: int = -1): EccStatus =
## Sign message ``data``[`soffset`..`eoffset`] and store result into `sig`.
let so = ostart
let eo = if ofinish == -1: (len(data) - 1) else: ofinish
let length = (eo - so + 1) * sizeof(T)
# We don't need to check `so` because compiler will do it for `data[so]`.
if eo >= len(data):
setErrorMsg("Index is out of bounds!")
return(Error)
if len(data) < KeyLength or length < KeyLength:
setErrorMsg("There no reason to sign this message!")
return(Error)
result = signMessage(seckey, cast[ptr byte](unsafeAddr data[so]),
length, sig)
proc recoverSignatureKey*(data: ptr byte, length: int, message: ptr byte,
pubkey: var PublicKey): EccStatus =
## Check signature and return public key from `data` with size `length` and
## `message`.
let ctx = getSecpContext()
var s: secp256k1_ecdsa_recoverable_signature
if length >= 65:
var recid = cint(cast[ptr UncheckedArray[byte]](data)[KeyLength * 2])
if secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, addr s,
cast[ptr cuchar](data),
recid) != 1:
return(Error)
if secp256k1_ecdsa_recover(ctx, addr pubkey, addr s,
cast[ptr cuchar](message)) != 1:
setErrorMsg("Message signature verification failed!")
return(Error)
return(Success)
else:
setErrorMsg("Incorrect signature size")
return(Error)
proc recoverSignatureKey*[A, B](data: openarray[A],
message: openarray[B],
pubkey: var PublicKey,
ostart: int = 0,
ofinish: int = -1): EccStatus =
## Check signature in ``data``[`soffset`..`eoffset`] and recover public key
## from signature to ``pubkey`` using message `message`.
if len(message) == 0:
setErrorMsg("Message could not be empty!")
return(Error)
let so = ostart
let eo = if ofinish == -1: (len(data) - 1) else: ofinish
let length = (eo - so + 1) * sizeof(A)
# We don't need to check `so` because compiler will do it for `data[so]`.
if eo > len(data):
setErrorMsg("Index is out of bounds!")
return(Error)
if length < sizeof(RawSignature) or len(data) < sizeof(RawSignature):
setErrorMsg("Invalid signature size!")
return(Error)
result = recoverSignatureKey(cast[ptr byte](unsafeAddr data[so]), length,
cast[ptr byte](unsafeAddr message[0]), pubkey)
proc ecdhAgree*(seckey: PrivateKey, pubkey: PublicKey,
secret: var SharedSecret): EccStatus =
## Calculate ECDH shared secret
var res: array[KeyLength + 1, byte]
let ctx = getSecpContext()
if secp256k1_ecdh_raw(ctx, cast[ptr cuchar](addr res),
unsafeAddr pubkey,
cast[ptr cuchar](unsafeAddr seckey)) != 1:
return(Error)
copyMem(addr secret[0], addr res[1], KeyLength)
return(Success)
proc getPublicKey*(seckey: PrivateKey): PublicKey =
## Return public key for private key `seckey`.
let ctx = getSecpContext()
if secp256k1_ec_pubkey_create(ctx, addr result,
cast[ptr cuchar](unsafeAddr seckey[0])) != 1:
raiseSecp256k1Error()
proc recoverPublicKey*(data: ptr byte, length: int,
pubkey: var PublicKey): EccStatus =
## Unserialize public key from `data` pointer and size `length` and'
## set `pubkey`.
let ctx = getSecpContext()
if length < sizeof(PublicKey):
setErrorMsg("Invalid public key!")
return(Error)
var rawkey: RawPublickey
rawkey.header = 0x04 # mark key with COMPRESSED flag
copyMem(addr rawkey.data[0], data, len(rawkey.data))
if secp256k1_ec_pubkey_parse(ctx, addr pubkey,
cast[ptr cuchar](addr rawkey),
sizeof(RawPublickey)) != 1:
return(Error)
return(Success)
proc recoverPublicKey*[T](data: openarray[T], pubkey: var PublicKey,
ostart: int = 0, ofinish: int = -1, ): EccStatus =
## Unserialize public key from openarray[T] `data`, from position `ostart` to
## position `ofinish` and save it to `pubkey`.
let so = ostart
let eo = if ofinish == -1: (len(data) - 1) else: ofinish
let length = (eo - so + 1) * sizeof(T)
# We don't need to check `so` because compiler will do it for `data[so]`.
if eo > len(data):
setErrorMsg("Index is out of bounds!")
return(Error)
if length < sizeof(PublicKey) or len(data) < sizeof(PublicKey):
setErrorMsg("Invalid public key size!")
return(Error)
result = recoverPublicKey(cast[ptr byte](unsafeAddr data[so]), length,
pubkey)
proc newPrivateKey*(): PrivateKey =
## Generates new secret key.
let ctx = getSecpContext()
while true:
if randomBytes(result) == KeyLength:
if secp256k1_ec_seckey_verify(ctx, cast[ptr cuchar](addr result[0])) == 1:
break
proc newKeyPair*(): KeyPair =
## Generates new private and public key.
result.seckey = newPrivateKey()
result.pubkey = result.seckey.getPublicKey()
proc getPrivateKey*(hexstr: string): PrivateKey =
## Set secret key from hexadecimal string representation.
let ctx = getSecpContext()
var o = fromHex(stripSpaces(hexstr))
if len(o) < KeyLength:
raise newException(EccException, "Invalid private key!")
copyMem(addr result[0], unsafeAddr o[0], KeyLength)
if secp256k1_ec_seckey_verify(ctx, cast[ptr cuchar](addr result[0])) != 1:
raise newException(EccException, "Invalid private key!")
proc getPublicKey*(hexstr: string): PublicKey =
## Set public key from hexadecimal string representation.
var o = fromHex(stripSpaces(hexstr))
if recoverPublicKey(o, result) != Success:
raise newException(EccException, "Invalid public key!")
proc dump*(s: openarray[byte], c: string = ""): string =
## Return hexadecimal dump of array `s`.
result = if len(c) > 0: c & "=>\n" else: ""
if len(s) > 0:
result &= dumpHex(unsafeAddr s[0], len(s))
else:
result &= "[]"
proc dump*(s: PublicKey, c: string = ""): string =
## Return hexadecimal dump of public key `s`.
result = if len(c) > 0: c & "=>\n" else: ""
result &= dumpHex(unsafeAddr s.data[0], sizeof(secp256k1_pubkey))
proc dump*(s: RawSignature, c: string = ""): string =
## Return hexadecimal dump of serialized signature `s`.
result = if len(c) > 0: c & "=>\n" else: ""
result &= dumpHex(unsafeAddr s.data[0], sizeof(RawSignature))
proc dump*(s: RawPublickey, c: string = ""): string =
## Return hexadecimal dump of serialized public key `s`.
result = if len(c) > 0: c & "=>\n" else: ""
result &= dumpHex(unsafeAddr s, sizeof(RawSignature))
proc dump*(s: secp256k1_ecdsa_recoverable_signature, c: string = ""): string =
## Return hexadecimal dump of signature `s`.
result = if len(c) > 0: c & "=>\n" else: ""
result &= dumpHex(unsafeAddr s.data[0],
sizeof(secp256k1_ecdsa_recoverable_signature))
proc dump*(p: pointer, s: int, c: string = ""): string =
## Return hexadecimal dump of memory blob `p` and size `s`.
result = if len(c) > 0: c & "=>\n" else: ""
result &= dumpHex(p, s)

View File

@ -9,7 +9,8 @@
## This module implements ECIES method encryption/decryption. ## This module implements ECIES method encryption/decryption.
import ecc, nimcrypto/sha2, nimcrypto/hash, nimcrypto/hmac import eth_keys
import nimcrypto/sha2, nimcrypto/hash, nimcrypto/hmac
import nimcrypto/rijndael, nimcrypto/utils, nimcrypto/sysrand import nimcrypto/rijndael, nimcrypto/utils, nimcrypto/sysrand
import nimcrypto/bcmode, nimcrypto/utils import nimcrypto/bcmode, nimcrypto/utils
@ -30,7 +31,7 @@ type
EciesHeader* = object {.packed.} EciesHeader* = object {.packed.}
version*: byte version*: byte
pubkey*: array[PublicKeyLength, byte] pubkey*: array[RawPublicKeySize, byte]
iv*: array[aes128.sizeBlock, byte] iv*: array[aes128.sizeBlock, byte]
data*: byte data*: byte
@ -109,12 +110,11 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
return(RandomError) return(RandomError)
var ephemeral = newKeyPair() var ephemeral = newKeyPair()
var epub = ephemeral.pubkey.getRaw()
if ecdhAgree(ephemeral.seckey, pubkey, secret) != EccStatus.Success: if ecdhAgree(ephemeral.seckey, pubkey, secret) != EthKeysStatus.Success:
return(EcdhError) return(EcdhError)
material = kdf(secret) material = kdf(secret.data)
burnMem(secret) burnMem(secret)
copyMem(addr encKey[0], addr material[0], aes128.sizeKey) copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
@ -123,7 +123,7 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
var header = cast[ptr EciesHeader](addr output[0]) var header = cast[ptr EciesHeader](addr output[0])
header.version = 0x04 header.version = 0x04
header.pubkey = epub.data header.pubkey = ephemeral.pubkey.getRaw()
header.iv = iv header.iv = iv
var so = eciesDataPos() var so = eciesDataPos()
@ -176,12 +176,12 @@ proc eciesDecrypt*(input: openarray[byte],
return(IncompleteError) return(IncompleteError)
if len(input) - eciesOverheadLength() > len(output): if len(input) - eciesOverheadLength() > len(output):
return(BufferOverrun) return(BufferOverrun)
if recoverPublicKey(header.pubkey, pubkey) != EccStatus.Success: if recoverPublicKey(header.pubkey, pubkey) != EthKeysStatus.Success:
return(IncorrectKey) return(IncorrectKey)
if ecdhAgree(seckey, pubkey, secret) != EccStatus.Success: if ecdhAgree(seckey, pubkey, secret) != EthKeysStatus.Success:
return(EcdhError) return(EcdhError)
var material = kdf(secret) var material = kdf(secret.data)
burnMem(secret) burnMem(secret)
copyMem(addr encKey[0], addr material[0], aes128.sizeKey) copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
var macKey = sha256.digest(material, ostart = KeyLength div 2) var macKey = sha256.digest(material, ostart = KeyLength div 2)

View File

@ -8,7 +8,7 @@
# #
import unittest import unittest
import ethp2p/ecc, ethp2p/auth, nimcrypto/utils import eth_keys, ethp2p/auth, nimcrypto/utils
# This was generated by `print` actual auth message generated by # This was generated by `print` actual auth message generated by
# https://github.com/ethereum/py-evm/blob/master/tests/p2p/test_auth.py # https://github.com/ethereum/py-evm/blob/master/tests/p2p/test_auth.py
@ -216,18 +216,18 @@ suite "Ethereum P2P handshake test suite":
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake = proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
result = newHandshake(flags) result = newHandshake(flags)
if Initiator in flags: if Initiator in flags:
result.host.seckey = getPrivateKey(testValue("initiator_private_key")) result.host.seckey = initPrivateKey(testValue("initiator_private_key"))
result.host.pubkey = result.host.seckey.getPublicKey() result.host.pubkey = result.host.seckey.getPublicKey()
let epki = testValue("initiator_ephemeral_private_key") let epki = testValue("initiator_ephemeral_private_key")
result.ephemeral.seckey = getPrivateKey(epki) result.ephemeral.seckey = initPrivateKey(epki)
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey() result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
let nonce = fromHex(stripSpaces(testValue("initiator_nonce"))) let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1] result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags: elif Responder in flags:
result.host.seckey = getPrivateKey(testValue("receiver_private_key")) result.host.seckey = initPrivateKey(testValue("receiver_private_key"))
result.host.pubkey = result.host.seckey.getPublicKey() result.host.pubkey = result.host.seckey.getPublicKey()
let epkr = testValue("receiver_ephemeral_private_key") let epkr = testValue("receiver_ephemeral_private_key")
result.ephemeral.seckey = getPrivateKey(epkr) result.ephemeral.seckey = initPrivateKey(epkr)
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey() result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
let nonce = fromHex(stripSpaces(testValue("receiver_nonce"))) let nonce = fromHex(stripSpaces(testValue("receiver_nonce")))
result.responderNonce[0..^1] = nonce[0..^1] result.responderNonce[0..^1] = nonce[0..^1]
@ -326,18 +326,18 @@ suite "Ethereum P2P handshake test suite":
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake = proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
result = newHandshake(flags) result = newHandshake(flags)
if Initiator in flags: if Initiator in flags:
result.host.seckey = getPrivateKey(testE8Value("initiator_private_key")) result.host.seckey = initPrivateKey(testE8Value("initiator_private_key"))
result.host.pubkey = result.host.seckey.getPublicKey() result.host.pubkey = result.host.seckey.getPublicKey()
let esec = testE8Value("initiator_ephemeral_private_key") let esec = testE8Value("initiator_ephemeral_private_key")
result.ephemeral.seckey = getPrivateKey(esec) result.ephemeral.seckey = initPrivateKey(esec)
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey() result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce"))) let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
result.initiatorNonce[0..^1] = nonce[0..^1] result.initiatorNonce[0..^1] = nonce[0..^1]
elif Responder in flags: elif Responder in flags:
result.host.seckey = getPrivateKey(testE8Value("receiver_private_key")) result.host.seckey = initPrivateKey(testE8Value("receiver_private_key"))
result.host.pubkey = result.host.seckey.getPublicKey() result.host.pubkey = result.host.seckey.getPublicKey()
let esec = testE8Value("receiver_ephemeral_private_key") let esec = testE8Value("receiver_ephemeral_private_key")
result.ephemeral.seckey = getPrivateKey(esec) result.ephemeral.seckey = initPrivateKey(esec)
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey() result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce"))) let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce")))
result.responderNonce[0..^1] = nonce[0..^1] result.responderNonce[0..^1] = nonce[0..^1]

View File

@ -1,139 +0,0 @@
#
# Ethereum P2P
# (c) Copyright 2018
# Status Research & Development GmbH
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
#
import unittest
import ethp2p/ecc
import nimcrypto/hash, nimcrypto/keccak, nimcrypto/utils
proc compare(x: openarray[byte], y: openarray[byte]): bool =
result = len(x) == len(y)
if result:
for i in 0..(len(x) - 1):
if x[i] != y[i]:
result = false
break
suite "ECC/ECDSA/ECDHE tests suite":
test "ECDHE/py-evm test_ecies.py#L19":
# ECDHE test vectors
# Copied from
# https://github.com/ethereum/py-evm/blob/master/tests/p2p/test_ecies.py#L19
const privateKeys = [
"332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b",
"7ebbc6a8358bc76dd73ebc557056702c8cfc34e5cfcd90eb83af0347575fd2ad"
]
const publicKeys = [
"""f0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07
f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1""",
"""83ede0f19c3c98649265956a4193677b14c338a22de2086a08d84e4446fe37e4e
233478259ec90dbeef52f4f6c890f8c38660ec7b61b9d439b8a6d1c323dc025"""
]
const sharedSecrets = [
"ee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08",
"167ccc13ac5e8a26b131c3446030c60fbfac6aa8e31149d0869f93626a4cdf62"
]
var secret: array[KeyLength, byte]
for i in 0..1:
var s = privateKeys[i].getPrivateKey()
var p = publicKeys[i].getPublicKey()
let expect = fromHex(stripSpaces(sharedSecrets[i]))
check:
ecdhAgree(s, p, secret) == Success
compare(expect, secret) == true
test "ECDHE/cpp-ethereum crypto.cpp#L394":
# ECDHE test vectors
# Copied from https://github.com/ethereum/cpp-ethereum/blob/develop/test/unittests/libdevcrypto/crypto.cpp#L394
var expectm = """
8ac7e464348b85d9fdfc0a81f2fdc0bbbb8ee5fb3840de6ed60ad9372e718977"""
var secret: array[KeyLength, byte]
var s = keccak256.digest("ecdhAgree").data
var p = s.getPublicKey()
let expect = fromHex(stripSpaces(expectm))
check:
ecdhAgree(s, p, secret) == Success
compare(expect, secret) == true
test "ECDHE/cpp-ethereum rlpx.cpp#L425":
# ECDHE test vectors
# Copied from https://github.com/ethereum/cpp-ethereum/blob/2409d7ec7d34d5ff5770463b87eb87f758e621fe/test/unittests/libp2p/rlpx.cpp#L425
var s0 = """
332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"""
var p0 = """
f0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a0
7f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1"""
var e0 = """
ee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08"""
var secret: array[KeyLength, byte]
var s = getPrivateKey(s0)
var p = getPublicKey(p0)
let expect = fromHex(stripSpaces(e0))
check:
ecdhAgree(s, p, secret) == Success
compare(expect, secret) == true
test "ECDSA/cpp-ethereum crypto.cpp#L132":
# ECDSA test vectors
# Copied from https://github.com/ethereum/cpp-ethereum/blob/develop/test/unittests/libdevcrypto/crypto.cpp#L132
var signature = """
b826808a8c41e00b7c5d71f211f005a84a7b97949d5e765831e1da4e34c9b8295d
2a622eee50f25af78241c1cb7cfff11bcf2a13fe65dee1e3b86fd79a4e3ed000"""
var pubkey = """
e40930c838d6cca526795596e368d16083f0672f4ab61788277abfa23c3740e1cc
84453b0b24f49086feba0bd978bb4446bae8dff1e79fcc1e9cf482ec2d07c3"""
var check1 = fromHex(stripSpaces(signature))
var check2 = fromHex(stripSpaces(pubkey))
var sig: Signature
var key: PublicKey
var s = keccak256.digest("sec").data
var m = keccak256.digest("msg").data
check signMessage(s, m, sig) == Success
var sersig = sig.getRaw().data
check recoverSignatureKey(sersig, m, key) == Success
var serkey = key.getRaw().data
check:
compare(sersig, check1) == true
compare(serkey, check2) == true
test "ECDSA/100 signatures":
# signature test
var rkey: PublicKey
var sig: Signature
for i in 1..100:
var m = newPrivateKey()
var s = newPrivateKey()
var key = s.getPublicKey()
check signMessage(s, m, sig) == Success
var sersig = sig.getRaw().data
check:
recoverSignatureKey(sersig, m, rkey) == Success
key == rkey
test "KEYS/100 create/recovery keys":
# key create/recovery test
var rkey: PublicKey
for i in 1..100:
var s = newPrivateKey()
var key = s.getPublicKey()
check:
recoverPublicKey(key.getRaw().data, rkey) == Success
key == rkey
test "ECDHE/100 shared secrets":
# ECDHE shared secret test
var secret1, secret2: SharedSecret
for i in 1..100:
var aliceSecret = newPrivateKey()
var alicePublic = aliceSecret.getPublicKey()
var bobSecret = newPrivateKey()
var bobPublic = bobSecret.getPublicKey()
check:
ecdhAgree(aliceSecret, bobPublic, secret1) == Success
ecdhAgree(bobSecret, alicePublic, secret2) == Success
secret1 == secret2

View File

@ -8,7 +8,7 @@
# #
import unittest import unittest
import ethp2p/ecc, ethp2p/ecies import eth_keys, ethp2p/ecies
import nimcrypto/utils, nimcrypto/sha2, nimcrypto/hmac, nimcrypto/rijndael import nimcrypto/utils, nimcrypto/sha2, nimcrypto/hmac, nimcrypto/rijndael
proc compare[A, B](x: openarray[A], y: openarray[B], s: int = 0): bool = proc compare[A, B](x: openarray[A], y: openarray[B], s: int = 0): bool =
@ -123,7 +123,7 @@ suite "ECIES test suite":
] ]
var data: array[1024, byte] var data: array[1024, byte]
for i in 0..1: for i in 0..1:
var s = secretKeys[i].getPrivateKey() var s = initPrivateKey(secretKeys[i])
var cipher = fromHex(stripSpaces(cipherText[i])) var cipher = fromHex(stripSpaces(cipherText[i]))
var expect = fromHex(stripSpaces(expectText[i])) var expect = fromHex(stripSpaces(expectText[i]))
check: check:
@ -164,7 +164,7 @@ suite "ECIES test suite":
] ]
var data: array[1024, byte] var data: array[1024, byte]
for i in 0..3: for i in 0..3:
var s = secretKeys[i].getPrivateKey() var s = initPrivateKey(secretKeys[i])
var cipher = fromHex(stripSpaces(cipherData[i])) var cipher = fromHex(stripSpaces(cipherData[i]))
check: check:
eciesDecrypt(cipher, data, s) == EciesStatus.Success eciesDecrypt(cipher, data, s) == EciesStatus.Success