# 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 constant-time RSA PKCS#1.5 DSA.
##
## This module uses unmodified parts of code from
## BearSSL library
## Copyright(C) 2018 Thomas Pornin .
{.push raises: [].}
import bearssl/[rsa, rand, hash]
import minasn1
import stew/[results, ctops]
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
import nimcrypto/utils as ncrutils
export Asn1Error, results
const
DefaultPublicExponent* = 65537'u32
## Default value for RSA public exponent.
## https://golang.org/src/crypto/rsa/rsa.go#226
MinKeySize* = 2048
## Minimal allowed RSA key size in bits.
## https://github.com/libp2p/go-libp2p-core/blob/master/crypto/rsa_common.go#L13
DefaultKeySize* = 3072 ## Default RSA key size in bits.
RsaOidSha1* = [byte 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A]
## RSA PKCS#1.5 SHA-1 hash object identifier.
RsaOidSha224* = [byte 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04]
## RSA PKCS#1.5 SHA-224 hash object identifier.
RsaOidSha256* = [byte 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01]
## RSA PKCS#1.5 SHA-256 hash object identifier.
RsaOidSha384* = [byte 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02]
## RSA PKCS#1.5 SHA-384 hash object identifier.
RsaOidSha512* = [byte 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03]
## RSA PKCS#1.5 SHA-512 hash object identifier.
type
RsaPrivateKey* = ref object
buffer*: seq[byte]
seck*: rsa.RsaPrivateKey
pubk*: rsa.RsaPublicKey
pexp*: ptr byte
pexplen*: uint
RsaPublicKey* = ref object
buffer*: seq[byte]
key*: rsa.RsaPublicKey
RsaKeyPair* = RsaPrivateKey
RsaSignature* = ref object
buffer*: seq[byte]
RsaPKI* = RsaPrivateKey | RsaPublicKey | RsaSignature
RsaKP* = RsaPrivateKey | RsaKeyPair
RsaError* = enum
RsaGenError
RsaKeyIncorrectError
RsaSignatureError
RsaLowSecurityError
RsaResult*[T] = Result[T, RsaError]
template getStart(bs, os, ls: untyped): untyped =
let p = cast[uint](os)
let s = cast[uint](unsafeAddr bs[0])
var so = 0
if p >= s:
so = cast[int](p - s)
so
template getFinish(bs, os, ls: untyped): untyped =
let p = cast[uint](os)
let s = cast[uint](unsafeAddr bs[0])
var eo = -1
if p >= s:
let so = cast[int](p - s)
if so + int(ls) <= len(bs):
eo = so + int(ls) - 1
eo
template getArray*(bs, os, ls: untyped): untyped =
toOpenArray(bs, getStart(bs, os, ls), getFinish(bs, os, ls))
template trimZeroes(b: seq[byte], pt, ptlen: untyped) =
var length = ptlen
for i in 0 ..< length:
if pt[] != byte(0x00):
break
pt = cast[ptr byte](cast[uint](pt) + 1)
ptlen -= 1
proc random*[T: RsaKP](
t: typedesc[T],
rng: var HmacDrbgContext,
bits = DefaultKeySize,
pubexp = DefaultPublicExponent,
): RsaResult[T] =
## Generate new random RSA private key using BearSSL's HMAC-SHA256-DRBG
## algorithm.
##
## ``bits`` number of bits in RSA key, must be in
## range [2048, 4096] (default = 3072).
##
## ``pubexp`` is RSA public exponent, which must be prime (default = 3).
if bits < MinKeySize:
return err(RsaLowSecurityError)
let
sko = 0
pko = rsaKbufPrivSize(bits)
eko = pko + rsaKbufPubSize(bits)
length = eko + ((bits + 7) shr 3)
let res = new T
res.buffer = newSeq[byte](length)
var keygen = rsaKeygenGetDefault()
if keygen(
addr rng.vtable,
addr res.seck,
addr res.buffer[sko],
addr res.pubk,
addr res.buffer[pko],
cuint(bits),
pubexp,
) == 0:
return err(RsaGenError)
let
compute = rsaComputePrivexpGetDefault()
computed = compute(addr res.buffer[eko], addr res.seck, pubexp)
if computed == 0:
return err(RsaGenError)
res.pexp = addr res.buffer[eko]
res.pexplen = computed
trimZeroes(res.buffer, res.seck.p, res.seck.plen)
trimZeroes(res.buffer, res.seck.q, res.seck.qlen)
trimZeroes(res.buffer, res.seck.dp, res.seck.dplen)
trimZeroes(res.buffer, res.seck.dq, res.seck.dqlen)
trimZeroes(res.buffer, res.seck.iq, res.seck.iqlen)
trimZeroes(res.buffer, res.pubk.n, res.pubk.nlen)
trimZeroes(res.buffer, res.pubk.e, res.pubk.elen)
trimZeroes(res.buffer, res.pexp, res.pexplen)
ok(res)
proc copy*[T: RsaPKI](key: T): T =
## Create copy of RSA private key, public key or signature.
doAssert(not isNil(key))
when T is RsaPrivateKey:
if len(key.buffer) > 0:
let length =
key.seck.plen.uint + key.seck.qlen.uint + key.seck.dplen.uint +
key.seck.dqlen.uint + key.seck.iqlen.uint + key.pubk.nlen.uint +
key.pubk.elen.uint + key.pexplen.uint
result = new RsaPrivateKey
result.buffer = newSeq[byte](length)
let po: uint = 0
let qo = po + key.seck.plen
let dpo = qo + key.seck.qlen
let dqo = dpo + key.seck.dplen
let iqo = dqo + key.seck.dqlen
let no = iqo + key.seck.iqlen
let eo = no + key.pubk.nlen
let peo = eo + key.pubk.elen
copyMem(addr result.buffer[po], key.seck.p, key.seck.plen)
copyMem(addr result.buffer[qo], key.seck.q, key.seck.qlen)
copyMem(addr result.buffer[dpo], key.seck.dp, key.seck.dplen)
copyMem(addr result.buffer[dqo], key.seck.dq, key.seck.dqlen)
copyMem(addr result.buffer[iqo], key.seck.iq, key.seck.iqlen)
copyMem(addr result.buffer[no], key.pubk.n, key.pubk.nlen)
copyMem(addr result.buffer[eo], key.pubk.e, key.pubk.elen)
copyMem(addr result.buffer[peo], key.pexp, key.pexplen)
result.seck.p = addr result.buffer[po]
result.seck.q = addr result.buffer[qo]
result.seck.dp = addr result.buffer[dpo]
result.seck.dq = addr result.buffer[dqo]
result.seck.iq = addr result.buffer[iqo]
result.pubk.n = addr result.buffer[no]
result.pubk.e = addr result.buffer[eo]
result.pexp = addr result.buffer[peo]
result.seck.plen = key.seck.plen
result.seck.qlen = key.seck.qlen
result.seck.dplen = key.seck.dplen
result.seck.dqlen = key.seck.dqlen
result.seck.iqlen = key.seck.iqlen
result.pubk.nlen = key.pubk.nlen
result.pubk.elen = key.pubk.elen
result.pexplen = key.pexplen
result.seck.nBitlen = key.seck.nBitlen
elif T is RsaPublicKey:
if len(key.buffer) > 0:
let length = key.key.nlen + key.key.elen
result = new RsaPublicKey
result.buffer = newSeq[byte](length)
let no = 0
let eo = no + key.key.nlen
copyMem(addr result.buffer[no], key.key.n, key.key.nlen)
copyMem(addr result.buffer[eo], key.key.e, key.key.elen)
result.key.n = cast[ptr char](addr result.buffer[no])
result.key.e = cast[ptr char](addr result.buffer[eo])
result.key.nlen = key.key.nlen
result.key.elen = key.key.elen
elif T is RsaSignature:
if len(key.buffer) > 0:
result = new RsaSignature
result.buffer = key.buffer
proc getPublicKey*(key: RsaPrivateKey): RsaPublicKey =
## Get RSA public key from RSA private key.
doAssert(not isNil(key))
let length = key.pubk.nlen + key.pubk.elen
result = new RsaPublicKey
result.buffer = newSeq[byte](length)
result.key.n = addr result.buffer[0]
result.key.e = addr result.buffer[key.pubk.nlen]
copyMem(addr result.buffer[0], cast[pointer](key.pubk.n), key.pubk.nlen)
copyMem(addr result.buffer[key.pubk.nlen], cast[pointer](key.pubk.e), key.pubk.elen)
result.key.nlen = key.pubk.nlen
result.key.elen = key.pubk.elen
proc seckey*(pair: RsaKeyPair): RsaPrivateKey {.inline.} =
## Get RSA private key from pair ``pair``.
result = cast[RsaPrivateKey](pair).copy()
proc pubkey*(pair: RsaKeyPair): RsaPublicKey {.inline.} =
## Get RSA public key from pair ``pair``.
result = cast[RsaPrivateKey](pair).getPublicKey()
proc clear*[T: RsaPKI | RsaKeyPair](pki: var T) =
## Wipe and clear EC private key, public key or scalar object.
doAssert(not isNil(pki))
when T is RsaPrivateKey:
burnMem(pki.buffer)
pki.buffer.setLen(0)
pki.seckey.p = nil
pki.seckey.q = nil
pki.seckey.dp = nil
pki.seckey.dq = nil
pki.seckey.iq = nil
pki.seckey.plen = 0
pki.seckey.qlen = 0
pki.seckey.dplen = 0
pki.seckey.dqlen = 0
pki.seckey.iqlen = 0
pki.seckey.nBitlen = 0
pki.pubkey.n = nil
pki.pubkey.e = nil
pki.pubkey.nlen = 0
pki.pubkey.elen = 0
elif T is RsaPublicKey:
burnMem(pki.buffer)
pki.buffer.setLen(0)
pki.key.n = nil
pki.key.e = nil
pki.key.nlen = 0
pki.key.elen = 0
elif T is RsaSignature:
burnMem(pki.buffer)
pki.buffer.setLen(0)
proc toBytes*(key: RsaPrivateKey, data: var openArray[byte]): RsaResult[int] =
## Serialize RSA private key ``key`` to ASN.1 DER binary form and store it
## to ``data``.
##
## Procedure returns number of bytes (octets) needed to store RSA private key,
## or `0` if private key is is incorrect.
if isNil(key):
err(RsaKeyIncorrectError)
elif len(key.buffer) > 0:
var b = Asn1Buffer.init()
var p = Asn1Composite.init(Asn1Tag.Sequence)
p.write(0'u64)
p.write(Asn1Tag.Integer, getArray(key.buffer, key.pubk.n, key.pubk.nlen))
p.write(Asn1Tag.Integer, getArray(key.buffer, key.pubk.e, key.pubk.elen))
p.write(Asn1Tag.Integer, getArray(key.buffer, key.pexp, key.pexplen))
p.write(Asn1Tag.Integer, getArray(key.buffer, key.seck.p, key.seck.plen))
p.write(Asn1Tag.Integer, getArray(key.buffer, key.seck.q, key.seck.qlen))
p.write(Asn1Tag.Integer, getArray(key.buffer, key.seck.dp, key.seck.dplen))
p.write(Asn1Tag.Integer, getArray(key.buffer, key.seck.dq, key.seck.dqlen))
p.write(Asn1Tag.Integer, getArray(key.buffer, key.seck.iq, key.seck.iqlen))
p.finish()
b.write(p)
b.finish()
var blen = len(b)
if len(data) >= blen:
copyMem(addr data[0], addr b.buffer[0], blen)
ok(blen)
else:
err(RsaKeyIncorrectError)
proc toBytes*(key: RsaPublicKey, data: var openArray[byte]): RsaResult[int] =
## Serialize RSA public key ``key`` to ASN.1 DER binary form and store it
## to ``data``.
##
## Procedure returns number of bytes (octets) needed to store RSA public key,
## or `0` if public key is incorrect.
if isNil(key):
err(RsaKeyIncorrectError)
elif len(key.buffer) > 0:
var b = Asn1Buffer.init()
var p = Asn1Composite.init(Asn1Tag.Sequence)
var c0 = Asn1Composite.init(Asn1Tag.Sequence)
var c1 = Asn1Composite.init(Asn1Tag.BitString)
var c10 = Asn1Composite.init(Asn1Tag.Sequence)
c0.write(Asn1Tag.Oid, Asn1OidRsaEncryption)
c0.write(Asn1Tag.Null)
c0.finish()
c10.write(Asn1Tag.Integer, getArray(key.buffer, key.key.n, key.key.nlen))
c10.write(Asn1Tag.Integer, getArray(key.buffer, key.key.e, key.key.elen))
c10.finish()
c1.write(c10)
c1.finish()
p.write(c0)
p.write(c1)
p.finish()
b.write(p)
b.finish()
var blen = len(b)
if len(data) >= blen:
copyMem(addr data[0], addr b.buffer[0], blen)
ok(blen)
else:
err(RsaKeyIncorrectError)
proc toBytes*(sig: RsaSignature, data: var openArray[byte]): RsaResult[int] =
## Serialize RSA signature ``sig`` to raw binary form and store it
## to ``data``.
##
## Procedure returns number of bytes (octets) needed to store RSA public key,
## or `0` if public key is incorrect.
if isNil(sig):
err(RsaSignatureError)
else:
var slen = len(sig.buffer)
if len(data) >= slen:
copyMem(addr data[0], addr sig.buffer[0], slen)
ok(slen)
proc getBytes*(key: RsaPrivateKey): RsaResult[seq[byte]] =
## Serialize RSA private key ``key`` to ASN.1 DER binary form and
## return it.
if isNil(key):
return err(RsaKeyIncorrectError)
var res = newSeq[byte](4096)
let length = ?key.toBytes(res)
if length > 0:
res.setLen(length)
ok(res)
else:
err(RsaKeyIncorrectError)
proc getBytes*(key: RsaPublicKey): RsaResult[seq[byte]] =
## Serialize RSA public key ``key`` to ASN.1 DER binary form and
## return it.
if isNil(key):
return err(RsaKeyIncorrectError)
var res = newSeq[byte](4096)
let length = ?key.toBytes(res)
if length > 0:
res.setLen(length)
ok(res)
else:
err(RsaKeyIncorrectError)
proc getBytes*(sig: RsaSignature): RsaResult[seq[byte]] =
## Serialize RSA signature ``sig`` to raw binary form and return it.
if isNil(sig):
return err(RsaSignatureError)
var res = newSeq[byte](4096)
let length = ?sig.toBytes(res)
if length > 0:
res.setLen(length)
ok(res)
else:
err(RsaSignatureError)
proc init*(key: var RsaPrivateKey, data: openArray[byte]): Result[void, Asn1Error] =
## Initialize RSA private key ``key`` from ASN.1 DER binary representation
## ``data``.
##
## Procedure returns ``Asn1Status``.
var field, rawn, rawpube, rawprie, rawp, rawq, rawdp, rawdq, rawiq: Asn1Field
# Asn1Field is not trivial so avoid too much Result
var ab = Asn1Buffer.init(data)
field = ?ab.read()
if field.kind != Asn1Tag.Sequence:
return err(Asn1Error.Incorrect)
var ib = field.getBuffer()
field = ?ib.read()
if field.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
if field.vint != 0'u64:
return err(Asn1Error.Incorrect)
rawn = ?ib.read()
if rawn.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawpube = ?ib.read()
if rawpube.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawprie = ?ib.read()
if rawprie.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawp = ?ib.read()
if rawp.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawq = ?ib.read()
if rawq.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawdp = ?ib.read()
if rawdp.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawdq = ?ib.read()
if rawdq.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawiq = ?ib.read()
if rawiq.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
if len(rawn) >= (MinKeySize shr 3) and len(rawp) > 0 and len(rawq) > 0 and
len(rawdp) > 0 and len(rawdq) > 0 and len(rawiq) > 0:
key = new RsaPrivateKey
key.buffer = @data
key.pubk.n = addr key.buffer[rawn.offset]
key.pubk.e = addr key.buffer[rawpube.offset]
key.seck.p = addr key.buffer[rawp.offset]
key.seck.q = addr key.buffer[rawq.offset]
key.seck.dp = addr key.buffer[rawdp.offset]
key.seck.dq = addr key.buffer[rawdq.offset]
key.seck.iq = addr key.buffer[rawiq.offset]
key.pexp = addr key.buffer[rawprie.offset]
key.pubk.nlen = uint(len(rawn))
key.pubk.elen = uint(len(rawpube))
key.seck.plen = uint(len(rawp))
key.seck.qlen = uint(len(rawq))
key.seck.dplen = uint(len(rawdp))
key.seck.dqlen = uint(len(rawdq))
key.seck.iqlen = uint(len(rawiq))
key.pexplen = uint(len(rawprie))
key.seck.nBitlen = cast[uint32](len(rawn) shl 3)
ok()
else:
err(Asn1Error.Incorrect)
proc init*(key: var RsaPublicKey, data: openArray[byte]): Result[void, Asn1Error] =
## Initialize RSA public key ``key`` from ASN.1 DER binary representation
## ``data``.
##
## Procedure returns ``Asn1Status``.
var field, rawn, rawe: Asn1Field
var ab = Asn1Buffer.init(data)
field = ?ab.read()
if field.kind != Asn1Tag.Sequence:
return err(Asn1Error.Incorrect)
var ib = field.getBuffer()
field = ?ib.read()
if field.kind != Asn1Tag.Sequence:
return err(Asn1Error.Incorrect)
var ob = field.getBuffer()
field = ?ob.read()
if field.kind != Asn1Tag.Oid:
return err(Asn1Error.Incorrect)
elif field != Asn1OidRsaEncryption:
return err(Asn1Error.Incorrect)
field = ?ob.read()
if field.kind != Asn1Tag.Null:
return err(Asn1Error.Incorrect)
field = ?ib.read()
if field.kind != Asn1Tag.BitString:
return err(Asn1Error.Incorrect)
var vb = field.getBuffer()
field = ?vb.read()
if field.kind != Asn1Tag.Sequence:
return err(Asn1Error.Incorrect)
var sb = field.getBuffer()
rawn = ?sb.read()
if rawn.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
rawe = ?sb.read()
if rawe.kind != Asn1Tag.Integer:
return err(Asn1Error.Incorrect)
if len(rawn) >= (MinKeySize shr 3) and len(rawe) > 0:
key = new RsaPublicKey
key.buffer = @data
key.key.n = addr key.buffer[rawn.offset]
key.key.e = addr key.buffer[rawe.offset]
key.key.nlen = uint(len(rawn))
key.key.elen = uint(len(rawe))
ok()
else:
err(Asn1Error.Incorrect)
proc init*(sig: var RsaSignature, data: openArray[byte]): Result[void, Asn1Error] =
## Initialize RSA signature ``sig`` from ASN.1 DER binary representation
## ``data``.
##
## Procedure returns ``Result[void, Asn1Status]``.
if len(data) > 0:
sig = new RsaSignature
sig.buffer = @data
ok()
else:
err(Asn1Error.Incorrect)
proc init*[T: RsaPKI](sospk: var T, data: string): Result[void, Asn1Error] {.inline.} =
## Initialize EC `private key`, `public key` or `scalar` ``sospk`` from
## hexadecimal string representation ``data``.
##
## Procedure returns ``Result[void, Asn1Status]``.
sospk.init(ncrutils.fromHex(data))
proc init*(
t: typedesc[RsaPrivateKey], data: openArray[byte]
): RsaResult[RsaPrivateKey] =
## Initialize RSA private key from ASN.1 DER binary representation ``data``
## and return constructed object.
var res: RsaPrivateKey
if res.init(data).isErr:
err(RsaKeyIncorrectError)
else:
ok(res)
proc init*(t: typedesc[RsaPublicKey], data: openArray[byte]): RsaResult[RsaPublicKey] =
## Initialize RSA public key from ASN.1 DER binary representation ``data``
## and return constructed object.
var res: RsaPublicKey
if res.init(data).isErr:
err(RsaKeyIncorrectError)
else:
ok(res)
proc init*(t: typedesc[RsaSignature], data: openArray[byte]): RsaResult[RsaSignature] =
## Initialize RSA signature from raw binary representation ``data`` and
## return constructed object.
var res: RsaSignature
if res.init(data).isErr:
err(RsaSignatureError)
else:
ok(res)
proc init*[T: RsaPKI](t: typedesc[T], data: string): T {.inline.} =
## Initialize RSA `private key`, `public key` or `signature` from hexadecimal
## string representation ``data`` and return constructed object.
result = t.init(ncrutils.fromHex(data))
proc `$`*(key: RsaPrivateKey): string =
## Return string representation of RSA private key.
if isNil(key) or len(key.buffer) == 0:
result = "Empty or uninitialized RSA key"
else:
result = "RSA key ("
result.add($key.seck.nBitlen)
result.add(" bits)\n")
result.add("p = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.p, key.seck.plen)))
result.add("\nq = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.q, key.seck.qlen)))
result.add("\ndp = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.dp, key.seck.dplen)))
result.add("\ndq = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.dq, key.seck.dqlen)))
result.add("\niq = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.iq, key.seck.iqlen)))
result.add("\npre = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.pexp, key.pexplen)))
result.add("\nm = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.pubk.n, key.pubk.nlen)))
result.add("\npue = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.pubk.e, key.pubk.elen)))
result.add("\n")
proc `$`*(key: RsaPublicKey): string =
## Return string representation of RSA public key.
if isNil(key) or len(key.buffer) == 0:
result = "Empty or uninitialized RSA key"
else:
let nbitlen = key.key.nlen shl 3
result = "RSA key ("
result.add($nbitlen)
result.add(" bits)\nn = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.key.n, key.key.nlen)))
result.add("\ne = ")
result.add(ncrutils.toHex(getArray(key.buffer, key.key.e, key.key.elen)))
result.add("\n")
proc `$`*(sig: RsaSignature): string =
## Return string representation of RSA signature.
if isNil(sig) or len(sig.buffer) == 0:
result = "Empty or uninitialized RSA signature"
else:
result = "RSA signature ("
result.add(ncrutils.toHex(sig.buffer))
result.add(")")
proc `==`*(a, b: RsaPrivateKey): bool =
## Compare two RSA private keys for equality.
##
## Result is true if ``a`` and ``b`` are both ``nil`` or ``a`` and ``b`` are
## equal by value.
if isNil(a) and isNil(b):
true
elif isNil(a) and (not isNil(b)):
false
elif isNil(b) and (not isNil(a)):
false
else:
if a.seck.nBitlen == b.seck.nBitlen:
if a.seck.nBitlen > 0'u:
let r1 = CT.isEqual(
getArray(a.buffer, a.seck.p, a.seck.plen),
getArray(b.buffer, b.seck.p, b.seck.plen),
)
let r2 = CT.isEqual(
getArray(a.buffer, a.seck.q, a.seck.qlen),
getArray(b.buffer, b.seck.q, b.seck.qlen),
)
let r3 = CT.isEqual(
getArray(a.buffer, a.seck.dp, a.seck.dplen),
getArray(b.buffer, b.seck.dp, b.seck.dplen),
)
let r4 = CT.isEqual(
getArray(a.buffer, a.seck.dq, a.seck.dqlen),
getArray(b.buffer, b.seck.dq, b.seck.dqlen),
)
let r5 = CT.isEqual(
getArray(a.buffer, a.seck.iq, a.seck.iqlen),
getArray(b.buffer, b.seck.iq, b.seck.iqlen),
)
let r6 = CT.isEqual(
getArray(a.buffer, a.pexp, a.pexplen), getArray(b.buffer, b.pexp, b.pexplen)
)
let r7 = CT.isEqual(
getArray(a.buffer, a.pubk.n, a.pubk.nlen),
getArray(b.buffer, b.pubk.n, b.pubk.nlen),
)
let r8 = CT.isEqual(
getArray(a.buffer, a.pubk.e, a.pubk.elen),
getArray(b.buffer, b.pubk.e, b.pubk.elen),
)
r1 and r2 and r3 and r4 and r5 and r6 and r7 and r8
else:
true
else:
false
proc `==`*(a, b: RsaSignature): bool =
## Compare two RSA signatures for equality.
if isNil(a) and isNil(b):
true
elif isNil(a) and (not isNil(b)):
false
elif isNil(b) and (not isNil(a)):
false
else:
# We need to cover all the cases because Signature initialization procedure
# do not perform any checks.
if len(a.buffer) == 0 and len(b.buffer) == 0:
true
elif len(a.buffer) == 0 and len(b.buffer) != 0:
false
elif len(b.buffer) == 0 and len(a.buffer) != 0:
false
elif len(a.buffer) != len(b.buffer):
false
else:
CT.isEqual(a.buffer, b.buffer)
proc `==`*(a, b: RsaPublicKey): bool =
## Compare two RSA public keys for equality.
if isNil(a) and isNil(b):
true
elif isNil(a) and (not isNil(b)):
false
elif isNil(b) and (not isNil(a)):
false
else:
let r1 = CT.isEqual(
getArray(a.buffer, a.key.n, a.key.nlen), getArray(b.buffer, b.key.n, b.key.nlen)
)
let r2 = CT.isEqual(
getArray(a.buffer, a.key.e, a.key.elen), getArray(b.buffer, b.key.e, b.key.elen)
)
(r1 and r2)
proc sign*[T: byte | char](
key: RsaPrivateKey, message: openArray[T]
): RsaResult[RsaSignature] {.gcsafe.} =
## Get RSA PKCS1.5 signature of data ``message`` using SHA256 and private
## key ``key``.
if isNil(key):
return err(RsaKeyIncorrectError)
var hc: HashCompatContext
var hash: array[32, byte]
let impl = rsaPkcs1SignGetDefault()
var res = new RsaSignature
res.buffer = newSeq[byte]((key.seck.nBitlen + 7) shr 3)
var kv = addr sha256Vtable
kv.init(addr hc.vtable)
if len(message) > 0:
kv.update(addr hc.vtable, unsafeAddr message[0], uint(len(message)))
else:
kv.update(addr hc.vtable, nil, 0)
kv.out(addr hc.vtable, addr hash[0])
var oid = RsaOidSha256
let implRes =
impl(addr oid[0], addr hash[0], uint(len(hash)), addr key.seck, addr res.buffer[0])
if implRes == 0:
err(RsaSignatureError)
else:
ok(res)
proc verify*[T: byte | char](
sig: RsaSignature, message: openArray[T], pubkey: RsaPublicKey
): bool {.inline.} =
## Verify RSA signature ``sig`` using public key ``pubkey`` and data
## ``message``.
##
## Return ``true`` if message verification succeeded, ``false`` if
## verification failed.
doAssert((not isNil(sig)) and (not isNil(pubkey)))
if len(sig.buffer) > 0:
var hc: HashCompatContext
var hash: array[32, byte]
var check: array[32, byte]
var impl = rsaPkcs1VrfyGetDefault()
var kv = addr sha256Vtable
kv.init(addr hc.vtable)
if len(message) > 0:
kv.update(addr hc.vtable, unsafeAddr message[0], uint(len(message)))
else:
kv.update(addr hc.vtable, nil, 0)
kv.out(addr hc.vtable, addr hash[0])
var oid = RsaOidSha256
let res = impl(
addr sig.buffer[0],
uint(len(sig.buffer)),
addr oid[0],
uint(len(check)),
addr pubkey.key,
addr check[0],
)
if res == 1:
result = equalMem(addr check[0], addr hash[0], len(hash))