2020-02-17 16:44:56 +00:00
|
|
|
import
|
2020-04-24 13:40:30 +00:00
|
|
|
std/[tables, options], nimcrypto, stint, chronicles, stew/results,
|
2020-02-17 16:44:56 +00:00
|
|
|
types, node, enr, hkdf, ../enode, eth/[rlp, keys]
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-06 16:24:15 +00:00
|
|
|
export keys
|
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
2019-12-16 19:38:45 +00:00
|
|
|
const
|
|
|
|
idNoncePrefix = "discovery-id-nonce"
|
|
|
|
keyAgreementPrefix = "discovery v5 key agreement"
|
2020-02-17 17:04:29 +00:00
|
|
|
authSchemeName* = "gcm"
|
2020-02-27 12:45:12 +00:00
|
|
|
gcmNonceSize* = 12
|
2020-03-10 15:01:04 +00:00
|
|
|
gcmTagSize* = 16
|
2020-02-27 12:45:12 +00:00
|
|
|
tagSize* = 32 ## size of the tag where each message (except whoareyou) starts
|
|
|
|
## with
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
type
|
2020-02-27 21:36:42 +00:00
|
|
|
PacketTag* = array[tagSize, byte]
|
2020-02-27 12:45:12 +00:00
|
|
|
|
2019-12-16 19:38:45 +00:00
|
|
|
AuthResponse = object
|
|
|
|
version: int
|
|
|
|
signature: array[64, byte]
|
|
|
|
record: Record
|
|
|
|
|
|
|
|
Codec* = object
|
|
|
|
localNode*: Node
|
|
|
|
privKey*: PrivateKey
|
|
|
|
db*: Database
|
2020-02-27 21:36:42 +00:00
|
|
|
handshakes*: Table[HandShakeKey, Whoareyou]
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
HandshakeSecrets = object
|
2020-02-27 12:45:12 +00:00
|
|
|
writeKey: AesKey
|
|
|
|
readKey: AesKey
|
|
|
|
authRespKey: AesKey
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-02-17 17:04:29 +00:00
|
|
|
AuthHeader* = object
|
2020-02-27 12:45:12 +00:00
|
|
|
auth*: AuthTag
|
|
|
|
idNonce*: IdNonce
|
2020-02-17 17:04:29 +00:00
|
|
|
scheme*: string
|
|
|
|
ephemeralKey*: array[64, byte]
|
|
|
|
response*: seq[byte]
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
DecodeError* = enum
|
2020-02-25 13:49:31 +00:00
|
|
|
HandshakeError,
|
2020-03-10 15:01:04 +00:00
|
|
|
PacketError,
|
2020-04-24 13:40:30 +00:00
|
|
|
DecryptError,
|
2020-04-27 12:13:00 +00:00
|
|
|
UnsupportedMessage
|
2020-04-24 13:40:30 +00:00
|
|
|
|
|
|
|
DecodeResult*[T] = Result[T, DecodeError]
|
|
|
|
EncodeResult*[T] = Result[T, cstring]
|
2020-02-25 13:49:31 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc mapErrTo[T, E](r: Result[T, E], v: static DecodeError):
|
|
|
|
DecodeResult[T] {.raises:[].} =
|
|
|
|
r.mapErr(proc (e: E): DecodeError = v)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc idNonceHash(nonce, ephkey: openarray[byte]): MDigest[256] {.raises:[].} =
|
2019-12-16 19:38:45 +00:00
|
|
|
var ctx: sha256
|
|
|
|
ctx.init()
|
|
|
|
ctx.update(idNoncePrefix)
|
|
|
|
ctx.update(nonce)
|
|
|
|
ctx.update(ephkey)
|
2020-04-04 16:44:01 +00:00
|
|
|
ctx.finish()
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc signIDNonce*(privKey: PrivateKey, idNonce, ephKey: openarray[byte]):
|
|
|
|
Result[SignatureNR, cstring] =
|
|
|
|
signNR(privKey, idNonceHash(idNonce, ephKey))
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-02-18 16:16:46 +00:00
|
|
|
proc deriveKeys(n1, n2: NodeID, priv: PrivateKey, pub: PublicKey,
|
2020-04-24 13:40:30 +00:00
|
|
|
idNonce: openarray[byte]): Result[HandshakeSecrets, cstring] =
|
|
|
|
let eph = ? ecdhRawFull(priv, pub)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
var info = newSeqOfCap[byte](idNoncePrefix.len + 32 * 2)
|
|
|
|
for i, c in keyAgreementPrefix: info.add(byte(c))
|
|
|
|
info.add(n1.toByteArrayBE())
|
|
|
|
info.add(n2.toByteArrayBE())
|
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
var secrets: HandshakeSecrets
|
|
|
|
static: assert(sizeof(secrets) == aesKeySize * 3)
|
|
|
|
var res = cast[ptr UncheckedArray[byte]](addr secrets)
|
|
|
|
hkdf(sha256, eph.data, idNonce, info, toOpenArray(res, 0, sizeof(secrets) - 1))
|
|
|
|
ok(secrets)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc encryptGCM*(key, nonce, pt, authData: openarray[byte]):
|
|
|
|
seq[byte] {.raises:[].} =
|
2019-12-16 19:38:45 +00:00
|
|
|
var ectx: GCM[aes128]
|
|
|
|
ectx.init(key, nonce, authData)
|
|
|
|
result = newSeq[byte](pt.len + gcmTagSize)
|
|
|
|
ectx.encrypt(pt, result)
|
|
|
|
ectx.getTag(result.toOpenArray(pt.len, result.high))
|
|
|
|
ectx.clear()
|
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
proc encodeAuthHeader(c: Codec,
|
|
|
|
toId: NodeID,
|
|
|
|
nonce: array[gcmNonceSize, byte],
|
|
|
|
handshakeSecrets: var HandshakeSecrets,
|
|
|
|
challenge: Whoareyou):
|
|
|
|
EncodeResult[seq[byte]] =
|
2019-12-16 19:38:45 +00:00
|
|
|
var resp = AuthResponse(version: 5)
|
|
|
|
let ln = c.localNode
|
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
# TODO: What goes over the wire now in case of no updated ENR?
|
2020-02-11 16:25:31 +00:00
|
|
|
if challenge.recordSeq < ln.record.seqNum:
|
2019-12-16 19:38:45 +00:00
|
|
|
resp.record = ln.record
|
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
let ephKeys = ? KeyPair.random()
|
|
|
|
let signature = ? signIDNonce(c.privKey, challenge.idNonce,
|
|
|
|
ephKeys.pubkey.toRaw)
|
|
|
|
resp.signature = signature.toRaw
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
handshakeSecrets = ? deriveKeys(ln.id, toId, ephKeys.seckey, challenge.pubKey,
|
|
|
|
challenge.idNonce)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
let respRlp = rlp.encode(resp)
|
|
|
|
|
|
|
|
var zeroNonce: array[gcmNonceSize, byte]
|
2020-02-18 16:16:46 +00:00
|
|
|
let respEnc = encryptGCM(handshakeSecrets.authRespKey, zeroNonce, respRLP, [])
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-02-18 16:16:46 +00:00
|
|
|
let header = AuthHeader(auth: nonce, idNonce: challenge.idNonce,
|
2020-04-24 13:40:30 +00:00
|
|
|
scheme: authSchemeName, ephemeralKey: ephKeys.pubkey.toRaw,
|
|
|
|
response: respEnc)
|
|
|
|
ok(rlp.encode(header))
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc `xor`[N: static[int], T](a, b: array[N, T]): array[N, T] {.raises:[].} =
|
2019-12-16 19:38:45 +00:00
|
|
|
for i in 0 .. a.high:
|
|
|
|
result[i] = a[i] xor b[i]
|
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc packetTag(destNode, srcNode: NodeID): PacketTag {.raises:[].} =
|
|
|
|
let
|
|
|
|
destId = destNode.toByteArrayBE()
|
|
|
|
srcId = srcNode.toByteArrayBE()
|
|
|
|
destidHash = sha256.digest(destId)
|
2019-12-16 19:38:45 +00:00
|
|
|
result = srcId xor destidHash.data
|
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
proc encodePacket*(c: Codec,
|
2020-03-18 15:24:23 +00:00
|
|
|
toId: NodeID,
|
|
|
|
toAddr: Address,
|
2020-04-27 12:13:00 +00:00
|
|
|
message: seq[byte],
|
2020-02-27 12:45:12 +00:00
|
|
|
challenge: Whoareyou):
|
2020-04-24 13:40:30 +00:00
|
|
|
EncodeResult[(seq[byte], array[gcmNonceSize, byte])] =
|
2019-12-16 19:38:45 +00:00
|
|
|
var nonce: array[gcmNonceSize, byte]
|
2020-04-24 13:40:30 +00:00
|
|
|
if randomBytes(nonce) != nonce.len:
|
|
|
|
return err("Could not randomize bytes")
|
|
|
|
|
2019-12-16 19:38:45 +00:00
|
|
|
var headEnc: seq[byte]
|
|
|
|
|
2020-02-27 12:45:12 +00:00
|
|
|
var writeKey: AesKey
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
if challenge.isNil:
|
|
|
|
headEnc = rlp.encode(nonce)
|
2020-02-27 12:45:12 +00:00
|
|
|
var readKey: AesKey
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
# We might not have the node's keys if the handshake hasn't been performed
|
|
|
|
# yet. That's fine, we will be responded with whoareyou.
|
2020-03-18 15:24:23 +00:00
|
|
|
discard c.db.loadKeys(toId, toAddr, readKey, writeKey)
|
2019-12-16 19:38:45 +00:00
|
|
|
else:
|
|
|
|
var sec: HandshakeSecrets
|
2020-04-27 12:13:00 +00:00
|
|
|
headEnc = ? c.encodeAuthHeader(toId, nonce, sec, challenge)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
writeKey = sec.writeKey
|
2020-02-17 22:47:13 +00:00
|
|
|
# TODO: is it safe to ignore the error here?
|
2020-03-18 15:24:23 +00:00
|
|
|
discard c.db.storeKeys(toId, toAddr, sec.readKey, sec.writeKey)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-03-18 15:24:23 +00:00
|
|
|
let tag = packetTag(toId, c.localNode.id)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
var packet = newSeqOfCap[byte](tag.len + headEnc.len)
|
|
|
|
packet.add(tag)
|
|
|
|
packet.add(headEnc)
|
|
|
|
packet.add(encryptGCM(writeKey, nonce, message, tag))
|
|
|
|
ok((packet, nonce))
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-03-10 15:01:04 +00:00
|
|
|
proc decryptGCM*(key: AesKey, nonce, ct, authData: openarray[byte]):
|
2020-04-24 13:40:30 +00:00
|
|
|
Option[seq[byte]] {.raises:[].} =
|
2020-03-10 15:01:04 +00:00
|
|
|
if ct.len <= gcmTagSize:
|
|
|
|
debug "cipher is missing tag", len = ct.len
|
|
|
|
return
|
|
|
|
|
2019-12-16 19:38:45 +00:00
|
|
|
var dctx: GCM[aes128]
|
|
|
|
dctx.init(key, nonce, authData)
|
2020-03-10 15:01:04 +00:00
|
|
|
var res = newSeq[byte](ct.len - gcmTagSize)
|
2019-12-16 19:38:45 +00:00
|
|
|
var tag: array[gcmTagSize, byte]
|
2020-03-10 15:01:04 +00:00
|
|
|
dctx.decrypt(ct.toOpenArray(0, ct.high - gcmTagSize), res)
|
2019-12-16 19:38:45 +00:00
|
|
|
dctx.getTag(tag)
|
|
|
|
dctx.clear()
|
|
|
|
|
2020-03-10 15:01:04 +00:00
|
|
|
if tag != ct.toOpenArray(ct.len - gcmTagSize, ct.high):
|
|
|
|
return
|
|
|
|
|
|
|
|
return some(res)
|
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
proc decodeMessage(body: openarray[byte]):
|
|
|
|
DecodeResult[Message] {.raises:[Defect].} =
|
2020-04-24 14:52:41 +00:00
|
|
|
if body.len < 1:
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(PacketError)
|
2020-02-27 18:09:05 +00:00
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
if body[0] < MessageKind.low.byte or body[0] > MessageKind.high.byte:
|
2020-04-24 14:52:41 +00:00
|
|
|
return err(PacketError)
|
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
let kind = cast[MessageKind](body[0])
|
|
|
|
var message = Message(kind: kind)
|
2020-04-24 14:52:41 +00:00
|
|
|
var rlp = rlpFromBytes(body.toOpenArray(1, body.high))
|
2020-02-27 18:09:05 +00:00
|
|
|
if rlp.enterList:
|
2020-04-24 13:40:30 +00:00
|
|
|
try:
|
2020-04-27 12:13:00 +00:00
|
|
|
message.reqId = rlp.read(RequestId)
|
2020-04-24 13:40:30 +00:00
|
|
|
except RlpError:
|
|
|
|
return err(PacketError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc decode[T](rlp: var Rlp, v: var T)
|
|
|
|
{.inline, nimcall, raises:[RlpError, ValueError, Defect].} =
|
2019-12-16 19:38:45 +00:00
|
|
|
for k, v in v.fieldPairs:
|
|
|
|
v = rlp.read(typeof(v))
|
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
try:
|
|
|
|
case kind
|
|
|
|
of unused: return err(PacketError)
|
2020-04-27 12:13:00 +00:00
|
|
|
of ping: rlp.decode(message.ping)
|
|
|
|
of pong: rlp.decode(message.pong)
|
|
|
|
of findNode: rlp.decode(message.findNode)
|
|
|
|
of nodes: rlp.decode(message.nodes)
|
2020-04-24 13:40:30 +00:00
|
|
|
of regtopic, ticket, regconfirmation, topicquery:
|
|
|
|
# TODO: Implement support for topic advertisement
|
2020-04-27 12:13:00 +00:00
|
|
|
return err(UnsupportedMessage)
|
2020-04-24 13:40:30 +00:00
|
|
|
except RlpError, ValueError:
|
|
|
|
return err(PacketError)
|
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
ok(message)
|
2019-12-16 19:38:45 +00:00
|
|
|
else:
|
2020-04-24 13:40:30 +00:00
|
|
|
err(PacketError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-02-18 16:16:46 +00:00
|
|
|
proc decodeAuthResp(c: Codec, fromId: NodeId, head: AuthHeader,
|
2020-04-24 13:40:30 +00:00
|
|
|
challenge: Whoareyou, secrets: var HandshakeSecrets, newNode: var Node):
|
|
|
|
DecodeResult[void] {.raises:[Defect].} =
|
2019-12-16 19:38:45 +00:00
|
|
|
if head.scheme != authSchemeName:
|
2020-02-17 16:44:56 +00:00
|
|
|
warn "Unknown auth scheme"
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(HandshakeError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
let ephKey = ? PublicKey.fromRaw(head.ephemeralKey).mapErrTo(HandshakeError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
secrets = ? deriveKeys(fromId, c.localNode.id, c.privKey, ephKey,
|
|
|
|
challenge.idNonce).mapErrTo(HandshakeError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
var zeroNonce: array[gcmNonceSize, byte]
|
|
|
|
let respData = decryptGCM(secrets.authRespKey, zeroNonce, head.response, [])
|
2020-03-10 15:01:04 +00:00
|
|
|
if respData.isNone():
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(HandshakeError)
|
2020-03-10 15:01:04 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
var authResp: AuthResponse
|
|
|
|
try:
|
|
|
|
authResp = rlp.decode(respData.get(), AuthResponse)
|
|
|
|
except RlpError, ValueError:
|
|
|
|
return err(HandshakeError)
|
2020-03-10 21:28:11 +00:00
|
|
|
# TODO:
|
|
|
|
# 1. Should allow for not having an ENR included, solved for now by sending
|
|
|
|
# whoareyou with always recordSeq of 0
|
|
|
|
# 2. Should verify ENR and check for correct id in case an ENR is included
|
|
|
|
# 3. Should verify id nonce signature
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-03-13 16:48:03 +00:00
|
|
|
# More TODO:
|
|
|
|
# This will also not work if ENR does not contain an IP address or if the
|
|
|
|
# IP address is out of date and doesn't match current UDP end point
|
2020-04-24 13:40:30 +00:00
|
|
|
try:
|
|
|
|
newNode = newNode(authResp.record)
|
|
|
|
ok()
|
|
|
|
except KeyError, ValueError:
|
|
|
|
err(HandshakeError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
proc decodePacket*(c: var Codec,
|
2020-02-22 18:49:14 +00:00
|
|
|
fromId: NodeID,
|
|
|
|
fromAddr: Address,
|
2020-04-20 18:14:39 +00:00
|
|
|
input: openArray[byte],
|
2020-02-27 12:45:12 +00:00
|
|
|
authTag: var AuthTag,
|
2020-04-27 12:13:00 +00:00
|
|
|
newNode: var Node): DecodeResult[Message] =
|
2020-04-20 18:14:39 +00:00
|
|
|
var r = rlpFromBytes(input.toOpenArray(tagSize, input.high))
|
2019-12-16 19:38:45 +00:00
|
|
|
var auth: AuthHeader
|
2020-02-27 12:45:12 +00:00
|
|
|
|
|
|
|
var readKey: AesKey
|
2020-02-27 18:09:05 +00:00
|
|
|
logScope: sender = $fromAddr
|
|
|
|
|
2019-12-16 19:38:45 +00:00
|
|
|
if r.isList:
|
2020-02-17 16:44:56 +00:00
|
|
|
# Handshake - rlp list indicates auth-header
|
2020-04-24 13:40:30 +00:00
|
|
|
try:
|
|
|
|
auth = r.read(AuthHeader)
|
|
|
|
except RlpError:
|
|
|
|
return err(PacketError)
|
2019-12-16 19:38:45 +00:00
|
|
|
authTag = auth.auth
|
|
|
|
|
2020-02-27 21:36:42 +00:00
|
|
|
let key = HandShakeKey(nodeId: fromId, address: $fromAddr)
|
|
|
|
let challenge = c.handshakes.getOrDefault(key)
|
2019-12-16 19:38:45 +00:00
|
|
|
if challenge.isNil:
|
2020-02-22 18:49:14 +00:00
|
|
|
trace "Decoding failed (no challenge)"
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(HandshakeError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
if auth.idNonce != challenge.idNonce:
|
2020-02-22 18:49:14 +00:00
|
|
|
trace "Decoding failed (different nonce)"
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(HandshakeError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
var sec: HandshakeSecrets
|
2020-04-24 13:40:30 +00:00
|
|
|
if c.decodeAuthResp(fromId, auth, challenge, sec, newNode).isErr:
|
2020-02-22 18:49:14 +00:00
|
|
|
trace "Decoding failed (bad auth)"
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(HandshakeError)
|
|
|
|
|
2020-02-27 21:36:42 +00:00
|
|
|
c.handshakes.del(key)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-03-18 14:27:26 +00:00
|
|
|
# For an incoming handshake, we are not sure the address in the ENR is there
|
|
|
|
# and if it is the real external IP, so we use the one we know from the
|
|
|
|
# UDP packet.
|
|
|
|
updateEndpoint(newNode, fromAddr)
|
|
|
|
|
2019-12-16 19:38:45 +00:00
|
|
|
# Swap keys to match remote
|
|
|
|
swap(sec.readKey, sec.writeKey)
|
2020-02-17 22:47:13 +00:00
|
|
|
# TODO: is it safe to ignore the error here?
|
|
|
|
discard c.db.storeKeys(fromId, fromAddr, sec.readKey, sec.writeKey)
|
2019-12-16 19:38:45 +00:00
|
|
|
readKey = sec.readKey
|
|
|
|
else:
|
2020-02-17 16:44:56 +00:00
|
|
|
# Message packet or random packet - rlp bytes (size 12) indicates auth-tag
|
2020-04-24 13:40:30 +00:00
|
|
|
try:
|
|
|
|
authTag = r.read(AuthTag)
|
|
|
|
except RlpError:
|
|
|
|
return err(PacketError)
|
2019-12-16 19:38:45 +00:00
|
|
|
auth.auth = authTag
|
2020-02-27 21:36:42 +00:00
|
|
|
var writeKey: AesKey
|
2019-12-16 19:38:45 +00:00
|
|
|
if not c.db.loadKeys(fromId, fromAddr, readKey, writeKey):
|
2020-02-22 18:49:14 +00:00
|
|
|
trace "Decoding failed (no keys)"
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(DecryptError)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-02-27 12:45:12 +00:00
|
|
|
let headSize = tagSize + r.position
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
let message = decryptGCM(
|
2020-04-20 18:14:39 +00:00
|
|
|
readKey, auth.auth,
|
|
|
|
input.toOpenArray(headSize, input.high),
|
|
|
|
input.toOpenArray(0, tagSize - 1))
|
2020-04-27 12:13:00 +00:00
|
|
|
if message.isNone():
|
2020-03-10 15:01:04 +00:00
|
|
|
discard c.db.deleteKeys(fromId, fromAddr)
|
2020-04-24 13:40:30 +00:00
|
|
|
return err(DecryptError)
|
2020-03-10 15:01:04 +00:00
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
decodeMessage(message.get())
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc newRequestId*(): Result[RequestId, cstring] {.raises:[].} =
|
|
|
|
var id: RequestId
|
|
|
|
if randomBytes(addr id, sizeof(id)) != sizeof(id):
|
|
|
|
err("Could not randomize bytes")
|
|
|
|
else:
|
|
|
|
ok(id)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
2020-04-24 13:40:30 +00:00
|
|
|
proc numFields(T: typedesc): int {.raises:[].} =
|
2019-12-16 19:38:45 +00:00
|
|
|
for k, v in fieldPairs(default(T)): inc result
|
|
|
|
|
2020-04-27 12:13:00 +00:00
|
|
|
proc encodeMessage*[T: SomeMessage](p: T, reqId: RequestId):
|
2020-04-24 13:40:30 +00:00
|
|
|
seq[byte] {.raises:[].} =
|
2019-12-16 19:38:45 +00:00
|
|
|
result = newSeqOfCap[byte](64)
|
2020-04-27 12:13:00 +00:00
|
|
|
result.add(messageKind(T).ord)
|
2019-12-16 19:38:45 +00:00
|
|
|
|
|
|
|
const sz = numFields(T)
|
|
|
|
var writer = initRlpList(sz + 1)
|
|
|
|
writer.append(reqId)
|
|
|
|
for k, v in fieldPairs(p):
|
|
|
|
writer.append(v)
|
|
|
|
result.add(writer.finish())
|