Some fixes and tests for ECIES.

This commit is contained in:
cheatfate 2018-03-30 18:42:23 +03:00
parent f020ff4712
commit d8c63e7da1
4 changed files with 51 additions and 34 deletions

View File

@ -26,6 +26,18 @@ const
AuthAckMessageLength* = 210 AuthAckMessageLength* = 210
type type
PlainAuthMessage* = object {.packed.}
signature: RawSignature
keyhash: array[keccak256.sizeDigest, byte]
pubkey: PublicKey
nonce: array[keccak256.sizeDigest, byte]
flag: byte
PlainAuthAckMessage* = object {.packed.}
pubkey: PublicKey
nonce: array[keccak256.sizeDigest, byte]
flag: byte
HandshakeFlag* = enum HandshakeFlag* = enum
Initiator, ## `Handshake` owner is connection initiator Initiator, ## `Handshake` owner is connection initiator
Responder, ## `Handshake` owner is connection responder Responder, ## `Handshake` owner is connection responder
@ -62,8 +74,8 @@ type
egressMac*: array[keccak256.sizeDigest, byte] egressMac*: array[keccak256.sizeDigest, byte]
ingressMac*: array[keccak256.sizeDigest, byte] ingressMac*: array[keccak256.sizeDigest, byte]
PlainAuthMessage* = array[PlainAuthMessageLength, byte] # PlainAuthMessage* = array[PlainAuthMessageLength, byte]
PlainAuthAckMessage* = array[PlainAuthAckMessageLength, byte] # PlainAuthAckMessage* = array[PlainAuthAckMessageLength, byte]
AuthMessage* = array[AuthMessageLength, byte] AuthMessage* = array[AuthMessageLength, byte]
AuthAckMessage* = array[AuthAckMessageLength, byte] AuthAckMessage* = array[AuthAckMessageLength, byte]
@ -95,50 +107,44 @@ proc newHandshake*(flags: set[HandshakeFlag] = {Initiator}): Handshake =
result.ephemeral = newKeyPair() result.ephemeral = newKeyPair()
if Initiator in flags: if Initiator in flags:
p = addr result.initiatorNonce[0] if randomBytes(result.initiatorNonce) != len(result.initiatorNonce):
raise newException(AuthException, "Could not obtain random data!")
else: else:
p = addr result.responderNonce[0] if randomBytes(result.responderNonce) != len(result.responderNonce):
if randomBytes(p, KeyLength) != KeyLength:
raise newException(AuthException, "Could not obtain random data!") raise newException(AuthException, "Could not obtain random data!")
proc authMessage*(h: var Handshake, proc authMessagePreEIP8*(h: var Handshake,
pubkey: PublicKey, pubkey: PublicKey,
output: var PlainAuthMessage): AuthStatus = output: var PlainAuthMessage,
flag: int = 0): AuthStatus =
## Create plain preEIP8 authentication message. ## Create plain preEIP8 authentication message.
var secret: SharedSecret var secret: SharedSecret
var signature: Signature var signature: Signature
var flag = byte(0x00) var flagb = byte(flag)
if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success: if ecdhAgree(h.host.seckey, pubkey, secret) != EccStatus.Success:
return(EcdhError) return(EcdhError)
if h.initiatorNonce.empty():
if randomBytes(addr h.initiatorNonce[0], KeyLength) != KeyLength:
return(RandomError)
var xornonce = h.initiatorNonce var xornonce = h.initiatorNonce
xornonce.sxor(secret) xornonce.sxor(secret)
if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success: if signMessage(h.ephemeral.seckey, xornonce, signature) != EccStatus.Success:
return(SignatureError) return(SignatureError)
copyMem(addr h.remoteHPubkey, unsafeAddr pubkey, sizeof(PublicKey)) h.remoteHPubkey = pubkey
move(output, signature.getRaw().data, 0, 64) output.signature = signature.getRaw()
move(output, keccak256.digest(h.ephemeral.pubkey.getRaw().data).data, 65, 96) output.keyhash = keccak256.digest(h.ephemeral.pubkey.getRaw().data).data
move(output, h.host.pubkey.getRaw().data, 97, 160) output.pubkey = cast[PublicKey](h.host.pubkey.getRaw().data)
move(output, h.initiatorNonce, 161, 192) output.nonce = h.initiatorNonce
output[193] = flag output.flag = flagb
proc authAckMessage*(h: var Handshake, proc authAckMessagePreEIP8*(h: var Handshake,
output: var PlainAuthAckMessage): AuthStatus = output: var PlainAuthAckMessage,
if EIP8 in h.flags: flag: int = 0): AuthStatus =
discard output.pubkey = cast[PublicKey](h.ephemeral.pubkey.getRaw().data)
else: output.nonce = h.responderNonce
move(output, h.ephemeral.pubkey.getRaw().data, 0, 63) output.flag = byte(flag)
move(output, h.responderNonce, 64, 95)
output[96] = byte(0x00)
proc encryptAuthMessage*(input: ptr byte, inputlen: int, proc encryptAuthMessage*(input: ptr byte, inputlen: int,
output: ptr byte, outputlen: int, output: ptr byte, outputlen: int,

View File

@ -16,7 +16,6 @@ const
PublicKeyLength* = 64 PublicKeyLength* = 64
SignatureLength* = 65 SignatureLength* = 65
type type
EccContext* = ref object of RootRef EccContext* = ref object of RootRef
context*: ptr secp256k1_context context*: ptr secp256k1_context
@ -264,7 +263,7 @@ proc newPrivateKey*(): PrivateKey =
## Generates new secret key. ## Generates new secret key.
let ctx = getSecpContext() let ctx = getSecpContext()
while true: while true:
if randomBytes(addr result[0], KeyLength) == KeyLength: if randomBytes(result) == KeyLength:
if secp256k1_ec_seckey_verify(ctx, cast[ptr cuchar](addr result[0])) == 1: if secp256k1_ec_seckey_verify(ctx, cast[ptr cuchar](addr result[0])) == 1:
break break

View File

@ -166,7 +166,7 @@ proc eciesDecrypt*(input: openarray[byte],
ctx: HMAC[sha256] ctx: HMAC[sha256]
secret: SharedSecret secret: SharedSecret
if len(input) == 0: if len(input) <= 0:
return(IncompleteError) return(IncompleteError)
var header = cast[ptr EciesHeader](unsafeAddr input[0]) var header = cast[ptr EciesHeader](unsafeAddr input[0])
@ -188,7 +188,6 @@ proc eciesDecrypt*(input: openarray[byte],
burnMem(material) burnMem(material)
let macsize = eciesMacLength(len(input) - eciesOverheadLength()) let macsize = eciesMacLength(len(input) - eciesOverheadLength())
let datsize = eciesDecryptedLength(len(input))
ctx.init(macKey.data) ctx.init(macKey.data)
burnMem(macKey) burnMem(macKey)
ctx.update(toOpenArray(input, eciesIvPos(), eciesIvPos() + macsize - 1)) ctx.update(toOpenArray(input, eciesIvPos(), eciesIvPos() + macsize - 1))
@ -201,6 +200,7 @@ proc eciesDecrypt*(input: openarray[byte],
sha256.sizeDigest): sha256.sizeDigest):
return(IncorrectTag) return(IncorrectTag)
let datsize = eciesDecryptedLength(len(input))
cipher.init(encKey, header.iv) cipher.init(encKey, header.iv)
burnMem(encKey) burnMem(encKey)
cipher.decrypt(toOpenArray(input, eciesDataPos(), cipher.decrypt(toOpenArray(input, eciesDataPos(),

View File

@ -9,7 +9,7 @@
import unittest import unittest
import ethp2p/ecc, ethp2p/ecies import ethp2p/ecc, ethp2p/ecies
import nimcrypto/utils, nimcrypto/sha2, nimcrypto/hmac 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 =
result = true result = true
@ -20,7 +20,19 @@ proc compare[A, B](x: openarray[A], y: openarray[B], s: int = 0): bool =
result = false result = false
break break
template offsetOf(a, b): int =
cast[int](cast[uint](unsafeAddr b) - cast[uint](unsafeAddr a))
suite "ECIES test suite": suite "ECIES test suite":
test "ECIES structures alignment":
var header: EciesHeader
check:
offsetOf(header, header.version) == 0
offsetOf(header, header.pubkey) == 1
offsetOf(header, header.iv) == 1 + 64
offsetOf(header, header.data) == 1 + 64 + aes128.sizeBlock
sizeof(header) == 1 + 64 + aes128.sizeBlock + 1
test "KDF test vectors": test "KDF test vectors":
# KDF test # KDF test
# Copied from https://github.com/ethereum/pydevp2p/blob/develop/devp2p/tests/test_ecies.py#L53 # Copied from https://github.com/ethereum/pydevp2p/blob/develop/devp2p/tests/test_ecies.py#L53