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:
parent
f8ba94b39e
commit
d7bd43c334
|
@ -18,7 +18,6 @@ requires "nim > 0.18.0",
|
|||
proc runTest(name: string, lang = "c") = exec "nim " & lang & " -r tests/" & name
|
||||
|
||||
task test, "Runs the test suite":
|
||||
runTest "testecc"
|
||||
runTest "testecies"
|
||||
runTest "testauth"
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
## This module implements Ethereum authentication
|
||||
|
||||
import endians
|
||||
import ecc, ecies, rlp
|
||||
import eth_keys, ecies, rlp
|
||||
import nimcrypto/sysrand, nimcrypto/hash, nimcrypto/utils, nimcrypto/hmac
|
||||
import nimcrypto/rijndael, nimcrypto/keccak, nimcrypto/sha2
|
||||
|
||||
|
@ -30,15 +30,17 @@ const
|
|||
AckMessageMaxEIP8* = AckMessageEIP8Length + 255
|
||||
|
||||
type
|
||||
Nonce* = array[KeyLength, byte]
|
||||
|
||||
AuthMessageV4* = object {.packed.}
|
||||
signature: RawSignature
|
||||
signature: array[RawSignatureSize, byte]
|
||||
keyhash: array[keccak256.sizeDigest, byte]
|
||||
pubkey: PublicKey
|
||||
nonce: array[keccak256.sizeDigest, byte]
|
||||
flag: byte
|
||||
|
||||
AckMessageV4* = object {.packed.}
|
||||
pubkey: PublicKey
|
||||
pubkey: array[RawPublicKeySize, byte]
|
||||
nonce: array[keccak256.sizeDigest, byte]
|
||||
flag: byte
|
||||
|
||||
|
@ -115,16 +117,17 @@ proc authMessagePreEIP8(h: var Handshake,
|
|||
outlen = 0
|
||||
flagb = byte(flag)
|
||||
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)
|
||||
var xornonce = h.initiatorNonce
|
||||
xornonce.sxor(secret)
|
||||
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success:
|
||||
xornonce.sxor(secret.data)
|
||||
if signRawMessage(xornonce, h.ephemeral.seckey,
|
||||
signature) != EthKeysStatus.Success:
|
||||
return(SignatureError)
|
||||
h.remoteHPubkey = pubkey
|
||||
header.signature = signature.getRaw()
|
||||
header.keyhash = keccak256.digest(h.ephemeral.pubkey.getRaw().data).data
|
||||
header.pubkey = cast[PublicKey](h.host.pubkey.getRaw().data)
|
||||
header.keyhash = keccak256.digest(h.ephemeral.pubkey.getRaw()).data
|
||||
header.pubkey = cast[PublicKey](h.host.pubkey.getRaw())
|
||||
header.nonce = h.initiatorNonce
|
||||
header.flag = flagb
|
||||
if encrypt:
|
||||
|
@ -156,15 +159,16 @@ proc authMessageEIP8(h: var Handshake,
|
|||
|
||||
assert(EIP8 in h.flags)
|
||||
outlen = 0
|
||||
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
||||
if ecdhAgree(h.host.seckey, pubkey, secret) != EthKeysStatus.Success:
|
||||
return(EcdhError)
|
||||
var xornonce = h.initiatorNonce
|
||||
xornonce.sxor(secret)
|
||||
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success:
|
||||
xornonce.sxor(secret.data)
|
||||
if signRawMessage(xornonce, h.ephemeral.seckey,
|
||||
signature) != EthKeysStatus.Success:
|
||||
return(SignatureError)
|
||||
h.remoteHPubkey = pubkey
|
||||
var payload = rlp.encodeList(signature.getRaw().data,
|
||||
h.host.pubkey.getRaw().data,
|
||||
var payload = rlp.encodeList(signature.getRaw(),
|
||||
h.host.pubkey.getRaw(),
|
||||
h.initiatorNonce,
|
||||
[byte(h.version)])
|
||||
assert(len(payload) == PlainAuthMessageEIP8Length)
|
||||
|
@ -208,7 +212,7 @@ proc ackMessagePreEIP8(h: var Handshake,
|
|||
var buffer: array[PlainAckMessageV4Length, byte]
|
||||
outlen = 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.flag = byte(flag)
|
||||
if encrypt:
|
||||
|
@ -234,7 +238,7 @@ proc ackMessageEIP8(h: var Handshake,
|
|||
buffer: array[PlainAckMessageMaxEIP8, byte]
|
||||
padsize: byte
|
||||
assert(EIP8 in h.flags)
|
||||
var payload = rlp.encodeList(h.ephemeral.pubkey.getRaw().data,
|
||||
var payload = rlp.encodeList(h.ephemeral.pubkey.getRaw(),
|
||||
h.responderNonce,
|
||||
[byte(h.version)])
|
||||
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:
|
||||
return(EciesError)
|
||||
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)
|
||||
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
|
||||
if ecdhAgree(h.host.seckey, pubkey, secret) != EthKeysStatus.Success:
|
||||
return(EcdhError)
|
||||
var xornonce = header.nonce
|
||||
xornonce.sxor(secret)
|
||||
if recoverSignatureKey(header.signature.data, xornonce,
|
||||
h.remoteEPubkey) != EccStatus.Success:
|
||||
xornonce.sxor(secret.data)
|
||||
if recoverSignatureKey(header.signature, xornonce,
|
||||
h.remoteEPubkey) != EthKeysStatus.Success:
|
||||
return(SignatureError)
|
||||
h.initiatorNonce = header.nonce
|
||||
h.remoteHPubkey = pubkey
|
||||
|
@ -347,9 +351,9 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
|
|||
var reader = rlpFromBytes(buffer.toRange())
|
||||
if not reader.isList() or reader.listLen() < 4:
|
||||
return(InvalidAuth)
|
||||
if reader.listElem(0).blobLen != SignatureLength:
|
||||
if reader.listElem(0).blobLen != RawSignatureSize:
|
||||
return(InvalidAuth)
|
||||
if reader.listElem(1).blobLen != PublicKeyLength:
|
||||
if reader.listElem(1).blobLen != RawPublicKeySize:
|
||||
return(InvalidAuth)
|
||||
if reader.listElem(2).blobLen != KeyLength:
|
||||
return(InvalidAuth)
|
||||
|
@ -359,17 +363,17 @@ proc decodeAuthMessageEip8(h: var Handshake, m: openarray[byte]): AuthStatus =
|
|||
var pubkeyBr = reader.listElem(1).toBytes()
|
||||
var nonceBr = reader.listElem(2).toBytes()
|
||||
var versionBr = reader.listElem(3).toBytes()
|
||||
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength,
|
||||
pubkey) != EccStatus.Success:
|
||||
if recoverPublicKey(pubkeyBr.toOpenArray(),
|
||||
pubkey) != EthKeysStatus.Success:
|
||||
return(InvalidPubKey)
|
||||
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)
|
||||
var xornonce = nonce
|
||||
xornonce.sxor(secret)
|
||||
if recoverSignatureKey(signatureBr.baseAddr, SignatureLength,
|
||||
addr xornonce[0],
|
||||
h.remoteEPubkey) != EccStatus.Success:
|
||||
xornonce.sxor(secret.data)
|
||||
if recoverSignatureKey(signatureBr.toOpenArray(),
|
||||
xornonce,
|
||||
h.remoteEPubkey) != EthKeysStatus.Success:
|
||||
return(SignatureError)
|
||||
h.initiatorNonce = nonce
|
||||
h.remoteHPubkey = pubkey
|
||||
|
@ -393,7 +397,7 @@ proc decodeAckMessageEip8*(h: var Handshake, m: openarray[byte]): AuthStatus =
|
|||
var reader = rlpFromBytes(buffer.toRange())
|
||||
if not reader.isList() or reader.listLen() < 3:
|
||||
return(InvalidAck)
|
||||
if reader.listElem(0).blobLen != PublicKeyLength:
|
||||
if reader.listElem(0).blobLen != RawPublicKeySize:
|
||||
return(InvalidAck)
|
||||
if reader.listElem(1).blobLen != KeyLength:
|
||||
return(InvalidAck)
|
||||
|
@ -402,8 +406,8 @@ proc decodeAckMessageEip8*(h: var Handshake, m: openarray[byte]): AuthStatus =
|
|||
let pubkeyBr = reader.listElem(0).toBytes()
|
||||
let nonceBr = reader.listElem(1).toBytes()
|
||||
let versionBr = reader.listElem(2).toBytes()
|
||||
if recoverPublicKey(pubkeyBr.baseAddr, PublicKeyLength,
|
||||
h.remoteEPubkey) != EccStatus.Success:
|
||||
if recoverPublicKey(pubkeyBr.toOpenArray(),
|
||||
h.remoteEPubkey) != EthKeysStatus.Success:
|
||||
return(InvalidPubKey)
|
||||
copyMem(addr h.responderNonce[0], nonceBr.baseAddr, KeyLength)
|
||||
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:
|
||||
return(EciesError)
|
||||
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)
|
||||
h.responderNonce = header.nonce
|
||||
result = Success
|
||||
|
@ -476,7 +480,8 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
|
|||
xornonce: Nonce
|
||||
|
||||
# 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)
|
||||
|
||||
# shared-secret = keccak(ecdhe-secret || keccak(nonce || initiator-nonce))
|
||||
|
@ -486,19 +491,19 @@ proc getSecrets*(h: Handshake, authmsg: openarray[byte],
|
|||
ctx1.update(h.initiatorNonce)
|
||||
mac1 = ctx1.finish()
|
||||
ctx1.clear()
|
||||
ctx0.update(shsec)
|
||||
ctx0.update(shsec.data)
|
||||
ctx0.update(mac1.data)
|
||||
mac1 = ctx0.finish()
|
||||
|
||||
# aes-secret = keccak(ecdhe-secret || shared-secret)
|
||||
ctx0.init()
|
||||
ctx0.update(shsec)
|
||||
ctx0.update(shsec.data)
|
||||
ctx0.update(mac1.data)
|
||||
mac1 = ctx0.finish()
|
||||
|
||||
# mac-secret = keccak(ecdhe-secret || aes-secret)
|
||||
ctx0.init()
|
||||
ctx0.update(shsec)
|
||||
ctx0.update(shsec.data)
|
||||
ctx0.update(mac1.data)
|
||||
secret.aesKey = mac1.data
|
||||
mac1 = ctx0.finish()
|
||||
|
|
323
ethp2p/ecc.nim
323
ethp2p/ecc.nim
|
@ -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)
|
|
@ -9,7 +9,8 @@
|
|||
|
||||
## 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/bcmode, nimcrypto/utils
|
||||
|
||||
|
@ -30,7 +31,7 @@ type
|
|||
|
||||
EciesHeader* = object {.packed.}
|
||||
version*: byte
|
||||
pubkey*: array[PublicKeyLength, byte]
|
||||
pubkey*: array[RawPublicKeySize, byte]
|
||||
iv*: array[aes128.sizeBlock, byte]
|
||||
data*: byte
|
||||
|
||||
|
@ -109,12 +110,11 @@ proc eciesEncrypt*(input: openarray[byte], output: var openarray[byte],
|
|||
return(RandomError)
|
||||
|
||||
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)
|
||||
|
||||
material = kdf(secret)
|
||||
material = kdf(secret.data)
|
||||
burnMem(secret)
|
||||
|
||||
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])
|
||||
header.version = 0x04
|
||||
header.pubkey = epub.data
|
||||
header.pubkey = ephemeral.pubkey.getRaw()
|
||||
header.iv = iv
|
||||
|
||||
var so = eciesDataPos()
|
||||
|
@ -176,12 +176,12 @@ proc eciesDecrypt*(input: openarray[byte],
|
|||
return(IncompleteError)
|
||||
if len(input) - eciesOverheadLength() > len(output):
|
||||
return(BufferOverrun)
|
||||
if recoverPublicKey(header.pubkey, pubkey) != EccStatus.Success:
|
||||
if recoverPublicKey(header.pubkey, pubkey) != EthKeysStatus.Success:
|
||||
return(IncorrectKey)
|
||||
if ecdhAgree(seckey, pubkey, secret) != EccStatus.Success:
|
||||
if ecdhAgree(seckey, pubkey, secret) != EthKeysStatus.Success:
|
||||
return(EcdhError)
|
||||
|
||||
var material = kdf(secret)
|
||||
var material = kdf(secret.data)
|
||||
burnMem(secret)
|
||||
copyMem(addr encKey[0], addr material[0], aes128.sizeKey)
|
||||
var macKey = sha256.digest(material, ostart = KeyLength div 2)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#
|
||||
|
||||
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
|
||||
# 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 =
|
||||
result = newHandshake(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()
|
||||
let epki = testValue("initiator_ephemeral_private_key")
|
||||
result.ephemeral.seckey = getPrivateKey(epki)
|
||||
result.ephemeral.seckey = initPrivateKey(epki)
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||
let nonce = fromHex(stripSpaces(testValue("initiator_nonce")))
|
||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||
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()
|
||||
let epkr = testValue("receiver_ephemeral_private_key")
|
||||
result.ephemeral.seckey = getPrivateKey(epkr)
|
||||
result.ephemeral.seckey = initPrivateKey(epkr)
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||
let nonce = fromHex(stripSpaces(testValue("receiver_nonce")))
|
||||
result.responderNonce[0..^1] = nonce[0..^1]
|
||||
|
@ -326,18 +326,18 @@ suite "Ethereum P2P handshake test suite":
|
|||
proc newTestHandshake(flags: set[HandshakeFlag]): Handshake =
|
||||
result = newHandshake(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()
|
||||
let esec = testE8Value("initiator_ephemeral_private_key")
|
||||
result.ephemeral.seckey = getPrivateKey(esec)
|
||||
result.ephemeral.seckey = initPrivateKey(esec)
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||
let nonce = fromHex(stripSpaces(testE8Value("initiator_nonce")))
|
||||
result.initiatorNonce[0..^1] = nonce[0..^1]
|
||||
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()
|
||||
let esec = testE8Value("receiver_ephemeral_private_key")
|
||||
result.ephemeral.seckey = getPrivateKey(esec)
|
||||
result.ephemeral.seckey = initPrivateKey(esec)
|
||||
result.ephemeral.pubkey = result.ephemeral.seckey.getPublicKey()
|
||||
let nonce = fromHex(stripSpaces(testE8Value("receiver_nonce")))
|
||||
result.responderNonce[0..^1] = nonce[0..^1]
|
||||
|
|
|
@ -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
|
|
@ -8,7 +8,7 @@
|
|||
#
|
||||
|
||||
import unittest
|
||||
import ethp2p/ecc, ethp2p/ecies
|
||||
import eth_keys, ethp2p/ecies
|
||||
import nimcrypto/utils, nimcrypto/sha2, nimcrypto/hmac, nimcrypto/rijndael
|
||||
|
||||
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]
|
||||
for i in 0..1:
|
||||
var s = secretKeys[i].getPrivateKey()
|
||||
var s = initPrivateKey(secretKeys[i])
|
||||
var cipher = fromHex(stripSpaces(cipherText[i]))
|
||||
var expect = fromHex(stripSpaces(expectText[i]))
|
||||
check:
|
||||
|
@ -164,7 +164,7 @@ suite "ECIES test suite":
|
|||
]
|
||||
var data: array[1024, byte]
|
||||
for i in 0..3:
|
||||
var s = secretKeys[i].getPrivateKey()
|
||||
var s = initPrivateKey(secretKeys[i])
|
||||
var cipher = fromHex(stripSpaces(cipherData[i]))
|
||||
check:
|
||||
eciesDecrypt(cipher, data, s) == EciesStatus.Success
|
||||
|
|
Loading…
Reference in New Issue