From 887795bbd20fbffb4cf2e0bd93b96dac886fca08 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Sun, 24 Feb 2019 02:07:13 +0200 Subject: [PATCH] Final RSA changes. Add RSA tests and test vectors. --- libp2p.nimble | 4 +- libp2p/crypto/common.nim | 315 +------------ libp2p/crypto/ecnist.nim | 2 - libp2p/crypto/minasn1.nim | 9 + libp2p/crypto/rsa.nim | 955 +++++++++++++++++++++++++++----------- tests/testrsa.nim | 562 ++++++++++++++++++++++ 6 files changed, 1261 insertions(+), 586 deletions(-) create mode 100644 tests/testrsa.nim diff --git a/libp2p.nimble b/libp2p.nimble index ad83c06..f342f5b 100644 --- a/libp2p.nimble +++ b/libp2p.nimble @@ -9,7 +9,8 @@ skipDirs = @["tests", "examples", "Nim"] requires "nim > 0.18.0", "nimcrypto", - "chronos" + "chronos", + "secp256k1" task test, "Runs the test suite": exec "nim c -r tests/testvarint" @@ -20,4 +21,5 @@ task test, "Runs the test suite": exec "nim c -r tests/testmultibase" exec "nim c -r tests/testcid" exec "nim c -r tests/testecnist" + exec "nim c -r tests/testrsa" exec "nim c -r tests/testdaemon" diff --git a/libp2p/crypto/common.nim b/libp2p/crypto/common.nim index 4b4c627..7f5c987 100644 --- a/libp2p/crypto/common.nim +++ b/libp2p/crypto/common.nim @@ -120,6 +120,8 @@ when sizeof(int) == 8: {.compile: bearRsaPath & "rsa_default_modulus.c".} {.compile: bearRsaPath & "rsa_default_privexp.c".} {.compile: bearRsaPath & "rsa_default_pubexp.c".} + {.compile: bearRsaPath & "rsa_default_pkcs1_sign.c".} + {.compile: bearRsaPath & "rsa_default_pkcs1_vrfy.c".} ## Elliptic Curve sources {.compile: bearEcPath & "ec_all_m31.c".} @@ -214,6 +216,8 @@ elif sizeof(int) == 4: {.compile: bearRsaPath & "rsa_default_modulus.c".} {.compile: bearRsaPath & "rsa_default_privexp.c".} {.compile: bearRsaPath & "rsa_default_pubexp.c".} + {.compile: bearRsaPath & "rsa_default_pkcs1_sign.c".} + {.compile: bearRsaPath & "rsa_default_pkcs1_vrfy.c".} ## Elliptic Curve sources {.compile: bearEcPath & "ec_all_m31.c".} @@ -236,60 +240,7 @@ elif sizeof(int) == 4: else: error("Sorry, your target architecture is not supported!") -## X509 sources -{.compile: bearX509Path & "asn1enc.c".} -{.compile: bearX509Path & "encode_rsa_pk8der.c".} -{.compile: bearX509Path & "encode_rsa_rawder.c".} -{.compile: bearX509Path & "skey_decoder.c".} - const - X509_BUFSIZE_KEY* = 520 - X509_BUFSIZE_SIG* = 512 - - ERR_X509_OK* = 32 - ERR_X509_INVALID_VALUE* = 33 - ERR_X509_TRUNCATED* = 34 - ERR_X509_EMPTY_CHAIN* = 35 - ERR_X509_INNER_TRUNC* = 36 - ERR_X509_BAD_TAG_CLASS* = 37 - ERR_X509_BAD_TAG_VALUE* = 38 - ERR_X509_INDEFINITE_LENGTH* = 39 - ERR_X509_EXTRA_ELEMENT* = 40 - ERR_X509_UNEXPECTED* = 41 - ERR_X509_NOT_CONSTRUCTED* = 42 - ERR_X509_NOT_PRIMITIVE* = 43 - ERR_X509_PARTIAL_BYTE* = 44 - ERR_X509_BAD_BOOLEAN* = 45 - ERR_X509_OVERFLOW* = 46 - ERR_X509_BAD_DN* = 47 - ERR_X509_BAD_TIME* = 48 - ERR_X509_UNSUPPORTED* = 49 - ERR_X509_LIMIT_EXCEEDED* = 50 - ERR_X509_WRONG_KEY_TYPE* = 51 - ERR_X509_BAD_SIGNATURE* = 52 - ERR_X509_TIME_UNKNOWN* = 53 - ERR_X509_EXPIRED* = 54 - ERR_X509_DN_MISMATCH* = 55 - ERR_X509_BAD_SERVER_NAME* = 56 - ERR_X509_CRITICAL_EXTENSION* = 57 - ERR_X509_NOT_CA* = 58 - ERR_X509_FORBIDDEN_KEY_USAGE* = 59 - ERR_X509_WEAK_PUBLIC_KEY* = 60 - ERR_X509_NOT_TRUSTED* = 62 - - BR_PEM_LINE64* = 1 - BR_PEM_CRLF* = 2 - - BR_X509_TA_CA* = 0x00000001 - BR_KEYTYPE_RSA* = 1 - BR_KEYTYPE_EC* = 2 - BR_KEYTYPE_KEYX* = 0x00000010 - BR_KEYTYPE_SIGN* = 0x00000020 - - BR_PEM_BEGIN_OBJ* = 1 - BR_PEM_END_OBJ* = 2 - BR_PEM_ERROR* = 3 - BR_EC_SECP256R1* = 23 BR_EC_SECP384R1* = 24 BR_EC_SECP521R1* = 25 @@ -298,43 +249,6 @@ const BR_EC_KBUF_PUB_MAX_SIZE* = 145 type - MarshalKind* = enum - RAW, PKCS8 - - X509Status* {.pure.} = enum - OK = ERR_X509_OK, - INVALID_VALUE = ERR_X509_INVALID_VALUE, - TRUNCATED = ERR_X509_TRUNCATED, - EMPTY_CHAIN = ERR_X509_EMPTY_CHAIN, - INNER_TRUNC = ERR_X509_INNER_TRUNC, - BAD_TAG_CLASS = ERR_X509_BAD_TAG_CLASS, - BAD_TAG_VALUE = ERR_X509_BAD_TAG_VALUE, - INDEFINITE_LENGTH = ERR_X509_INDEFINITE_LENGTH, - EXTRA_ELEMENT = ERR_X509_EXTRA_ELEMENT, - UNEXPECTED = ERR_X509_UNEXPECTED, - NOT_CONSTRUCTED = ERR_X509_NOT_CONSTRUCTED, - NOT_PRIMITIVE = ERR_X509_NOT_PRIMITIVE, - PARTIAL_BYTE = ERR_X509_PARTIAL_BYTE, - BAD_BOOLEAN = ERR_X509_BAD_BOOLEAN, - OVERFLOW = ERR_X509_OVERFLOW, - BAD_DN = ERR_X509_BAD_DN, - BAD_TIME = ERR_X509_BAD_TIME, - UNSUPPORTED = ERR_X509_UNSUPPORTED, - LIMIT_EXCEEDED = ERR_X509_LIMIT_EXCEEDED, - WRONG_KEY_TYPE = ERR_X509_WRONG_KEY_TYPE, - BAD_SIGNATURE = ERR_X509_BAD_SIGNATURE, - TIME_UNKNOWN = ERR_X509_TIME_UNKNOWN, - EXPIRED = ERR_X509_EXPIRED, - DN_MISMATCH = ERR_X509_DN_MISMATCH, - BAD_SERVER_NAME = ERR_X509_BAD_SERVER_NAME, - CRITICAL_EXTENSION = ERR_X509_CRITICAL_EXTENSION, - NOT_CA = ERR_X509_NOT_CA, - FORBIDDEN_KEY_USAGE = ERR_X509_FORBIDDEN_KEY_USAGE, - WEAK_PUBLIC_KEY = ERR_X509_WEAK_PUBLIC_KEY, - NOT_TRUSTED = ERR_X509_NOT_TRUSTED, - INCORRECT_VALUE = 100 - MISSING_KEY = 101 - BrHashClass* {.importc: "br_hash_class", header: "bearssl_hash.h", bycopy.} = object contextSize* {.importc: "context_size".}: int @@ -450,91 +364,6 @@ type x* {.importc: "x".}: ptr cuchar xlen* {.importc: "xlen".}: int - BrPublicKeyUnion* {.importc: "no_name", header: "bearssl_x509.h", - bycopy.} = object {.union.} - rsa* {.importc: "rsa".}: BrRsaPublicKey - ec* {.importc: "ec".}: BrEcPublicKey - - BrPrivateKeyUnion* {.importc: "no_name", header: "bearssl_x509.h", - bycopy.} = object {.union.} - rsa* {.importc: "rsa".}: BrRsaPrivateKey - ec* {.importc: "ec".}: BrEcPrivateKey - - X509Pkey* {.importc: "br_x509_pkey", header: "bearssl_x509.h", - bycopy.} = object - keyType* {.importc: "key_type".}: cuchar - key* {.importc: "key".}: BrPublicKeyUnion - - BrX509CpuStruct* {.importc: "no_name", header: "bearssl_x509.h", - bycopy.} = object - dp* {.importc: "dp".}: ptr uint32 - rp* {.importc: "rp".}: ptr uint32 - ip* {.importc: "ip".}: ptr cuchar - - BrX509DecoderContext* {.importc: "br_x509_decoder_context", - header: "bearssl_x509.h", bycopy.} = object - pkey* {.importc: "pkey".}: X509Pkey - cpu* {.importc: "cpu".}: BrX509CpuStruct - dpStack* {.importc: "dp_stack".}: array[32, uint32] - rpStack* {.importc: "rp_stack".}: array[32, uint32] - err* {.importc: "err".}: cint - pad* {.importc: "pad".}: array[256, cuchar] - decoded* {.importc: "decoded".}: bool - notbeforeDays* {.importc: "notbefore_days".}: uint32 - notbeforeSeconds* {.importc: "notbefore_seconds".}: uint32 - notafterDays* {.importc: "notafter_days".}: uint32 - notafterSeconds* {.importc: "notafter_seconds".}: uint32 - isCA* {.importc: "isCA".}: bool - copyDn* {.importc: "copy_dn".}: cuchar - appendDnCtx* {.importc: "append_dn_ctx".}: pointer - appendDn* {.importc: "append_dn".}: proc (ctx: pointer, buf: pointer, - length: int) {.cdecl.} - hbuf* {.importc: "hbuf".}: ptr cuchar - hlen* {.importc: "hlen".}: int - pkeyData* {.importc: "pkey_data".}: array[X509_BUFSIZE_KEY, cuchar] - signerKeyType* {.importc: "signer_key_type".}: cuchar - signerHashId* {.importc: "signer_hash_id".}: cuchar - - BrSkeyDecoderContext* {.importc: "br_skey_decoder_context", - header: "bearssl_x509.h", bycopy.} = object - pkey* {.importc: "key".}: BrPrivateKeyUnion - cpu* {.importc: "cpu".}: BrX509CpuStruct - dpStack* {.importc: "dp_stack".}: array[32, uint32] - rpStack* {.importc: "rp_stack".}: array[32, uint32] - err* {.importc: "err".}: cint - hbuf* {.importc: "hbuf".}: ptr cuchar - hlen* {.importc: "hlen".}: int - pad* {.importc: "pad".}: array[256, cuchar] - keyType* {.importc: "key_type".}: cuchar - keyData* {.importc: "key_data".}: array[3 * X509_BUFSIZE_SIG, cuchar] - - BrPemCpuStruct* {.importc: "no_name", header: "bearssl_pem.h", - bycopy.} = object - dp* {.importc: "dp".}: ptr uint32 - rp* {.importc: "rp".}: ptr uint32 - ip* {.importc: "ip".}: ptr cuchar - - BrPemDecoderContext* {.importc: "br_pem_decoder_context", - header: "bearssl_pem.h", bycopy.} = object - cpu* {.importc: "cpu".}: BrPemCpuStruct - dpStack* {.importc: "dp_stack".}: array[32, uint32] - rpStack* {.importc: "rp_stack".}: array[32, uint32] - err* {.importc: "err".}: cint - hbuf* {.importc: "hbuf".}: ptr cuchar - hlen* {.importc: "hlen".}: int - dest* {.importc: "dest".}: proc (destCtx: pointer; src: pointer, - length: int) {.cdecl.} - destCtx* {.importc: "dest_ctx".}: pointer - event* {.importc: "event".}: cuchar - name* {.importc: "name".}: array[128, char] - buf* {.importc: "buf".}: array[255, cuchar] - pptr* {.importc: "ptr".}: int - - BrAsn1Uint* {.importc: "br_asn1_uint", header: "inner.h", bycopy.} = object - data* {.importc: "data".}: ptr cuchar - length* {.importc: "len".}: int - asn1len* {.importc: "asn1len".}: int - BrEcImplementation* {.importc: "br_ec_impl", header: "bearssl_ec.h", bycopy.} = object supportedCurves* {.importc: "supported_curves".}: uint32 @@ -572,21 +401,9 @@ type hash_out: ptr cuchar): uint32 {.cdecl.} BrPemDecoderProc* = proc (destctx: pointer, src: pointer, length: int) {.cdecl.} - -type - PemObject* = object - name*: string - data*: seq[byte] - - PemList* = seq[PemObject] - -proc brRsaPkcs1SigPad*(hashoid: ptr cuchar, hash: ptr cuchar, hashlen: int, - nbitlen: uint32, x: ptr cchar): uint32 {.cdecl, - importc: "br_rsa_pkcs1_sig_pad", header: "inner.h".} - -proc brRsaPkcs1SigUnpad*(sig: ptr cuchar, siglen: int, hashoid: ptr cuchar, - hashlen: int, hashout: ptr cuchar): uint32 {.cdecl, - importc: "br_rsa_pkcs1_sig_unpad", header: "inner.h".} + BrRsaPkcs1Sign* = proc (hash_oid: ptr cuchar, hash: ptr cuchar, hash_len: int, + pk: ptr BrRsaPrivateKey, + x: ptr cuchar): uint32 {.cdecl.} proc brPrngSeederSystem*(name: cstringArray): BrPrngSeeder {.cdecl, importc: "br_prng_seeder_system", header: "bearssl_rand.h".} @@ -598,6 +415,12 @@ proc brHmacDrbgInit*(ctx: ptr BrHmacDrbgContext, digestClass: ptr BrHashClass, proc brRsaKeygenGetDefault*(): BrRsaKeygen {. cdecl, importc: "br_rsa_keygen_get_default", header: "bearssl_rsa.h".} +proc BrRsaPkcs1SignGetDefault*(): BrRsaPkcs1Sign {. + cdecl, importc: "br_rsa_pkcs1_sign_get_default", header: "bearssl_rsa.h".} + +proc BrRsaPkcs1VrfyGetDefault*(): BrRsaPkcs1Verify {. + cdecl, importc: "br_rsa_pkcs1_vrfy_get_default", header: "bearssl_rsa.h".} + proc brRsaComputeModulusGetDefault*(): BrRsaComputeModulus {. cdecl, importc: "br_rsa_compute_modulus_get_default", header: "bearssl_rsa.h".} @@ -642,65 +465,6 @@ proc brEcdsaVerifyAsn1*(impl: ptr BrEcImplementation, hash: pointer, siglen: int): uint32 {. cdecl, importc: "br_ecdsa_i31_vrfy_asn1", header: "bearssl_ec.h".} -proc brAsn1UintPrepare*(xdata: pointer, xlen: int): BrAsn1Uint {. - cdecl, importc: "br_asn1_uint_prepare", header: "inner.h".} - -proc brAsn1EncodeLength*(dest: pointer, length: int): int {. - cdecl, importc: "br_asn1_encode_length", header: "inner.h".} - -proc brAsn1EncodeUint*(dest: pointer, pp: BrAsn1Uint): int {. - cdecl, importc: "br_asn1_encode_uint", header: "inner.h".} - -proc brEncodeRsaRawDer*(dest: ptr byte, sk: ptr BrRsaPrivateKey, - pk: ptr BrRsaPublicKey, d: ptr byte, - dlength: int): int {. - cdecl, importc: "br_encode_rsa_raw_der", header: "bearssl_x509.h".} - -proc brEncodeRsaPkcs8Der*(dest: ptr byte, sk: ptr BrRsaPrivateKey, - pk: ptr BrRsaPublicKey, d: ptr byte, - dlength: int): int {. - cdecl, importc: "br_encode_rsa_pkcs8_der", header: "bearssl_x509.h".} - -proc brPemEncode*(dest: ptr byte, data: ptr byte, length: int, banner: cstring, - flags: cuint): int {. - cdecl, importc: "br_pem_encode", header: "bearssl_pem.h".} - -proc brPemDecoderInit*(ctx: ptr BrPemDecoderContext) {. - cdecl, importc: "br_pem_decoder_init", header: "bearssl_pem.h".} - -proc brPemDecoderPush*(ctx: ptr BrPemDecoderContext, - data: pointer, length: int): int {. - cdecl, importc: "br_pem_decoder_push", header: "bearssl_pem.h".} - -proc brPemDecoderSetdest*(ctx: ptr BrPemDecoderContext, dest: BrPemDecoderProc, - destctx: pointer) {.inline.} = - ctx.dest = dest - ctx.destCtx = destctx - -proc brPemDecoderEvent*(ctx: ptr BrPemDecoderContext): cint {. - cdecl, importc: "br_pem_decoder_event", header: "bearssl_pem.h".} - -proc brPemDecoderName*(ctx: ptr BrPemDecoderContext): cstring = - result = addr ctx.name[0] - -proc brSkeyDecoderInit*(ctx: ptr BrSkeyDecoderContext) {.cdecl, - importc: "br_skey_decoder_init", header: "bearssl_x509.h".} - -proc brSkeyDecoderPush*(ctx: ptr BrSkeyDecoderContext, - data: pointer, length: int) {.cdecl, - importc: "br_skey_decoder_push", header: "bearssl_x509.h".} - -proc brSkeyDecoderLastError*(ctx: ptr BrSkeyDecoderContext): int {.inline.} = - if ctx.err != 0: - result = ctx.err - else: - if ctx.keyType == '\0': - result = ERR_X509_TRUNCATED - -proc brSkeyDecoderKeyType*(ctx: ptr BrSkeyDecoderContext): int {.inline.} = - if ctx.err == 0: - result = cast[int](ctx.keyType) - var sha256Vtable* {.importc: "br_sha256_vtable", header: "bearssl_hash.h".}: BrHashClass var sha384Vtable* {.importc: "br_sha384_vtable", @@ -715,56 +479,3 @@ template brRsaPrivateKeyBufferSize*(size: int): int = template brRsaPublicKeyBufferSize*(size: int): int = # BR_RSA_KBUF_PUB_SIZE(size) (4 + ((size + 7) shr 3)) - -proc blobAppend(pseq: pointer, data: pointer, length: int) {.cdecl.} = - var cseq = cast[ptr seq[byte]](pseq) - let offset = len(cseq[]) - cseq[].setLen(offset + length) - copyMem(addr cseq[][offset], data, length) - -proc unmarshalPem*(data: string): PemList = - ## Decode PEM encoded string. - var ctx: BrPemDecoderContext - result = newSeq[PemObject]() - if len(data) > 0: - var nlstring = "\n" - var extranl = true - - brPemDecoderInit(addr ctx) - var pbuf = cast[pointer](unsafeAddr data[0]) - var plen = len(data) - var item = newSeq[byte]() - - GC_ref(item) - var inobj: bool - while plen > 0: - var tlen = brPemDecoderPush(addr ctx, pbuf, plen) - pbuf = cast[pointer](cast[uint](pbuf) + cast[uint](tlen)) - plen = plen - tlen - let event = brPemDecoderEvent(addr ctx) - if event == BR_PEM_BEGIN_OBJ: - item.setLen(0) - brPemDecoderSetdest(addr ctx, blobAppend, cast[pointer](addr item)) - inobj = true - elif event == BR_PEM_END_OBJ: - if inobj: - result.add(PemObject(name: $brPemDecoderName(addr ctx), data: item)) - inobj = false - else: - break - elif event == BR_PEM_ERROR: - result.setLen(0) - break - if plen == 0 and extranl: - # We add an extra newline at the end, in order to - # support PEM files that lack the newline on their last - # line (this is somwehat invalid, but PEM format is not - # standardised and such files do exist in the wild, so - # we'd better accept them). - extranl = false - pbuf = cast[pointer](addr nlstring[0]) - plen = 1 - if inobj: - # PEM object was not properly finished - result.setLen(0) - GC_unref(item) diff --git a/libp2p/crypto/ecnist.nim b/libp2p/crypto/ecnist.nim index 8868e1c..9c8e93e 100644 --- a/libp2p/crypto/ecnist.nim +++ b/libp2p/crypto/ecnist.nim @@ -162,7 +162,6 @@ proc copy*[T: EcPKI](dst: var T, src: T): bool = let length = len(src.buffer) if length > 0: dst.buffer = src.buffer - dst.curve = src.curve result = true proc copy*[T: EcPKI](src: T): T {.inline.} = @@ -187,7 +186,6 @@ proc clear*[T: EcPKI|EcKeyPair](pki: var T) = elif T is EcSignature: burnMem(pki.buffer) pki.buffer.setLen(0) - pki.curve = 0 else: burnMem(pki.seckey.buffer) burnMem(pki.pubkey.buffer) diff --git a/libp2p/crypto/minasn1.nim b/libp2p/crypto/minasn1.nim index 694644d..ca5794b 100644 --- a/libp2p/crypto/minasn1.nim +++ b/libp2p/crypto/minasn1.nim @@ -117,6 +117,12 @@ template isEnough*(ab: Asn1Buffer, length: int): bool = proc len*[T: Asn1Buffer|Asn1Composite](abc: T): int {.inline.} = len(abc.buffer) - abc.offset +proc len*(field: Asn1Field): int {.inline.} = + result = field.length + +template getPtr*(field: untyped): pointer = + cast[pointer](unsafeAddr field.buffer[field.offset]) + proc extend*[T: Asn1Buffer|Asn1Composite](abc: var T, length: int) {.inline.} = ## Extend buffer or composite's internal buffer by ``length`` octets. abc.buffer.setLen(len(abc.buffer) + length) @@ -521,6 +527,9 @@ proc read*(ab: var Asn1Buffer, field: var Asn1Field): Asn1Status = if not ab.isEnough(cast[int](length)): result = Asn1Status.Incomplete break + if ab.buffer[ab.offset] == 0x00'u8: + length -= 1 + ab.offset += 1 field = Asn1Field(kind: Asn1Tag.Integer, klass: klass, index: ttag, offset: cast[int](ab.offset), length: cast[int](length)) diff --git a/libp2p/crypto/rsa.nim b/libp2p/crypto/rsa.nim index 5567687..56d439a 100644 --- a/libp2p/crypto/rsa.nim +++ b/libp2p/crypto/rsa.nim @@ -1,58 +1,114 @@ -import strutils, hexdump, nimcrypto/utils -import common +## Nim-Libp2p +## Copyright (c) 2018 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 RSA PKCS#1.5 DSA. +import nimcrypto/utils +import common, minasn1 +export Asn1Status const - DefaultPublicExponent = 3'u32 + DefaultPublicExponent* = 3'u32 + ## Default value for RSA public exponent. + MinKeySize* = 512 + ## Minimal allowed RSA key size in bits. + DefaultKeySize* = 2048 + ## Default RSA key size in bits. + + RsaOidSha1* = [ + 0x05'u8, 0x2B'u8, 0x0E'u8, 0x03'u8, 0x02'u8, 0x1A'u8 + ] + ## RSA PKCS#1.5 SHA-1 hash object identifier. + RsaOidSha224* = [ + 0x09'u8, 0x60'u8, 0x86'u8, 0x48'u8, 0x01'u8, 0x65'u8, 0x03'u8, 0x04'u8, + 0x02'u8, 0x04'u8 + ] + ## RSA PKCS#1.5 SHA-224 hash object identifier. + RsaOidSha256* = [ + 0x09'u8, 0x60'u8, 0x86'u8, 0x48'u8, 0x01'u8, 0x65'u8, 0x03'u8, 0x04'u8, + 0x02'u8, 0x01'u8 + ] + ## RSA PKCS#1.5 SHA-256 hash object identifier. + RsaOidSha384* = [ + 0x09'u8, 0x60'u8, 0x86'u8, 0x48'u8, 0x01'u8, 0x65'u8, 0x03'u8, 0x04'u8, + 0x02'u8, 0x02'u8 + ] + ## RSA PKCS#1.5 SHA-384 hash object identifier. + RsaOidSha512* = [ + 0x09'u8, 0x60'u8, 0x86'u8, 0x48'u8, 0x01'u8, 0x65'u8, 0x03'u8, 0x04'u8, + 0x02'u8, 0x03'u8 + ] + ## RSA PKCS#1.5 SHA-512 hash object identifier. type RsaPrivateKey* = ref object buffer*: seq[byte] - key*: BrRsaPrivateKey + seck*: BrRsaPrivateKey + pubk*: BrRsaPublicKey + pexp*: ptr cuchar + pexplen*: int RsaPublicKey* = ref object buffer*: seq[byte] key*: BrRsaPublicKey - RsaKeyPair* = object - seckey*: RsaPrivateKey - pubkey*: RsaPublicKey + RsaKeyPair* = RsaPrivateKey - RsaModulus* = seq[byte] - RsaPublicExponent* = uint32 - RsaPrivateExponent* = seq[byte] + RsaSignature* = ref object + buffer*: seq[byte] + + RsaPKI* = RsaPrivateKey | RsaPublicKey | RsaSignature + RsaKP* = RsaPrivateKey | RsaKeyPair RsaError = object of Exception RsaRngError = object of RsaError RsaGenError = object of RsaError - RsaIncorrectKeyError = object of RsaError + RsaKeyIncorrectError = object of RsaError + RsaSignatureError = object of RsaError -proc random*(t: typedesc[RsaKeyPair], bits: int): RsaKeyPair = - ## Generate new random RSA key pair (private key and public key) using - ## BearSSL's HMAC-SHA256-DRBG algorithm. - ## - ## ``bits`` number of bits in RSA key, must be in range [512, 4096]. - var rng: BrHmacDrbgContext - var keygen: BrRsaKeygen - var seeder = brPrngSeederSystem(nil) - brHmacDrbgInit(addr rng, addr sha256Vtable, nil, 0) - if seeder(addr rng.vtable) == 0: - raise newException(ValueError, "Could not seed RNG") - keygen = brRsaKeygenGetDefault() - result.seckey = new RsaPrivateKey - result.pubkey = new RsaPublicKey - result.seckey.buffer = newSeq[byte](brRsaPrivateKeyBufferSize(bits)) - result.pubkey.buffer = newSeq[byte](brRsaPublicKeyBufferSize(bits)) - if keygen(addr rng.vtable, - addr result.seckey.key, addr result.seckey.buffer[0], - addr result.pubkey.key, addr result.pubkey.buffer[0], - cuint(bits), DefaultPublicExponent) == 0: - raise newException(RsaGenError, "Could not create keypair") +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 -proc random*(t: typedesc[RsaPrivateKey], bits: int): RsaPrivateKey = +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 + ls <= len(bs): + eo = so + 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.. 0: + let length = key.seck.plen + key.seck.qlen + key.seck.dplen + + key.seck.dqlen + key.seck.iqlen + key.pubk.nlen + + key.pubk.elen + key.pexplen + result = new RsaPrivateKey + result.buffer = newSeq[byte](length) + let po = 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 = cast[ptr cuchar](addr result.buffer[po]) + result.seck.q = cast[ptr cuchar](addr result.buffer[qo]) + result.seck.dp = cast[ptr cuchar](addr result.buffer[dpo]) + result.seck.dq = cast[ptr cuchar](addr result.buffer[dqo]) + result.seck.iq = cast[ptr cuchar](addr result.buffer[iqo]) + result.pubk.n = cast[ptr cuchar](addr result.buffer[no]) + result.pubk.e = cast[ptr cuchar](addr result.buffer[eo]) + result.pexp = cast[ptr cuchar](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 cuchar](addr result.buffer[no]) + result.key.e = cast[ptr cuchar](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 getKey*(key: RsaPrivateKey): RsaPublicKey = + ## Get RSA public key from RSA private key. + let length = key.pubk.nlen + key.pubk.elen result = new RsaPublicKey - result.buffer = newSeq[byte](modlen + sizeof(ebuf)) + result.buffer = newSeq[byte](length) result.key.n = cast[ptr cuchar](addr result.buffer[0]) - result.key.nlen = modlen - result.key.e = cast[ptr cuchar](addr result.buffer[modlen]) - result.key.elen = sizeof(ebuf) - ebuf[0] = cast[byte](pubexp shr 24) - ebuf[1] = cast[byte](pubexp shr 16) - ebuf[2] = cast[byte](pubexp shr 8) - ebuf[3] = cast[byte](pubexp) - copyMem(addr result.buffer[0], addr modulus[0], modlen) - copyMem(addr result.buffer[modlen], addr ebuf[0], sizeof(ebuf)) + result.key.e = cast[ptr cuchar](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).getKey() -proc brEncodePublicRsaRawDer(pubkey: RsaPublicKey, - dest: var openarray[byte]): int = - # RSAPublicKey ::= SEQUENCE { - # modulus INTEGER, -- n - # publicExponent INTEGER, -- e - # } - var num: array[2, BrAsn1Uint] - num[0] = brAsn1UintPrepare(pubkey.key.n, pubkey.key.nlen) - num[1] = brAsn1UintPrepare(pubkey.key.e, pubkey.key.elen) - var slen = 0 - for i in 0..<2: - var ilen = num[i].asn1len - slen += 1 + brAsn1EncodeLength(nil, ilen) + ilen - result = 1 + brAsn1EncodeLength(nil, slen) + slen - if len(dest) >= result: - var offset = 1 - dest[0] = 0x30'u8 - offset += brAsn1EncodeLength(addr dest[offset], slen) - for i in 0..<2: - offset += brAsn1EncodeUint(addr dest[offset], num[i]) +proc clear*[T: RsaPKI|RsaKeyPair](pki: var T) = + ## Wipe and clear EC private key, public key or scalar object. + 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 brAsn1EncodeBitString(data: var openarray[byte], bytesize: int): int = - result = 1 + brAsn1EncodeLength(nil, bytesize + 1) + 1 +proc toBytes*(key: RsaPrivateKey, data: var openarray[byte]): 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 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() + result = len(b) + if len(data) >= result: + copyMem(addr data[0], addr b.buffer[0], result) + +proc toBytes*(key: RsaPublicKey, data: var openarray[byte]): 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 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() + result = len(b) + if len(data) >= result: + copyMem(addr data[0], addr b.buffer[0], result) + +proc toBytes*(sig: RsaSignature, data: var openarray[byte]): 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. + result = len(sig.buffer) if len(data) >= result: - var offset = 1 - data[0] = 0x03'u8 - offset += brAsn1EncodeLength(addr data[offset], bytesize + 1) - data[offset] = 0'u8 + copyMem(addr data[0], addr sig.buffer[0], result) -proc marshalBin*(pubkey: RsaPublicKey, kind: MarshalKind): seq[byte] = - ## Serialize RSA public key ``pubkey`` to binary [RAWDER, PKCS8DER] format. - var tmp: array[1, byte] - if kind == RAW: - var res = brEncodePublicRsaRawDer(pubkey, tmp) - result = newSeq[byte](res) - res = brEncodePublicRsaRawDer(pubkey, result) - assert res == len(result) - elif kind == PKCS8: - var pkcs8head = [ - 0x30'u8, 0x0D'u8, 0x06'u8, 0x09'u8, 0x2A'u8, 0x86'u8, 0x48'u8, 0x86'u8, - 0xF7'u8, 0x0D'u8, 0x01'u8, 0x01'u8, 0x01'u8, 0x05'u8, 0x00'u8 - ] - var lenraw = brEncodePublicRsaRawDer(pubkey, tmp) - echo "lenraw = ", lenraw, " hex = 0x", toHex(lenraw) - echo "RAWDER" +proc getBytes*(key: RsaPrivateKey): seq[byte] = + ## Serialize RSA private key ``key`` to ASN.1 DER binary form and + ## return it. + result = newSeq[byte](4096) + let length = key.toBytes(result) + if length > 0: + result.setLen(length) + else: + raise newException(RsaKeyIncorrectError, "Incorrect private key") - var lenseq = sizeof(pkcs8head) + brAsn1EncodeLength(nil, lenraw) + lenraw + - brAsn1EncodeBitString(tmp, lenraw) - echo "brAsn1EncodeBitString = ", brAsn1EncodeBitString(tmp, lenraw) - echo "lenseq = ", lenseq - result = newSeq[byte](1 + brAsn1EncodeLength(nil, lenseq) + lenseq) - result[0] = 0x30'u8 - echo dumpHex(result) - var offset = 1 - offset += brAsn1EncodeLength(addr result[offset], lenseq) - echo dumpHex(result) - copyMem(addr result[offset], addr pkcs8head[0], sizeof(pkcs8head)) - echo dumpHex(result) - offset += sizeof(pkcs8head) - offset += brAsn1EncodeBitString(result.toOpenArray(offset, len(result) - 1), - lenraw) - echo "AFTER BITSTRING" - echo dumpHex(result) - echo dumpHex(result.toOpenArray(offset, len(result) - 1)) - offset += brAsn1EncodeLength(addr result[offset], lenraw) +proc getBytes*(key: RsaPublicKey): seq[byte] = + ## Serialize RSA public key ``key`` to ASN.1 DER binary form and + ## return it. + result = newSeq[byte](4096) + let length = key.toBytes(result) + if length > 0: + result.setLen(length) + else: + raise newException(RsaKeyIncorrectError, "Incorrect private key") - var res = brEncodePublicRsaRawDer(pubkey, result.toOpenArray(offset, - len(result) - 1)) - echo "AFTER RAWDER" - echo dumpHex(result) - offset += res +proc getBytes*(sig: RsaSignature): seq[byte] = + ## Serialize RSA signature ``sig`` to raw binary form and return it. + result = newSeq[byte](4096) + let length = sig.toBytes(result) + if length > 0: + result.setLen(length) + else: + raise newException(RsaSignatureError, "Incorrect signature") +proc init*(key: var RsaPrivateKey, data: openarray[byte]): Asn1Status = + ## 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 + version: uint64 -proc marshalBin*(seckey: RsaPrivateKey, kind: MarshalKind): seq[byte] = - ## Serialize RSA private key ``seckey`` to binary [RAWDER, PKCS8DER] format. - var ebuf: array[4, byte] - var pubkey: BrRsaPublicKey - var modulus = seckey.modulus() - var pubexp = seckey.pubexp() - var privexp = seckey.privexp(pubexp) - pubkey.n = cast[ptr cuchar](addr modulus[0]) - pubkey.nlen = len(modulus) - ebuf[0] = cast[byte](pubexp shr 24) - ebuf[1] = cast[byte](pubexp shr 16) - ebuf[2] = cast[byte](pubexp shr 8) - ebuf[3] = cast[byte](pubexp) - pubkey.e = cast[ptr cuchar](addr ebuf[0]) - pubkey.elen = sizeof(ebuf) - if kind == RAW: - let length = brEncodeRsaRawDer(nil, unsafeAddr seckey.key, addr pubkey, - addr privexp[0], len(privexp)) - result = newSeq[byte](length) - let res = brEncodeRsaRawDer(addr result[0], unsafeAddr seckey.key, - addr pubkey, addr privexp[0], - len(privexp)) - assert(res == length) - elif kind == PKCS8: - let length = brEncodeRsaPkcs8Der(nil, unsafeAddr seckey.key, addr pubkey, - addr privexp[0], len(privexp)) - result = newSeq[byte](length) - let res = brEncodeRsaPkcs8Der(addr result[0], unsafeAddr seckey.key, - addr pubkey, addr privexp[0], - len(privexp)) - assert(res == length) + var ab = Asn1Buffer.init(data) + result = ab.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.Sequence: + return Asn1Status.Incorrect -proc marshalPem*[T: RsaPrivateKey | RsaPublicKey](key: T, - kind: MarshalKind): string = - ## Serialize RSA private key ``seckey`` to PEM encoded format string. - var banner: string - let flags = cast[cuint](BR_PEM_CRLF or BR_PEM_LINE64) - if kind == RAW: - when T is RsaPrivateKey: - banner = "RSA PRIVATE KEY" - else: - banner = "RSA PUBLIC KEY" - elif kind == PKCS8: - when T is RsaPrivateKey: - banner = "PRIVATE KEY" - else: - banner = "PUBLIC KEY" - var buffer = marshalBin(key, kind) - if len(buffer) > 0: - let length = brPemEncode(nil, nil, len(buffer), banner, flags) - result = newString(length + 1) - let res = brPemEncode(cast[ptr byte](addr result[0]), addr buffer[0], - len(buffer), banner, flags) - result.setLen(res) + var ib = field.getBuffer() -proc copyRsaPrivateKey(brkey: BrRsaPrivateKey, - buffer: ptr cuchar): RsaPrivateKey = - result.buffer = newSeq[byte](brRsaPrivateKeyBufferSize(int(brkey.nBitlen))) - let p = cast[uint](addr result.buffer[0]) - let o = cast[uint](buffer) - let size = brkey.iqlen + cast[int](cast[uint](brkey.iq) - o) - if size > 0 and size <= len(result.buffer): - copyMem(addr result.buffer[0], buffer, size) - result.key.nBitlen = brkey.nBitlen - result.key.plen = brkey.plen - result.key.qlen = brkey.qlen - result.key.dplen = brkey.dplen - result.key.dqlen = brkey.dqlen - result.key.iqlen = brkey.iqlen - result.key.p = cast[ptr cuchar](p + cast[uint](brkey.p) - o) - result.key.q = cast[ptr cuchar](p + cast[uint](brkey.q) - o) - result.key.dp = cast[ptr cuchar](p + cast[uint](brkey.dp) - o) - result.key.dq = cast[ptr cuchar](p + cast[uint](brkey.dq) - o) - result.key.iq = cast[ptr cuchar](p + cast[uint](brkey.iq) - o) + result = ib.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect -proc tryUnmarshalBin*(key: var RsaPrivateKey, - data: openarray[byte]): X509Status = - ## Unserialize RSA private key ``key`` from binary blob ``data``. Binary blob - ## must be RAWDER or PKCS8DER format. - var ctx: BrSkeyDecoderContext - result = X509Status.INCORRECT_VALUE + if field.vint != 0'u64: + return Asn1Status.Incorrect + + result = ib.read(rawn) + if result != Asn1Status.Success: + return + if rawn.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = ib.read(rawpube) + if result != Asn1Status.Success: + return + if rawpube.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = ib.read(rawprie) + if result != Asn1Status.Success: + return + if rawprie.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = ib.read(rawp) + if result != Asn1Status.Success: + return + if rawp.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = ib.read(rawq) + if result != Asn1Status.Success: + return + if rawq.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = ib.read(rawdp) + if result != Asn1Status.Success: + return + if rawdp.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = ib.read(rawdq) + if result != Asn1Status.Success: + return + if rawdq.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = ib.read(rawiq) + if result != Asn1Status.Success: + return + if rawiq.kind != Asn1Tag.Integer: + return Asn1Status.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 = cast[ptr cuchar](addr key.buffer[rawn.offset]) + key.pubk.e = cast[ptr cuchar](addr key.buffer[rawpube.offset]) + key.seck.p = cast[ptr cuchar](addr key.buffer[rawp.offset]) + key.seck.q = cast[ptr cuchar](addr key.buffer[rawq.offset]) + key.seck.dp = cast[ptr cuchar](addr key.buffer[rawdp.offset]) + key.seck.dq = cast[ptr cuchar](addr key.buffer[rawdq.offset]) + key.seck.iq = cast[ptr cuchar](addr key.buffer[rawiq.offset]) + key.pexp = cast[ptr cuchar](addr key.buffer[rawprie.offset]) + key.pubk.nlen = len(rawn) + key.pubk.elen = len(rawpube) + key.seck.plen = len(rawp) + key.seck.qlen = len(rawq) + key.seck.dplen = len(rawdp) + key.seck.dqlen = len(rawdq) + key.seck.iqlen = len(rawiq) + key.pexplen = len(rawprie) + key.seck.nBitlen = cast[uint32](len(rawn) shl 3) + result = Asn1Status.Success + else: + result = Asn1Status.Incorrect + +proc init*(key: var RsaPublicKey, data: openarray[byte]): Asn1Status = + ## Initialize RSA public key ``key`` from ASN.1 DER binary representation + ## ``data``. + ## + ## Procedure returns ``Asn1Status``. + var field, rawn, rawe, oid: Asn1Field + var ab = Asn1Buffer.init(data) + + result = ab.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.Sequence: + return Asn1Status.Incorrect + var ib = field.getBuffer() + + result = ib.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.Sequence: + return Asn1Status.Incorrect + var ob = field.getBuffer() + + result = ob.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.Oid: + return Asn1Status.Incorrect + if field != Asn1OidRsaEncryption: + return Asn1Status.Incorrect + + result = ob.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.Null: + return Asn1Status.Incorrect + + result = ib.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.BitString: + return Asn1Status.Incorrect + var vb = field.getBuffer() + + result = vb.read(field) + if result != Asn1Status.Success: + return + if field.kind != Asn1Tag.Sequence: + return Asn1Status.Incorrect + var sb = field.getBuffer() + + result = sb.read(rawn) + if result != Asn1Status.Success: + return + if rawn.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + result = sb.read(rawe) + if result != Asn1Status.Success: + return + if rawn.kind != Asn1Tag.Integer: + return Asn1Status.Incorrect + + if len(rawn) >= (MinKeySize shr 3) and len(rawe) > 0: + key = new RsaPublicKey + key.buffer = @data + key.key.n = cast[ptr cuchar](addr key.buffer[rawn.offset]) + key.key.e = cast[ptr cuchar](addr key.buffer[rawe.offset]) + key.key.nlen = len(rawn) + key.key.elen = len(rawe) + result = Asn1Status.Success + else: + result = Asn1Status.Incorrect + +proc init*(sig: var RsaSignature, data: openarray[byte]): Asn1Status = + ## Initialize RSA signature ``sig`` from ASN.1 DER binary representation + ## ``data``. + ## + ## Procedure returns ``Asn1Status``. + result = Asn1Status.Incorrect if len(data) > 0: - brSkeyDecoderInit(addr ctx) - brSkeyDecoderPush(addr ctx, unsafeAddr data[0], len(data)) - let err = brSkeyDecoderLastError(addr ctx) - if err == 0: - let kt = brSkeyDecoderKeyType(addr ctx) - if kt == BR_KEYTYPE_RSA: - key = copyRsaPrivateKey(ctx.pkey.rsa, addr ctx.keyData[0]) - if key.key.nBitlen == ctx.pkey.rsa.nBitlen: - result = X509Status.OK - else: - result = X509Status.INCORRECT_VALUE - else: - result = X509Status.WRONG_KEY_TYPE - else: - result = cast[X509Status](err) + sig = new RsaSignature + sig.buffer = @data + result = Asn1Status.Success -proc unmarshalBin*(rt: typedesc[RsaPrivateKey], - data: openarray[byte]): RsaPrivateKey {.inline.} = - let res = tryUnmarshalBin(result, data) - if res != X509Status.OK: - raise newException(ValueError, $res) +proc init*[T: RsaPKI](sospk: var T, data: string): Asn1Status {.inline.} = + ## Initialize EC `private key`, `public key` or `scalar` ``sospk`` from + ## hexadecimal string representation ``data``. + ## + ## Procedure returns ``Asn1Status``. + result = sospk.init(fromHex(data)) -proc getPrivateKey*(key: var RsaPrivateKey, pemlist: PemList): X509Status = - ## Get first - result = X509Status.MISSING_KEY - for item in pemlist: - if "RSA PRIVATE KEY" in item.name: - result = key.tryUnmarshalBin(item.data) - return - elif "PRIVATE KEY" in item.name: - result = key.tryUnmarshalBin(item.data) - return +proc init*(t: typedesc[RsaPrivateKey], data: openarray[byte]): RsaPrivateKey = + ## Initialize RSA private key from ASN.1 DER binary representation ``data`` + ## and return constructed object. + let res = result.init(data) + if res != Asn1Status.Success: + raise newException(RsaKeyIncorrectError, + "Incorrect private key (" & $res & ")") -proc cmp(a: ptr cuchar, alen: int, b: ptr cuchar, blen: int): bool = - var arr = cast[ptr UncheckedArray[byte]](a) - var brr = cast[ptr UncheckedArray[byte]](b) +proc init*(t: typedesc[RsaPublicKey], data: openarray[byte]): RsaPublicKey = + ## Initialize RSA public key from ASN.1 DER binary representation ``data`` + ## and return constructed object. + let res = result.init(data) + if res != Asn1Status.Success: + raise newException(RsaKeyIncorrectError, + "Incorrect public key (" & $res & ")") + +proc init*(t: typedesc[RsaSignature], data: openarray[byte]): RsaSignature = + ## Initialize RSA signature from raw binary representation ``data`` and + ## return constructed object. + let res = result.init(data) + if res != Asn1Status.Success: + raise newException(RsaKeyIncorrectError, + "Incorrect signature (" & $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(fromHex(data)) + +proc `$`*(key: RsaPrivateKey): string = + ## Return string representation of RSA private key. + if len(key.buffer) == 0: + result = "Empty RSA key" + else: + result = "RSA key (" + result.add($key.seck.nBitlen) + result.add(" bits)\n") + result.add("p = ") + result.add(toHex(getArray(key.buffer, key.seck.p, key.seck.plen))) + result.add("\nq = ") + result.add(toHex(getArray(key.buffer, key.seck.q, key.seck.qlen))) + result.add("\ndp = ") + result.add(toHex(getArray(key.buffer, key.seck.dp, key.seck.dplen))) + result.add("\ndq = ") + result.add(toHex(getArray(key.buffer, key.seck.dq, key.seck.dqlen))) + result.add("\niq = ") + result.add(toHex(getArray(key.buffer, key.seck.iq, key.seck.iqlen))) + result.add("\npre = ") + result.add(toHex(getArray(key.buffer, key.pexp, key.pexplen))) + result.add("\nm = ") + result.add(toHex(getArray(key.buffer, key.pubk.n, key.pubk.nlen))) + result.add("\npue = ") + result.add(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 len(key.buffer) == 0: + result = "Empty RSA key" + else: + let nbitlen = key.key.nlen shl 3 + result = "RSA key (" + result.add($nbitlen) + result.add(" bits)\nn = ") + result.add(toHex(getArray(key.buffer, key.key.n, key.key.nlen))) + result.add("\ne = ") + result.add(toHex(getArray(key.buffer, key.key.e, key.key.elen))) + result.add("\n") + +proc `$`*(sig: RsaSignature): string = + ## Return string representation of RSA signature. + if len(sig.buffer) == 0: + result = "Empty RSA signature" + else: + result = "RSA signature (" + result.add(toHex(sig.buffer)) + result.add(")") + +proc cmp(a: openarray[byte], b: openarray[byte]): bool = + let alen = len(a) + let blen = len(b) if alen == blen: - var n = len(a) - var res, diff: int - while n > 0: - dec(n) - diff = int(arr[n]) - int(brr[n]) - res = (res and -not(diff)) or diff - result = (res == 0) + if alen == 0: + result = true + else: + var n = alen + var res, diff: int + while n > 0: + dec(n) + diff = int(a[n]) - int(b[n]) + res = (res and -not(diff)) or diff + result = (res == 0) proc `==`*(a, b: RsaPrivateKey): bool = ## Compare two RSA private keys for equality. - if a.key.nBitlen == b.key.nBitlen: - if cast[int](a.key.nBitlen) > 0: - let r1 = cmp(a.key.p, a.key.plen, b.key.p, b.key.plen) - let r2 = cmp(a.key.q, a.key.qlen, b.key.q, b.key.qlen) - let r3 = cmp(a.key.dp, a.key.dplen, b.key.dp, b.key.dplen) - let r4 = cmp(a.key.dq, a.key.dqlen, b.key.dq, b.key.dqlen) - let r5 = cmp(a.key.iq, a.key.iqlen, b.key.iq, b.key.iqlen) - result = r1 and r2 and r3 and r4 and r5 + if a.seck.nBitlen == b.seck.nBitlen: + if cast[int](a.seck.nBitlen) > 0: + let r1 = cmp(getArray(a.buffer, a.seck.p, a.seck.plen), + getArray(b.buffer, b.seck.p, b.seck.plen)) + let r2 = cmp(getArray(a.buffer, a.seck.q, a.seck.qlen), + getArray(b.buffer, b.seck.q, b.seck.qlen)) + let r3 = cmp(getArray(a.buffer, a.seck.dp, a.seck.dplen), + getArray(b.buffer, b.seck.dp, b.seck.dplen)) + let r4 = cmp(getArray(a.buffer, a.seck.dq, a.seck.dqlen), + getArray(b.buffer, b.seck.dq, b.seck.dqlen)) + let r5 = cmp(getArray(a.buffer, a.seck.iq, a.seck.iqlen), + getArray(b.buffer, b.seck.iq, b.seck.iqlen)) + let r6 = cmp(getArray(a.buffer, a.pexp, a.pexplen), + getArray(b.buffer, b.pexp, b.pexplen)) + let r7 = cmp(getArray(a.buffer, a.pubk.n, a.pubk.nlen), + getArray(b.buffer, b.pubk.n, b.pubk.nlen)) + let r8 = cmp(getArray(a.buffer, a.pubk.e, a.pubk.elen), + getArray(b.buffer, b.pubk.e, b.pubk.elen)) + result = r1 and r2 and r3 and r4 and r5 and r6 and r7 and r8 else: result = true -proc `$`*(a: RsaPrivateKey): string = - ## Return hexadecimal string representation of RSA private key. - result = "P: " - result.add("\n") - result.add("Q: ") - result.add("\n") - result.add("DP: ") - result.add("\n") - result.add("DQ: ") - result.add("\n") - result.add("IQ: ") - result.add("\n") +proc `==`*(a, b: RsaSignature): bool = + ## Compare two RSA signatures for equality. + result = (a.buffer == b.buffer) -when isMainModule: - var pk = RsaKeyPair.random() +proc `==`*(a, b: RsaPublicKey): bool = + ## Compare two RSA public keys for equality. + let r1 = cmp(getArray(a.buffer, a.key.n, a.key.nlen), + getArray(b.buffer, b.key.n, b.key.nlen)) + let r2 = cmp(getArray(a.buffer, a.key.e, a.key.elen), + getArray(b.buffer, b.key.e, b.key.elen)) + result = r1 and r2 +proc sign*[T: byte|char](key: RsaPrivateKey, + message: openarray[T]): RsaSignature = + ## Get RSA PKCS1.5 signature of data ``message`` using SHA256 and private + ## key ``key``. + var hc: BrHashCompatContext + var hash: array[32, byte] + var impl = BrRsaPkcs1SignGetDefault() + result = new RsaSignature + result.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], len(message)) + else: + kv.update(addr hc.vtable, nil, 0) + kv.output(addr hc.vtable, addr hash[0]) + var oid = RsaOidSha256 + let res = impl(cast[ptr cuchar](addr oid[0]), + cast[ptr cuchar](addr hash[0]), len(hash), + addr key.seck, cast[ptr cuchar](addr result.buffer[0])) + if res == 0: + raise newException(RsaSignatureError, "Signature generation error") + +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. + if len(sig.buffer) > 0: + var hc: BrHashCompatContext + var hash: array[32, byte] + var check: array[32, byte] + var impl = BrRsaPkcs1VrfyGetDefault() + var kv = addr sha256Vtable + kv.init(addr hc.vtable) + if len(message) > 0: + kv.update(addr hc.vtable, unsafeAddr message[0], len(message)) + else: + kv.update(addr hc.vtable, nil, 0) + kv.output(addr hc.vtable, addr hash[0]) + var oid = RsaOidSha256 + let res = impl(cast[ptr cuchar](addr sig.buffer[0]), len(sig.buffer), + cast[ptr cuchar](addr oid[0]), + len(check), addr pubkey.key, cast[ptr cuchar](addr check[0])) + if res == 1: + result = equalMem(addr check[0], addr hash[0], len(hash)) diff --git a/tests/testrsa.nim b/tests/testrsa.nim new file mode 100644 index 0000000..f281a06 --- /dev/null +++ b/tests/testrsa.nim @@ -0,0 +1,562 @@ +## Nim-Libp2p +## Copyright (c) 2018 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. +import unittest +import nimcrypto/utils +import ../libp2p/crypto/rsa + +const + TestsCount = 20 # number of random tests + + PrivateKeys = [ + """3082013902010002410093405660EDBF5DADAF93DD91E1B38F1EF086A3A0C6EA + 38011E58E08D27B163A9506AB837F4AF5CA23338AB3BFBC0AC7A0FAF6B9EE3B1 + 27BD66083C7B272C1D370203010001024017EE5DDB74E823F6655197B1ECC77C + DC1F651254BFDF32E8E3A0B825D8AC98B09D979753AC4F496AC5E66B5CFD740E + 86532AA1BB38C0E217C1D3736D519E4EC1022100C3FEB2BEA19C735EE3203F5A + F05E097491FB323217AA1A8494E2E623D9BDA497022100C0554F5A22AAF0E79C + 4E84BEEB336E975E6315664BB97E51120F6936DA8D40610220119A4877729058 + 944715D85AD487BD95A89EC4ED56CEC23EF21846CF257930D502202372D5C848 + 21777B48BCD40C982F0790108E7490411EB4205F12C6129D1F71A1022030BC71 + BF63EA847A3A682242AC73F3F3B4A232FA0BFC2D2892B7D8AA24958C4C""", + """3082025D02010002818100BDB0E9A7AF9865E318C8890A883D185F6D9F88868A + 5F586244CA6A07F298349EE52E38EA322D7E453AD2AA8B5019C25C50AEAAA59A + FA57173F2B3B44501EF45BCC6C1F75BEBAC5C11D7861A8A9DAED68964DEFA6AB + D0D9FE232E6BA9E97572B6F68C57030CCBAE19E972D16338201B7C11E00364FE + 7D613CB3367BB6CF2C3BE502030100010281807E679DCC785EFDC64F85928CCA + 2CAC492B2BDC368B8EEDBECE48744FC78155CF6CB95883F7DC0900E929E92BDC + BCCD9FE6C3FE434CFAF57E304206C486FF99A54CCBAA8396B6F6A85F72C9976F + 1B2F376D4518F90481EEA12D4FFD663505F2170473FAE9D593D2A5E8D754CFFE + 06420B464D2EA0DE17D99AB453148D85264721024100E2B5B2266846CC59CF7C + 238DBE0A2B0EAA2AB23E1EE1298E695B1A2A8C0D9B33ABB912C9558BE8CCEC5B + 25F7CFB77E8CD59E63469C089155DC29F2B1C1F5FA0F024100D632D7F41EA20A + 622843605354F05451F889CF4354A51B92C7BF1737B1EEDB9C4BBEDEF1EAA8E8 + 763C5EBAB618527A692912D79B610B94D88C97B6ED2C0BEECB02403A48FE4933 + 8BE823BADD2E82D575E5C5FA67C9B580D8E087357CEF883AC390C04308ECD488 + 42D512423DD8D0123E19B1F985A3FE564539A03A5F2A7F1ADFB36F024100D4F8 + 3E818987A17D50FD14A4263AC20BB26B0AF9AE0A6FACF40F8A3D251C119C882F + 6229F42036E98042CBAFCBE50DB2CB54E1ACAAC3C21DC144036C3334361B0241 + 00C2200411163DFAD0D56501AB1076A2CD7CC2FDB066CEAACD8212EC1F292C22 + F1840B8C1E23D941436F2EB38873FB76DC649D4DB85FF026D1D5DF405481A2F7 + F5""", + """308204A40201000282010100B7362C6653ED53C35C3AE663DA496C9B834FF34D + 72DA98FA6DBF4AABA39FCA0901F58A1B1D205076D20010151DBCA8FC2693E14D + 3502320D61E796E9C102C4EBB8F50B90616DE7FB8EA0A4BFC89BE542CC86DB7F + 179B153BC325048BD43A745A203F86B48D54589D4AFBEE128DCAD9C481C157F0 + AECB1EF4720E0E8535C96C318F3B53FEEFDD13774981BDDF6CB60331B2A954E2 + D60DE05961114A8B05EDF13DD116AE47E930CAC1A56FAFFE5438AB5DB0A88A35 + 63BCB9B4315121FC4685317847CA4806722BC74ED9954F409D42CAA3BE028E25 + 1FA0218414F2ED4469ED78C601C71EF7C68D06BBA24DCDFDC8DCCB1F93A72896 + 756B9CCA840E4188D8D02F89020301000102820100532B9596526D3F84453F3B + CD828FA86D247C4BF011BEA889AEFE92F03E1450CC2C06824E72B773AFACFF78 + 4D8DB552653D420E9A55010D25C417350C22A1963188423DA0AA8A1130C27BAC + AE9F6C1DF46812A45C1AA43D4C66F74C0C0A290B1ECADAEBD4D4FFC0468F7EB4 + 81D9BA87874C7C2FE6C402D3A7968B490E31EFA15C4D75FA6DBB49FDFBE5FA79 + F3A2BA7B519F6736E0786A78AB09DAF4F2D78C82DCD2342966D5D5C64E7F728C + B26D01847B30164CA037C44FD9A410F327C20273C26D708FE64F9ED739A43D1F + 97468D9BB7F789D79329CAE445CEA3CE2B66DD1552E29DEDBFFB2C7BC5B7C7CC + 513C410119E25C00D51849D1D85331C35A2527A54D02818100D1CFF7A51E0F10 + 39FF367CB1D13EEAE02CB54A7E155007BA981F25EEA673FD593642F63A49C934 + DC0B907AAB94FEB1A0ADCCE528A6C047137CF2C9A0479C53F4348769F279E5FA + 670C31D0CF922BFE3056B55049149756C3F4AEA085BB5CCD626265BCC9604939 + DCF8F0CF24E2088B021FE7803121C1B9377AC2C4B2296252FF02818100DF8B1C + FCEA9D5781B72CDF2FAF0C728103E93B09F861632B6AA1144E7A7BD0FF737812 + B8B57CDF5A7276340860EFA2881CE44B834A879DA0489D5F80FA40ADC4E90BDF + 20FE3101099B3BCB053BAE5C4403D2E5F6423521B8A86A1E8B4D79384D04B629 + B9EAE7324669B59FA4DD2359D3B6CC40E5A198FCF2D2F6B57C07A36577028180 + 3EAB06861C2755AA6D0F62495E7D937C27FE72649C8B0DF3EEF206CB748E5A92 + E60134388EC779716C46D84D1DB0C16F83DA1A6C7CFA1B80B7A67110DDB2D4E4 + B137ED2E4EB8A1855C00192596BC6B2D17090B14F900871AFA9F9A34B794ED87 + C06A30EC594525F259ACDBC2617D39C005B588F5A3E690230118E1D57144FC13 + 02818100BC1D2701B6B53D644D2F1789DAF6D08CBD2BD1A0EC4197E07B549DF0 + 04A69913BEA9B6A77522661A88E3EB9979696F0EB7B16DD2482FA377B463AA70 + B1272893E1C139BD5BEB05027E7D6CB534A9DFBEE4B0DF0FC25B4FCB42FE3A41 + E3AA34B4AB5857F8BA32605E5CAA9873761C3F8527F8EE4BCF171D15826E55FE + CFB6B2B5028181009234B4E0D11E2B61E46FDBDD5C577BA1DDB4EC9AD70D2EC3 + 69BE24654A493672A5686D76AD357ED6203FC1896BA57100CCEB7491D1A5A728 + 8A97B09FF1DC131E7136B557002205E2ADE5DB114499F15AA1E3C6C7E6FE6381 + 04B6A67697C60C4113F613130403C554688C7046D6AC5F5364A658917E23D40F + 1FDDD3DE4C9F51A9""", + """30820928020100028202010099EBDD4A8DBFD112966F0242CD0D0DFEE9A48572 + D49ED4F1E8BD52A08924691A6CE53A47140EC84D046DB142E0607733204FD461 + D8CB58BDBF05E51FFB77854660ED814861429AE54BD682A06F0B3C51FBD7A27D + 5117862D9EDF6B6A51B4E91F9A973EC3F8EF223EE656B4DA0B1CD41B323EE1ED + 4BCF88611C7BB11EEC1C7ED7D058B1E0F589D3682B25534CDD56F125D0298DD5 + 45D26BEB6E1DE1A5411CFE2520B066113AE24198BA24BC47D33B44552A1305E2 + 1FD6E1E1699DA1DB04975D35B27011AE9E613B7D4BD7F8144C00E11CC6704CE6 + 5DE9761B4ACE8D1F6516BE3B83A8DA3FAC94391DD2FD503342C0D92400461520 + 0F40B2BD0430A4472C0BB43F1629E851B5F87ACA7AEF56C06FBF5E2E481DC07C + E1A8A06F20ABC88203E2C588AACD1AC9CC8D42C52A37DD6CEC2E4916B3A4DD1C + F24B82BC6E02A0B87B6436AECA94E271D571A2FC7AEB494F180EAED8DF63B9F9 + 06E725447C1E3AC5CE54D996B7AB6E41E2CCA301D901716C886798077C61801B + F770A2A20F9E08BE57F9B02C56379D4B8D32DF4D07B3E70F781CAA4147B6F6A0 + 1BEC7AD99B07FC5DC8927A4DF9B17DFED8B01CCE57F78DD3EB1A408C900024CD + B1C5C909F55DC9D8C9E1F38DD52A3DB9287276D556FC1E5CADB7F727A0C74108 + CF0F8D88948331E024FA6EBBB064302E58B4FD3EAD0135CA0BAE5BEB2DC6DD5C + AC02EF7A59743F5415109D750203010001028202003A518A9BD7CF48A8F14488 + 27C5475FF9288F445CB8C0A15032EDA0A3E0B261FC382C36037E4F07875ED92C + E378DE33EBB41F6B09D3B9601B2C885042E8E56522C050DCBE0ED3CC9A7A3C81 + 6BC6070CC8C751F167E7D4B0EB1219F6B9E6D153CEBCA4F78C0B0298081AFFD9 + 30102BD115A8D8F8830F494793FBD5C5CB408C9F66A7B3235A67CDDBC2C92E30 + 3C9C5477B3EB06038E3E11370091CD5294697251BCC180F47B2CC3533549B9E7 + 7893490E5FF23C18EB8F42BC7CCAA6860FD4D280E77A7F49C3CE906A98D3A6B5 + 8103370613FFFFC6B335FBE1DAFF61F9485EE5DA17F48F8279C3AAB9655A6ECD + F74E20816549914429CC7DA7FB43DE26302D39160686AAFCA829C56FFBDEEC24 + EE70F9AD7F2363EB69FAE1436402BF55EC20A0C40CF0D7373FF55166644620B4 + 87006235789763832E7AC18852647449BA99955F3F35809827EF40FBAC271666 + 014AC48AB9FC70CC79A124A248ABDC791D87B957DC227223BA00269DC3250C71 + C42029659BD1DFF8F2709272061ED18C7BF2C627A245F33E5DEDC9F00270F910 + C778577AD94988DCD7795C2F507D04365E520500C05044FFC19A809B7A11B87E + 0AF4776C210E28278AC4C7F656B90D8DCEA7D5E39E8DBCF76C505E64EF757B37 + 8984070CB9F777A780D8595B17943C1BB5D6B26253F8BE95CFA79F1B736ACAA7 + AC6B900D89991B31271AB1251FD3F26DE1A8BD4AC10282010100C23FB658997C + C6E33A0C61BCA39FA63FB5188ED619F3B5C93602C218338370BA0F199077098F + E134E9625FF54E4098E8ECEEDEE2795A829E0DCF5EA79EC2F959F2D811E98A8C + 5240DF704C4E1EC6FCBE842B9A6D835234DCE9E69F7D24CD82A77924DC9EB9C4 + 9F8157BDC902A1E643C5C640077D3FF4FB2CA463115154BE346AC35932F1A403 + 3E4439993800D17F5527340368C4CEA4519CCBEFA27D82035AD62899D52A1FC1 + F77B67A538A979A6DBC304C16AD49BD8CEEBB9AE52BC05A715C7772934346859 + 4FB7BCD379ACF76EBC7DBE7A70EFCEC61E8B5033E423426FBF23F688CB5405E9 + 4AAB1EB0120E08755F3F681CC2964D661A4DE67B177885E779D10282010100CA + DA3B820CEE14BD3624C936957892A76AFF59B203BF734797824E24B674758B19 + F90C1EDB590ED23D810EA9649D5DEB79DC83B7E93EA99F0077BBB0C30FE53017 + 5C57099A528EC12AAFCB28DD4291135811122B3C56CEF79351A0E46C37DDCFB4 + 280C3FC40A9F782FDCF9A892E3606E2184C6A659D2159F49C4BB589B69A561DC + E0741793A33930F626F474E16949817FF64103D90B2977BC5AED5727172EABEB + F771020FCED09A2D3C5D7B29AE5BE7DC9E05A59D730B04D8BD8CBD6D18DC394E + 69121540D15A5B380B74D4DF67D4B358A620456024F16859AA14C1B74A4FDA6B + 2D91197C88232C67AA485FD224C222DA4E80132B07D966A040339977BA2E6502 + 82010014CACB12EAC2FF6AFD20AC298BFE1FC67BF4F7FD14E410564C50B943EB + E7AADE4F9575F037F6CDAD6339E3799779B4A46210238D6C5DF2D3463927319B + FDD4C0FB0C83C652CD3854B75606E4E9C874EF53A8732C3BBD45E94BA422F83B + 434033FAF4A624DB4F9F9F31AC1FE3073F658BBAEFC99D6F862288A1C3F4EB96 + BD9150C83E3904C280925EAD27A865F606C22FA312A65942A1361729812A0C73 + 2936C4EBADA3B29199AA8AEC0A3469779B13B4E94266D4012690E78C858B5792 + E7529E7A33CFE1B835AF21C4B58235B07A8AF48EB9FE72BCD85A16A16C1C8465 + AD1E7194070A74A0F95AC3BC522E6C901D3827CA5621C202E0E5F9E6ACD05357 + 49F2B10282010100B3125FC1F8A41BCEC48348D554B1F1D4B551E1DE920C6A39 + E2F7F6FFD5C98D1254C553FBD16B16F865AF0E405F3FC46F614E5740FD388208 + 8923299F6B331701933DC2E00949A417C61515E5671DA2704F2812BFF42E35DA + BC442D22389E40C360A891D7A0BA37F8A3581154CD06C853B06743EE0A10B961 + BA7F5F5B6326AA067033FC87EB9F0597C154B62C8FE8A0291FCB45AD9DE68A9E + D6B9F6171FBD09485FB03A24B5CBBEBEBBFC3411CBC3CE022AF19CCE8CE5C7EE + 695F3B64E57032C8ABFD792725E72A3AA8890483FF0BEBEABEF1383FC61616BE + 25994D658CE69F0393E5CFD78DE5A81745143F7BC74907D038A35FD08C060BD6 + DAD492388246EB39028201003A3A37839A14AAEA638ADB559DCF7CAD40E0D2FF + 3F3EACB9F32C1EDDB99A09505DEDDD08C22BF9F425ED8E3F1CF3685E22D480CB + EA0AF3329F62B7A33F4B2C4127D1041CCA480440BCB9862B15263FF473A62CF5 + FF78D9A79282DFF4F20AB2A025AD2D1ED557A74A45FEE4BC8617C8FBE10ACB6F + DEF43E0AFE077E564E0DA2FCE8A29C6341564D02562D8399C71E355A008945CA + DAA3C8C35515BDBBCBB73C023814531D5D880017DEF1D79EA68C66774009396A + 226A426A5BFCC35CB5D2C4718229AC2367BF7D4FB2379093D3607A2EBFBA778C + C40489622820A790E236BBB62BEAE9961D165D0784FFBA77A2999638D5AA7313 + 2BF2E6C334598283CFFA28CD""" + ] + + PublicKeys = [ + """305C300D06092A864886F70D0101010500034B00304802410093405660EDBF5D + ADAF93DD91E1B38F1EF086A3A0C6EA38011E58E08D27B163A9506AB837F4AF5C + A23338AB3BFBC0AC7A0FAF6B9EE3B127BD66083C7B272C1D370203010001""", + """30819F300D06092A864886F70D010101050003818D0030818902818100BDB0E9 + A7AF9865E318C8890A883D185F6D9F88868A5F586244CA6A07F298349EE52E38 + EA322D7E453AD2AA8B5019C25C50AEAAA59AFA57173F2B3B44501EF45BCC6C1F + 75BEBAC5C11D7861A8A9DAED68964DEFA6ABD0D9FE232E6BA9E97572B6F68C57 + 030CCBAE19E972D16338201B7C11E00364FE7D613CB3367BB6CF2C3BE5020301 + 0001""", + """30820122300D06092A864886F70D01010105000382010F003082010A02820101 + 00B7362C6653ED53C35C3AE663DA496C9B834FF34D72DA98FA6DBF4AABA39FCA + 0901F58A1B1D205076D20010151DBCA8FC2693E14D3502320D61E796E9C102C4 + EBB8F50B90616DE7FB8EA0A4BFC89BE542CC86DB7F179B153BC325048BD43A74 + 5A203F86B48D54589D4AFBEE128DCAD9C481C157F0AECB1EF4720E0E8535C96C + 318F3B53FEEFDD13774981BDDF6CB60331B2A954E2D60DE05961114A8B05EDF1 + 3DD116AE47E930CAC1A56FAFFE5438AB5DB0A88A3563BCB9B4315121FC468531 + 7847CA4806722BC74ED9954F409D42CAA3BE028E251FA0218414F2ED4469ED78 + C601C71EF7C68D06BBA24DCDFDC8DCCB1F93A72896756B9CCA840E4188D8D02F + 890203010001""", + """30820222300D06092A864886F70D01010105000382020F003082020A02820201 + 0099EBDD4A8DBFD112966F0242CD0D0DFEE9A48572D49ED4F1E8BD52A0892469 + 1A6CE53A47140EC84D046DB142E0607733204FD461D8CB58BDBF05E51FFB7785 + 4660ED814861429AE54BD682A06F0B3C51FBD7A27D5117862D9EDF6B6A51B4E9 + 1F9A973EC3F8EF223EE656B4DA0B1CD41B323EE1ED4BCF88611C7BB11EEC1C7E + D7D058B1E0F589D3682B25534CDD56F125D0298DD545D26BEB6E1DE1A5411CFE + 2520B066113AE24198BA24BC47D33B44552A1305E21FD6E1E1699DA1DB04975D + 35B27011AE9E613B7D4BD7F8144C00E11CC6704CE65DE9761B4ACE8D1F6516BE + 3B83A8DA3FAC94391DD2FD503342C0D924004615200F40B2BD0430A4472C0BB4 + 3F1629E851B5F87ACA7AEF56C06FBF5E2E481DC07CE1A8A06F20ABC88203E2C5 + 88AACD1AC9CC8D42C52A37DD6CEC2E4916B3A4DD1CF24B82BC6E02A0B87B6436 + AECA94E271D571A2FC7AEB494F180EAED8DF63B9F906E725447C1E3AC5CE54D9 + 96B7AB6E41E2CCA301D901716C886798077C61801BF770A2A20F9E08BE57F9B0 + 2C56379D4B8D32DF4D07B3E70F781CAA4147B6F6A01BEC7AD99B07FC5DC8927A + 4DF9B17DFED8B01CCE57F78DD3EB1A408C900024CDB1C5C909F55DC9D8C9E1F3 + 8DD52A3DB9287276D556FC1E5CADB7F727A0C74108CF0F8D88948331E024FA6E + BBB064302E58B4FD3EAD0135CA0BAE5BEB2DC6DD5CAC02EF7A59743F5415109D + 750203010001""" + ] + + Messages = [ + "sample", "test", "sample", "test", "sample", "test", "sample", "test" + ] + + Signatures = [ + """2E597258C445FC802E502E23E9ECC711F22A2264F661C2A738032A62408FA74B + C170A8C32F5283355898B2A547BB8C63E0A5CC80CBB326EE52C46094B9D1C517""", + """8CA474B07CFB229D530E9A1955D30058D3589DFCF44B442DA9F3D5362F1D36CD + 03184CFAA1F9153E034FCB2DA2226BD99BCD51612F909016707B22585A43DA96""", + """7B5311FED58881F5684343E030011A376563A7264C72553C66A5B2EA5D203127 + 8701B248C8D5008415AB7AB302F53A2C44064C84720C5F96A179B0DBC3AF7447 + 3E5A040513B0F8212509EE168E7AB90C0E59E157182471202D189F5C0AA2C30B + F172994581ECEC82F0D59DA7E35611B7E6938E8642F9F13DEC8E257FC5F1D70F""", + """8C8D7F3E30F8E8D9171F38BFFE2D80A8CF1F9CFC40BED7E530B650149BD395EF + FE1FFF5683B7E75215453E7ECFB5EF40BBF35B194FB85B718C80B3190C2851B1 + 36C84EBB8A8E45D55AB610B7E78B38F0D6A4E7032959CA1EF94E3B1C26702366 + F1524A9FFBA5243DF07876B7B77C4262C064841FEDCF82D1D7A1A0A411ED6E8D""", + """30768DB2A850D5F3F9044D2C7545C3E3D30B03B6F8159D305F19B1CF939C1336 + DDA83B406D86ECE5170CE922671FCB178FC03DEF87DBA73C002DC7D302FD122F + 1A23BB9F39F21DEBFE42E69A06A0871691B02AEE59772B485ABBF92B99B8E3C8 + F3E0396CD1F197BDD18B9C42684D1A4B91961737CCDA98E40791242ED49D79F5 + 02605DC05C3D5106A424ED1D763630864E9E3F448022F411B62F3F5EA87C567B + 7D888B1F5758BD98D366BC4188323986953A42B5A6A3F5AD31F60DF7DE3A1351 + 67B714734185DE35599DD53F884DEABCA41614363B596F01BC3EA349717ECF41 + 7251F2A93D1737304271F06DF6224A1BA2058B70996EEBC0CE4AF95006F3208C""", + """1E5217F9A99A084E7DF3B1AA68B32B04FAEE95A8815B40C236241E4B40DA094A + 7A8C768414E9B66298A48F0A3769E2D873077590D9D6A1A613E20003589E7FC7 + F27FC6ECD471B8C50D2ED4D7915F61FC04F82CCD1708DF64AC3666720CA9FF40 + D332199F35AEA377A8F8F12354A25A8A277EF9069A659C1819BD8C97146A7453 + EFFF4274AD0A406B957041ACD2FB892D5009673E50AE6B067C7E906F35A919C6 + 7CAEBE2EACA1067E9B76DB8A74AF8416A3C1E0711F92A520AFE4A258ACDB6128 + 47B06DF8B6705618D284C4272EDC1C11D1EDDF4D8174DE4F4706034421B0CB82 + 70771C40A2020E022886C44F7811AB06A3838B055F8D652104E653A1312B7506""", + """984A8ACD327392DF3C16D504AC152BB7484255A9A8E06EE54F4CE61D017B6908 + 11FA8D1845E72073E775A07FE5C8C028B1FC6E9B8349993D69A058DFA233E9FF + 69B2F65D12A80051C823AD81BAA143C958D484E1ED5749F67B21E2D69D7EC6E9 + CB55926C84DCCE0C367672D852A7E42CB049E33DFBB621EA2A66EB5736145D4E + 07FFE255B4248A68801768CF28F877B3E4FABD6D95F11109E0B1025BB917F6DE + 03CC9023D8225537369F39F509C188884F6C1900DE08E623E15A25373BA70F5A + F00C0375E6601CF93120A3145D6DD0C7D89022AC73FA2C12BE0F78AD280B9BF6 + 0DD431F9DD674C2BC092E81BC26A064C9E38614D9E464B40204C27255F69089F + 9B795E640D3A18159C39B3E1529F3AF0718322505F42331271F4AAA66F46F0C3 + 9BA01FB86A7B93AA727379F0C1F9AD08C7AC9B1CCA9F80B5569A292C589E8A97 + 236E8D29AAC10B698960D3183DE3E82CAA642C004DF65C99F6018D8EE42DF17F + 176AD36B63A09CFB2F7809DBDE52B4D09AF5310833B6819FBB88FDE1288320E9 + 4ABD50457654A1C7DD8E858743E04928388C95979814BE22FDE99B4914BC452A + 114DBD8F053EA97C5616C46D840D3251125C49A363D29AA31063BF5004D18724 + CCD2E6533B92608B283EC59937BB267D918B9EAC14AF8BB522D7494CFEE859EE + 07C0602C8683E4E658D585EBA488824A7BBFA32D7A31F5C46CBCE662998C1C75""", + """7B154A8A46ADF18B09E4A499391A40E798F63BC314584D701CB68923102F96E0 + 6F85314BD1F52E98AC24332C42AB15C116C165BF2F48C9116796D4B2FF0A1DA5 + 74669EE1FF51196BDFFBAECD67F97CFC9001F71E0840E5484E77E15C21CC66BD + B75593424A313E9E6828952ACC606DF0698491F6EF66E55DCBF16F1B7AC406C6 + 9B3AAF6554536802A33CE991365E18C50C407CF7C7A35AE43BB51E056C7D629B + 5558E32ADC25E4CDB9FF5EEB71A30C907F8FFA8DFBB428EEFD6ECB70605DB498 + B4F7EF081A4AE5B37BE32788C556F99D97956B1F0D3EF7932E5CA2F0A1C02764 + 09C979A06060A816EEDA400D01413E375434C9E083EE436BC73CE71BBE626102 + 19657852B3A5F9003DC72317A06CC4AF7EBD70E4CE90A2481C337C1E74BCC219 + 25FECA4765C24AA3DDF05D0A49BD66264A8E156D15CCEE08FC3B94776DBCCDFE + 4E55DE8B67DF40751F1CFBE71A74600C61954E21A021A4182152421384C9A0C8 + F7A3BAED39812B536076CA482C1C83C10153E1739648C5384134E58EB3F72D36 + 29E98C648410A8107EEADA3FAFD32B5AEF12D4E991EAA8364FDFF5AE1C9F34ED + 28B120B402C92717B0D480B5DE8D53CADC0903296614158F0DFC38505762C829 + 9DC529573BBF8C149D5E1E8745F9B72D4E68607C859327AF54D0013F37542236 + ACB51807206B8332127E3692269013B96F0CABD95D7431805E48176ADC5D1366""" + ] + +suite "RSA 512/1024/2048/4096 test suite": + + test "[rsa512] Private key serialize/deserialize test": + for i in 0.. 0 + check: + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPrivateKey.init(skey1) + var rkey4 = RsaPrivateKey.init(skey2) + check: + rkey1 == key + rkey2 == key + rkey3 == key + rkey4 == key + + test "[rsa1024] Private key serialize/deserialize test": + for i in 0.. 0 + check: + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPrivateKey.init(skey1) + var rkey4 = RsaPrivateKey.init(skey2) + check: + rkey1 == key + rkey2 == key + rkey3 == key + rkey4 == key + + test "[rsa2048] Private key serialize/deserialize test": + var rkey1, rkey2: RsaPrivateKey + var skey2 = newSeq[byte](4096) + var key = RsaPrivateKey.random(2048) + var skey1 = key.getBytes() + check key.toBytes(skey2) > 0 + check: + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPrivateKey.init(skey1) + var rkey4 = RsaPrivateKey.init(skey2) + check: + rkey1 == key + rkey2 == key + rkey3 == key + rkey4 == key + + test "[rsa4096] Private key serialize/deserialize test": + # This test is too slow to run in debug mode. + when defined(release): + var rkey1, rkey2: RsaPrivateKey + var skey2 = newSeq[byte](4096) + var key = RsaPrivateKey.random(4096) + var skey1 = key.getBytes() + check key.toBytes(skey2) > 0 + check: + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPrivateKey.init(skey1) + var rkey4 = RsaPrivateKey.init(skey2) + check: + rkey1 == key + rkey2 == key + rkey3 == key + rkey4 == key + + test "[rsa512] Public key serialize/deserialize test": + for i in 0.. 0 + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPublicKey.init(skey1) + var rkey4 = RsaPublicKey.init(skey2) + check: + rkey1 == pair.pubkey + rkey2 == pair.pubkey + rkey3 == pair.pubkey + rkey4 == pair.pubkey + + test "[rsa1024] Public key serialize/deserialize test": + for i in 0.. 0 + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPublicKey.init(skey1) + var rkey4 = RsaPublicKey.init(skey2) + check: + rkey1 == pair.pubkey + rkey2 == pair.pubkey + rkey3 == pair.pubkey + rkey4 == pair.pubkey + + test "[rsa2048] Public key serialize/deserialize test": + var rkey1, rkey2: RsaPublicKey + var skey2 = newSeq[byte](4096) + var pair = RsaKeyPair.random(2048) + var skey1 = pair.pubkey.getBytes() + check: + pair.pubkey.toBytes(skey2) > 0 + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPublicKey.init(skey1) + var rkey4 = RsaPublicKey.init(skey2) + check: + rkey1 == pair.pubkey + rkey2 == pair.pubkey + rkey3 == pair.pubkey + rkey4 == pair.pubkey + + test "[rsa4096] Public key serialize/deserialize test": + when defined(release): + var rkey1, rkey2: RsaPublicKey + var skey2 = newSeq[byte](4096) + var pair = RsaKeyPair.random(4096) + var skey1 = pair.pubkey.getBytes() + check: + pair.pubkey.toBytes(skey2) > 0 + rkey1.init(skey1) == Asn1Status.Success + rkey2.init(skey2) == Asn1Status.Success + var rkey3 = RsaPublicKey.init(skey1) + var rkey4 = RsaPublicKey.init(skey2) + check: + rkey1 == pair.pubkey + rkey2 == pair.pubkey + rkey3 == pair.pubkey + rkey4 == pair.pubkey + + test "[rsa512] Generate/Sign/Serialize/Deserialize/Verify test": + var message = "message to sign" + for i in 0..