diff --git a/chronosim/signed_envelope.nim b/chronosim/signed_envelope.nim new file mode 100644 index 0000000..e0ead53 --- /dev/null +++ b/chronosim/signed_envelope.nim @@ -0,0 +1,181 @@ +# Nim-Libp2p +# Copyright (c) 2023 Status Research & Development GmbH +# Licensed under either of +# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +# * MIT license ([LICENSE-MIT](LICENSE-MIT)) +# at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +## This module implements Signed Envelope. + +when (NimMajor, NimMinor) < (1, 4): + {.push raises: [Defect].} +else: + {.push raises: [].} + +import std/sugar +import pkg/stew/[results, byteutils] +import libp2p/multicodec, + libp2p/crypto/crypto, + libp2p/protobuf/minprotobuf, + libp2p/vbuffer + +export crypto + +type + EnvelopeError* = enum + EnvelopeInvalidProtobuf, + EnvelopeFieldMissing, + EnvelopeInvalidSignature, + EnvelopeWrongType + + Envelope* = object + publicKey*: PublicKey + domain*: string + payloadType*: seq[byte] + payload: seq[byte] +# signature*: Signature + +proc mapProtobufError(e: ProtoError): EnvelopeError = + case e: + of RequiredFieldMissing: + EnvelopeFieldMissing + else: + EnvelopeInvalidProtobuf + +proc getSignatureBuffer(e: Envelope): seq[byte] = + var buffer = initVBuffer() + + let domainBytes = e.domain.toBytes() + buffer.writeSeq(domainBytes) + buffer.writeSeq(e.payloadType) + buffer.writeSeq(e.payload) + + buffer.buffer + +proc decode*(T: typedesc[Envelope], + buf: seq[byte], + domain: string): Result[Envelope, EnvelopeError] = + + let pb = initProtoBuffer(buf) + var envelope = Envelope() + + envelope.domain = domain + ? pb.getRequiredField(1, envelope.publicKey).mapErr(mapProtobufError) + discard ? pb.getField(2, envelope.payloadType).mapErr(mapProtobufError) + ? pb.getRequiredField(3, envelope.payload).mapErr(mapProtobufError) +# ? pb.getRequiredField(5, envelope.signature).mapErr(mapProtobufError) + + # if envelope.signature.verify(envelope.getSignatureBuffer(), envelope.publicKey) == false: + # err(EnvelopeInvalidSignature) + # else: + # ok(envelope) + ok(envelope) + +proc init*(T: typedesc[Envelope], + privateKey: PrivateKey, + payloadType: seq[byte], + payload: seq[byte], + domain: string): Result[Envelope, CryptoError] = + var envelope = Envelope( + publicKey: ? privateKey.getPublicKey(), + domain: domain, + payloadType: payloadType, + payload: payload, + ) + +# envelope.signature = ? privateKey.sign(envelope.getSignatureBuffer()) + + ok(envelope) + +proc encode*(env: Envelope): Result[seq[byte], CryptoError] = + var pb = initProtoBuffer() + + try: + pb.write(1, env.publicKey) + pb.write(2, env.payloadType) + pb.write(3, env.payload) +# pb.write(5, env.signature) + except ResultError[CryptoError] as exc: + return err(exc.error) + + pb.finish() + ok(pb.buffer) + +proc payload*(env: Envelope): seq[byte] = + # Payload is readonly + env.payload + +proc getField*(pb: ProtoBuffer, field: int, + value: var Envelope, + domain: string): ProtoResult[bool] {. + inline.} = + var buffer: seq[byte] + let res = ? pb.getField(field, buffer) + if not(res): + ok(false) + else: + let env = Envelope.decode(buffer, domain) + if env.isOk(): + value = env.get() + ok(true) + else: + err(ProtoError.IncorrectBlob) + +proc write*(pb: var ProtoBuffer, field: int, env: Envelope): Result[void, CryptoError] = + let e = env.encode() + + if e.isErr(): + return err(e.error) + pb.write(field, e.get()) + ok() + +type + SignedPayload*[T] = object + # T needs to have .encode(), .decode(), .payloadType(), .domain() + envelope*: Envelope + data*: T + +proc init*[T](_: typedesc[SignedPayload[T]], + privateKey: PrivateKey, + data: T): Result[SignedPayload[T], CryptoError] = + mixin encode + + let envelope = ? Envelope.init(privateKey, + T.payloadType(), + data.encode(), + T.payloadDomain) + + ok(SignedPayload[T](data: data, envelope: envelope)) + +proc getField*[T](pb: ProtoBuffer, field: int, + value: var SignedPayload[T]): ProtoResult[bool] {. + inline.} = + if not ? getField(pb, field, value.envelope, T.payloadDomain): + ok(false) + else: + mixin decode + value.data = ? T.decode(value.envelope.payload).mapErr(x => ProtoError.IncorrectBlob) + ok(true) + +proc decode*[T]( + _: typedesc[SignedPayload[T]], + buffer: seq[byte] + ): Result[SignedPayload[T], EnvelopeError] = + + let + envelope = ? Envelope.decode(buffer, T.payloadDomain) + data = ? T.decode(envelope.payload).mapErr(x => EnvelopeInvalidProtobuf) + signedPayload = SignedPayload[T](envelope: envelope, data: data) + + if envelope.payloadType != T.payloadType: + return err(EnvelopeWrongType) + + when compiles(? signedPayload.checkValid()): + ? signedPayload.checkValid() + + ok(signedPayload) + +proc encode*[T](msg: SignedPayload[T]): Result[seq[byte], CryptoError] = + msg.envelope.encode() diff --git a/das.nims b/das.nims index d88c8ab..dd6e7bf 100644 --- a/das.nims +++ b/das.nims @@ -1,4 +1,5 @@ patchFile("chronos", "chronos", "chronosim/chronos") patchFile("chronos", "pkg", "chronosim/chronos") patchFile("nimcrypto", "rijndael", "chronosim/rijndael") +patchFile("libp2p", "signed_envelope", "chronosim/signed_envelope") --d:chronosFutureDuration