Fix import bugs.

Add PublicKey and Signature types to daemonapi.
Add more tests for crypto.nim
This commit is contained in:
cheatfate 2019-03-06 13:23:12 +02:00
parent 6321689c3c
commit dc2d0538ff
No known key found for this signature in database
GPG Key ID: 46ADD633A7201F95
5 changed files with 233 additions and 63 deletions

View File

@ -8,11 +8,13 @@
## those terms.
## This module implements Public Key and Private Key interface for libp2p.
import rsa, ecnist
import ed25519/ed25519
import ../protobuf/minprotobuf
import rsa, ecnist, ed25519/ed25519
import ../protobuf/minprotobuf, ../vbuffer
import nimcrypto/[rijndael, blowfish, sha, sha2, hash, hmac, utils]
# This is workaround for Nim's `import` bug
export rijndael, blowfish, sha, sha2, hash, hmac, utils
type
PKScheme* = enum
RSA = 0,
@ -69,7 +71,11 @@ type
macsize*: int
data*: seq[byte]
Signature* = object
data*: seq[byte]
P2pKeyError* = object of Exception
P2pSigError* = object of Exception
const
SupportedSchemes* = {RSA, Ed25519, ECDSA}
@ -192,6 +198,14 @@ proc toBytes*(key: PublicKey, data: var openarray[byte]): int =
if len(data) >= result:
copyMem(addr data[0], addr msg.buffer[0], len(msg.buffer))
proc toBytes*(sig: Signature, data: var openarray[byte]): int =
## Serialize signature ``sig`` and store it to ``data``.
##
## Returns number of bytes (octets) needed to store signature ``sig``.
result = len(sig.data)
if len(data) >= result:
copyMem(addr data[0], unsafeAddr sig.data[0], len(sig.data))
proc getBytes*(key: PrivateKey): seq[byte] =
## Return private key ``key`` in binary form (using libp2p's protobuf
## serialization).
@ -210,6 +224,10 @@ proc getBytes*(key: PublicKey): seq[byte] =
msg.finish()
result = msg.buffer
proc getBytes*(sig: Signature): seq[byte] =
## Return signature ``sig`` in binary form.
result = sig.data
proc init*(key: var PrivateKey, data: openarray[byte]): bool =
## Initialize private key ``key`` from libp2p's protobuf serialized raw
## binary form.
@ -264,6 +282,14 @@ proc init*(key: var PublicKey, data: openarray[byte]): bool =
key = nkey
result = true
proc init*(sig: var Signature, data: openarray[byte]): bool =
## Initialize signature ``sig`` from raw binary form.
##
## Returns ``true`` on success.
if len(data) > 0:
sig.data = @data
result = true
proc init*(key: var PrivateKey, data: string): bool =
## Initialize private key ``key`` from libp2p's protobuf serialized
## hexadecimal string representation.
@ -278,6 +304,13 @@ proc init*(key: var PublicKey, data: string): bool =
## Returns ``true`` on success.
result = key.init(fromHex(data))
proc init*(sig: var Signature, data: string): bool =
## Initialize signature ``sig`` from serialized hexadecimal string
## representation.
##
## Returns ``true`` on success.
result = sig.init(fromHex(data))
proc init*(t: typedesc[PrivateKey], data: openarray[byte]): PrivateKey =
## Create new private key from libp2p's protobuf serialized binary form.
if not result.init(data):
@ -288,6 +321,11 @@ proc init*(t: typedesc[PublicKey], data: openarray[byte]): PublicKey =
if not result.init(data):
raise newException(P2pKeyError, "Incorrect binary form")
proc init*(t: typedesc[Signature], data: openarray[byte]): Signature =
## Create new public key from libp2p's protobuf serialized binary form.
if not result.init(data):
raise newException(P2pSigError, "Incorrect binary form")
proc init*(t: typedesc[PrivateKey], data: string): PrivateKey =
## Create new private key from libp2p's protobuf serialized hexadecimal string
## form.
@ -298,6 +336,10 @@ proc init*(t: typedesc[PublicKey], data: string): PublicKey =
## form.
result = t.init(fromHex(data))
proc init*(t: typedesc[Signature], data: string): Signature =
## Create new signature from serialized hexadecimal string form.
result = t.init(fromHex(data))
proc `==`*(key1, key2: PublicKey): bool =
## Return ``true`` if two public keys ``key1`` and ``key2`` of the same
## scheme and equal.
@ -346,34 +388,38 @@ proc `$`*(key: PublicKey): string =
result.add($(key.eckey))
result.add(")")
proc sign*(key: PrivateKey, data: openarray[byte]): seq[byte] =
proc `$`*(sig: Signature): string =
## Get string representation of signature ``sig``.
result = toHex(sig.data)
proc sign*(key: PrivateKey, data: openarray[byte]): Signature =
## Sign message ``data`` using private key ``key`` and return generated
## signature in raw binary form.
if key.scheme == RSA:
var sig = key.rsakey.sign(data)
result = sig.getBytes()
result.data = sig.getBytes()
elif key.scheme == Ed25519:
var sig = key.edkey.sign(data)
result = sig.getBytes()
result.data = sig.getBytes()
elif key.scheme == ECDSA:
var sig = key.eckey.sign(data)
result = sig.getBytes()
result.data = sig.getBytes()
proc verify*(sig: openarray[byte], message: openarray[byte],
proc verify*(sig: Signature, message: openarray[byte],
key: PublicKey): bool =
## Verify signature ``sig`` using message ``message`` and public key ``key``.
## Return ``true`` if message signature is valid.
if key.scheme == RSA:
var signature: RsaSignature
if signature.init(sig) == Asn1Status.Success:
if signature.init(sig.data) == Asn1Status.Success:
result = signature.verify(message, key.rsakey)
elif key.scheme == Ed25519:
var signature: EdSignature
if signature.init(sig):
if signature.init(sig.data):
result = signature.verify(message, key.edkey)
elif key.scheme == ECDSA:
var signature: EcSignature
if signature.init(sig) == Asn1Status.Success:
if signature.init(sig.data) == Asn1Status.Success:
result = signature.verify(message, key.eckey)
template makeSecret(buffer, hmactype, secret, seed) =
@ -485,3 +531,62 @@ proc makeSecret*(remoteEPublic: PublicKey, localEPrivate: PrivateKey,
if remoteEPublic.scheme == ECDSA:
if localEPrivate.scheme == remoteEPublic.scheme:
result = toSecret(remoteEPublic.eckey, localEPrivate.eckey, data)
## Serialization/Deserialization helpers
proc write*(vb: var VBuffer, pubkey: PublicKey) {.inline.} =
## Write PublicKey value ``pubkey`` to buffer ``vb``.
vb.writeSeq(pubkey.getBytes())
proc write*(vb: var VBuffer, seckey: PrivateKey) {.inline.} =
## Write PrivateKey value ``seckey`` to buffer ``vb``.
vb.writeSeq(seckey.getBytes())
proc write*(vb: var VBuffer, sig: PrivateKey) {.inline.} =
## Write Signature value ``sig`` to buffer ``vb``.
vb.writeSeq(sig.getBytes())
proc initProtoField*(index: int, pubkey: PublicKey): ProtoField =
## Initialize ProtoField with PublicKey ``pubkey``.
result = initProtoField(index, pubkey.getBytes())
proc initProtoField*(index: int, seckey: PrivateKey): ProtoField =
## Initialize ProtoField with PrivateKey ``seckey``.
result = initProtoField(index, seckey.getBytes())
proc initProtoField*(index: int, sig: Signature): ProtoField =
## Initialize ProtoField with Signature ``sig``.
result = initProtoField(index, sig.getBytes())
proc getValue*(data: var ProtoBuffer, field: int, value: var PublicKey): int =
## Read ``PublicKey`` from ProtoBuf's message and validate it.
var buf: seq[byte]
var key: PublicKey
result = getLengthValue(data, field, buf)
if result > 0:
if not key.init(buf):
result = -1
else:
value = key
proc getValue*(data: var ProtoBuffer, field: int, value: var PrivateKey): int =
## Read ``PrivateKey`` from ProtoBuf's message and validate it.
var buf: seq[byte]
var key: PrivateKey
result = getLengthValue(data, field, buf)
if result > 0:
if not key.init(buf):
result = -1
else:
value = key
proc getValue*(data: var ProtoBuffer, field: int, value: var Signature): int =
## Read ``Signature`` from ProtoBuf's message and validate it.
var buf: seq[byte]
var sig: Signature
result = getLengthValue(data, field, buf)
if result > 0:
if not sig.init(buf):
result = -1
else:
value = sig

View File

@ -14,7 +14,33 @@ import constants
import nimcrypto/[hash, sha2, sysrand, utils]
# This workaround needed because of some bugs in Nim Static[T].
export sha2
export hash, sha2
const
EdPrivateKeySize* = 64
## Size in octets (bytes) of serialized ED25519 private key.
EdPublicKeySize* = 32
## Size in octets (bytes) of serialized ED25519 public key.
EdSignatureSize* = 64
## Size in octets (bytes) of serialized ED25519 signature.
type
EdPrivateKey* = object
data*: array[EdPrivateKeySize, byte]
EdPublicKey* = object
data*: array[EdPublicKeySize, byte]
EdSignature* = object
data*: array[EdSignatureSize, byte]
EdKeyPair* = object
seckey*: EdPrivateKey
pubkey*: EdPublicKey
EdError* = object of Exception
EdRngError* = object of EdError
EdIncorrectError* = object of EdError
proc `-`(x: uint32): uint32 {.inline.} =
result = (0xFFFF_FFFF'u32 - x) + 1'u32
@ -1612,32 +1638,6 @@ proc checkScalar*(scalar: openarray[byte]): uint32 =
c = -1
result = NEQ(z, 0'u32) and LT0(c)
const
EdPrivateKeySize* = 64
## Size in octets (bytes) of serialized ED25519 private key.
EdPublicKeySize* = 32
## Size in octets (bytes) of serialized ED25519 public key.
EdSignatureSize* = 64
## Size in octets (bytes) of serialized ED25519 signature.
type
EdPrivateKey* = object
data*: array[EdPrivateKeySize, byte]
EdPublicKey* = object
data*: array[EdPublicKeySize, byte]
EdSignature* = object
data*: array[EdSignatureSize, byte]
EdKeyPair* = object
seckey*: EdPrivateKey
pubkey*: EdPublicKey
EdError* = object of Exception
EdRngError* = object of EdError
EdIncorrectError* = object of EdError
proc random*(t: typedesc[EdPrivateKey]): EdPrivateKey =
## Generate new random ED25519 private key using OS specific CSPRNG.
var
@ -1645,11 +1645,11 @@ proc random*(t: typedesc[EdPrivateKey]): EdPrivateKey =
pk: array[EdPublicKeySize, byte]
if randomBytes(result.data.toOpenArray(0, 31)) != 32:
raise newException(EdRngError, "Could not generate random data")
var hash = sha512.digest(result.data.toOpenArray(0, 31))
hash.data[0] = hash.data[0] and 0xF8'u8
hash.data[31] = hash.data[31] and 0x3F'u8
hash.data[31] = hash.data[31] or 0x40'u8
geScalarMultBase(point, hash.data)
var hh = sha512.digest(result.data.toOpenArray(0, 31))
hh.data[0] = hh.data[0] and 0xF8'u8
hh.data[31] = hh.data[31] and 0x3F'u8
hh.data[31] = hh.data[31] or 0x40'u8
geScalarMultBase(point, hh.data)
geP3ToBytes(pk, point)
copyMem(addr result.data[32], addr pk[0], 32)
@ -1659,11 +1659,11 @@ proc random*(t: typedesc[EdKeyPair]): EdKeyPair =
var point: GeP3
if randomBytes(result.seckey.data.toOpenArray(0, 31)) != 32:
raise newException(EdRngError, "Could not generate random data")
var hash = sha512.digest(result.seckey.data.toOpenArray(0, 31))
hash.data[0] = hash.data[0] and 0xF8'u8
hash.data[31] = hash.data[31] and 0x3F'u8
hash.data[31] = hash.data[31] or 0x40'u8
geScalarMultBase(point, hash.data)
var hh = sha512.digest(result.seckey.data.toOpenArray(0, 31))
hh.data[0] = hh.data[0] and 0xF8'u8
hh.data[31] = hh.data[31] and 0x3F'u8
hh.data[31] = hh.data[31] or 0x40'u8
geScalarMultBase(point, hh.data)
geP3ToBytes(result.pubkey.data, point)
copyMem(addr result.seckey.data[32], addr result.pubkey.data[0], 32)

View File

@ -12,8 +12,9 @@ import os, osproc, strutils, tables, streams, strtabs
import chronos
import ../varint, ../multiaddress, ../multicodec, ../base58, ../cid, ../peer
import ../wire, ../multihash, ../protobuf/minprotobuf
import ../crypto/crypto
export peer, multiaddress, multicodec, multihash, cid
export peer, multiaddress, multicodec, multihash, cid, crypto
when not defined(windows):
import posix
@ -78,9 +79,7 @@ type
VALUE = 1,
END = 2
# PeerID* = seq[byte]
MultiProtocol* = string
LibP2PPublicKey* = seq[byte]
DHTValue* = seq[byte]
P2PStreamFlags* {.pure.} = enum
@ -137,8 +136,8 @@ type
data*: seq[byte]
seqno*: seq[byte]
topics*: seq[string]
signature*: seq[byte]
key*: seq[byte]
signature*: Signature
key*: PublicKey
P2PStreamCallback* = proc(api: DaemonAPI,
stream: P2PStream): Future[void] {.gcsafe.}
@ -662,7 +661,7 @@ proc newDaemonApi*(flags: set[P2PDaemonFlags] = {},
raise newException(DaemonLocalError, "Could not find daemon executable!")
# Starting daemon process
echo "Starting ", cmd, " ", args.join(" ")
# echo "Starting ", cmd, " ", args.join(" ")
api.process = startProcess(cmd, "", args, env, {poStdErrToStdOut})
# Waiting until daemon will not be bound to control socket.
while true:
@ -928,6 +927,10 @@ proc dhtGetSingleValue(pb: var ProtoBuffer): seq[byte] =
if pb.getLengthValue(3, result) == -1:
raise newException(DaemonLocalError, "Missing field `value`!")
proc dhtGetSinglePublicKey(pb: var ProtoBuffer): PublicKey =
if pb.getValue(3, result) == -1:
raise newException(DaemonLocalError, "Missing field `value`!")
proc dhtGetSinglePeerID(pb: var ProtoBuffer): PeerID =
if pb.getValue(3, result) == -1:
raise newException(DaemonLocalError, "Missing field `value`!")
@ -975,7 +978,7 @@ proc dhtFindPeer*(api: DaemonAPI, peer: PeerID,
await api.closeConnection(transp)
proc dhtGetPublicKey*(api: DaemonAPI, peer: PeerID,
timeout = 0): Future[LibP2PPublicKey] {.async.} =
timeout = 0): Future[PublicKey] {.async.} =
## Get peer's public key from peer with id ``peer``.
##
## You can specify timeout for DHT request with ``timeout`` value. ``0`` value
@ -985,7 +988,7 @@ proc dhtGetPublicKey*(api: DaemonAPI, peer: PeerID,
var pb = await transp.transactMessage(requestDHTGetPublicKey(peer, timeout))
withMessage(pb) do:
pb.enterDhtMessage(DHTResponseType.VALUE)
result = pb.dhtGetSingleValue()
result = pb.dhtGetSinglePublicKey()
finally:
await api.closeConnection(transp)
@ -1178,8 +1181,6 @@ proc pubsubPublish*(api: DaemonAPI, topic: string,
proc getPubsubMessage*(pb: var ProtoBuffer): PubSubMessage =
result.data = newSeq[byte]()
result.seqno = newSeq[byte]()
result.signature = newSeq[byte]()
result.key = newSeq[byte]()
discard pb.getValue(1, result.peer)
discard pb.getBytes(2, result.data)
discard pb.getBytes(3, result.seqno)
@ -1193,8 +1194,8 @@ proc getPubsubMessage*(pb: var ProtoBuffer): PubSubMessage =
result.topics = newSeq[string]()
result.topics.add(stritem)
item.setLen(0)
discard pb.getBytes(5, result.signature)
discard pb.getBytes(6, result.key)
discard pb.getValue(5, result.signature)
discard pb.getValue(6, result.key)
proc pubsubLoop(api: DaemonAPI, ticket: PubsubTicket) {.async.} =
while true:

View File

@ -24,6 +24,9 @@ import tables
import nimcrypto/[sha, sha2, keccak, blake2, hash, utils]
import varint, vbuffer, base58, multicodec, multibase
# This is workaround for Nim `import` bug.
export sha, sha2, keccak, blake2, hash, utils
const
MaxHashSize* = 128

View File

@ -342,9 +342,6 @@ const
"FA5CB0689A1DFDBAE8618BC079D70E318377B0DA"
]
proc cmp(a, b: openarray[byte]): bool =
result = (@a == @b)
@ -390,6 +387,70 @@ suite "Key interface test suite":
toHex(checkseckey) == stripSpaces(PrivateKeys[i])
toHex(checkpubkey) == stripSpaces(PublicKeys[i])
test "Generate/Sign/Serialize/Deserialize/Verify test":
var msg = "message to sign"
var bmsg = cast[seq[byte]](msg)
for i in 0..<5:
var seckey = PrivateKey.random(ECDSA)
var pubkey = seckey.getKey()
var pair = KeyPair.random(ECDSA)
var sig1 = pair.seckey.sign(bmsg)
var sig2 = seckey.sign(bmsg)
var sersig1 = sig1.getBytes()
var sersig2 = sig2.getBytes()
var serpub1 = pair.pubkey.getBytes()
var serpub2 = pubkey.getBytes()
var recsig1 = Signature.init(sersig1)
var recsig2 = Signature.init(sersig2)
var recpub1 = PublicKey.init(serpub1)
var recpub2 = PublicKey.init(serpub2)
check:
sig1.verify(bmsg, pair.pubkey) == true
recsig1.verify(bmsg, recpub1) == true
sig2.verify(bmsg, pubkey) == true
recsig2.verify(bmsg, recpub2) == true
for i in 0..<5:
var seckey = PrivateKey.random(Ed25519)
var pubkey = seckey.getKey()
var pair = KeyPair.random(Ed25519)
var sig1 = pair.seckey.sign(bmsg)
var sig2 = seckey.sign(bmsg)
var sersig1 = sig1.getBytes()
var sersig2 = sig2.getBytes()
var serpub1 = pair.pubkey.getBytes()
var serpub2 = pubkey.getBytes()
var recsig1 = Signature.init(sersig1)
var recsig2 = Signature.init(sersig2)
var recpub1 = PublicKey.init(serpub1)
var recpub2 = PublicKey.init(serpub2)
check:
sig1.verify(bmsg, pair.pubkey) == true
recsig1.verify(bmsg, recpub1) == true
sig2.verify(bmsg, pubkey) == true
recsig2.verify(bmsg, recpub2) == true
for i in 0..<5:
var seckey = PrivateKey.random(RSA, 512)
var pubkey = seckey.getKey()
var pair = KeyPair.random(RSA, 512)
var sig1 = pair.seckey.sign(bmsg)
var sig2 = seckey.sign(bmsg)
var sersig1 = sig1.getBytes()
var sersig2 = sig2.getBytes()
var serpub1 = pair.pubkey.getBytes()
var serpub2 = pubkey.getBytes()
var recsig1 = Signature.init(sersig1)
var recsig2 = Signature.init(sersig2)
var recpub1 = PublicKey.init(serpub1)
var recpub2 = PublicKey.init(serpub2)
check:
sig1.verify(bmsg, pair.pubkey) == true
recsig1.verify(bmsg, recpub1) == true
sig2.verify(bmsg, pubkey) == true
recsig2.verify(bmsg, recpub2) == true
test "Go key stretch function AES128-SHA256 test vectors":
check testStretcher(0, 4, Aes128, Sha256) == true
test "Go key stretch function AES256-SHA512 test vectors":