From 52f88dbd1e9614c955c4bc0fd9b7f6faf02ac512 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Wed, 20 Feb 2019 00:11:59 +0200 Subject: [PATCH] Initial commit, not ready yet. --- libp2p/crypto/common.nim | 754 +++++++++++++++++++++++++++++++++++++++ libp2p/crypto/ecnist.nim | 587 ++++++++++++++++++++++++++++++ libp2p/crypto/rsa.nim | 342 ++++++++++++++++++ 3 files changed, 1683 insertions(+) create mode 100644 libp2p/crypto/common.nim create mode 100644 libp2p/crypto/ecnist.nim create mode 100644 libp2p/crypto/rsa.nim diff --git a/libp2p/crypto/common.nim b/libp2p/crypto/common.nim new file mode 100644 index 000000000..ffa8d4d09 --- /dev/null +++ b/libp2p/crypto/common.nim @@ -0,0 +1,754 @@ +import strutils +import hexdump +from os import DirSep + +when defined(vcc): + {.passC: "/Zi /FS".} + +const + bearPath = currentSourcePath.rsplit(DirSep, 1)[0] & DirSep & + ".." & DirSep & "BearSSL" & DirSep + bearSrcPath = bearPath & "src" + bearIncPath = bearPath & "inc" + bearIntPath = bearSrcPath & DirSep & "int" & DirSep + bearCodecPath = bearSrcPath & DirSep & "codec" & DirSep + bearRandPath = bearSrcPath & DirSep & "rand" & DirSep + bearRsaPath = bearSrcPath & DirSep & "rsa" & DirSep + bearEcPath = bearSrcPath & DirSep & "ec" & DirSep + bearX509Path = bearSrcPath & DirSep & "x509" & DirSep + bearMacPath = bearSrcPath & DirSep & "mac" & DirSep + bearHashPath = bearSrcPath & DirSep & "hash" & DirSep + +{.passC: "-I" & bearSrcPath} +{.passC: "-I" & bearIncPath} + +when defined(windows): + {.passC: "-DBR_USE_WIN32_TIME=1".} + {.passC: "-DBR_USE_WIN32_RAND=1".} +else: + {.passC: "-DBR_USE_UNIX_TIME=1".} + {.passC: "-DBR_USE_URANDOM=1".} + +when system.cpuEndian == bigEndian: + {.passC: "-DBR_BE_UNALIGNED=1".} +else: + {.passC: "-DBR_LE_UNALIGNED=1".} + +{.pragma: bearssl_func, importc, cdecl.} + +when sizeof(int) == 8: + {.passC: "-DBR_64=1".} + {.passC:" -DBR_amd64=1".} + when defined(vcc): + {.passC: "-DBR_UMUL128=1".} + else: + {.passC: "-DBR_INT128=1".} + + ## Codec sources + {.compile: bearCodecPath & "ccopy.c".} + {.compile: bearCodecPath & "enc64be.c".} + {.compile: bearCodecPath & "dec64be.c".} + {.compile: bearCodecPath & "enc32be.c".} + {.compile: bearCodecPath & "dec32be.c".} + {.compile: bearCodecPath & "pemenc.c".} + {.compile: bearCodecPath & "pemdec.c".} + + ## Big integer sources + {.compile: bearIntPath & "i31_add.c".} + {.compile: bearIntPath & "i31_bitlen.c".} + {.compile: bearIntPath & "i31_decmod.c".} + {.compile: bearIntPath & "i31_decode.c".} + {.compile: bearIntPath & "i31_decred.c".} + {.compile: bearIntPath & "i31_encode.c".} + {.compile: bearIntPath & "i31_fmont.c".} + {.compile: bearIntPath & "i31_iszero.c".} + {.compile: bearIntPath & "i31_moddiv.c".} + {.compile: bearIntPath & "i31_modpow.c".} + {.compile: bearIntPath & "i31_modpow2.c".} + {.compile: bearIntPath & "i31_montmul.c".} + {.compile: bearIntPath & "i31_mulacc.c".} + {.compile: bearIntPath & "i31_muladd.c".} + {.compile: bearIntPath & "i31_ninv31.c".} + {.compile: bearIntPath & "i31_reduce.c".} + {.compile: bearIntPath & "i31_rshift.c".} + {.compile: bearIntPath & "i31_sub.c".} + {.compile: bearIntPath & "i31_tmont.c".} + + ## Additional integer sources + {.compile: bearIntPath & "i32_div32.c".} + {.compile: bearIntPath & "i62_modpow2.c".} + + ## Random generator sources + {.compile: bearRandPath & "sysrng.c".} + {.compile: bearRandPath & "hmac_drbg.c".} + {.compile: bearRandPath & "aesctr_drbg.c".} + + ## HMAC sources + {.compile: bearMacPath & "hmac.c".} + {.compile: bearMacPath & "hmac_ct.c".} + + ## HASH sources + {.compile: bearHashPath & "mgf1.c".} + {.compile: bearHashPath & "ghash_ctmul64.c".} + {.compile: bearHashPath & "sha2small.c".} # SHA2-224/256 + {.compile: bearHashPath & "sha2big.c".} # SHA2-384/512 + + ## RSA sources + {.compile: bearRsaPath & "rsa_i31_keygen_inner.c".} + {.compile: bearRsaPath & "rsa_i62_keygen.c".} + {.compile: bearRsaPath & "rsa_i62_oaep_decrypt.c".} + {.compile: bearRsaPath & "rsa_i62_oaep_encrypt.c".} + {.compile: bearRsaPath & "rsa_i62_pkcs1_sign.c".} + {.compile: bearRsaPath & "rsa_i62_pkcs1_vrfy.c".} + {.compile: bearRsaPath & "rsa_i62_priv.c".} + {.compile: bearRsaPath & "rsa_i62_pub.c".} + {.compile: bearRsaPath & "rsa_oaep_pad.c".} + {.compile: bearRsaPath & "rsa_oaep_unpad.c".} + {.compile: bearRsaPath & "rsa_pkcs1_sig_pad.c".} + {.compile: bearRsaPath & "rsa_pkcs1_sig_unpad.c".} + {.compile: bearRsaPath & "rsa_ssl_decrypt.c".} + {.compile: bearRsaPath & "rsa_default_keygen.c".} + {.compile: bearRsaPath & "rsa_i31_modulus.c".} + {.compile: bearRsaPath & "rsa_i31_privexp.c".} + {.compile: bearRsaPath & "rsa_i31_pubexp.c".} + {.compile: bearRsaPath & "rsa_default_modulus.c".} + {.compile: bearRsaPath & "rsa_default_privexp.c".} + {.compile: bearRsaPath & "rsa_default_pubexp.c".} + + ## Elliptic Curve sources + {.compile: bearEcPath & "ec_all_m31.c".} + {.compile: bearEcPath & "ec_default.c".} + {.compile: bearEcPath & "ec_keygen.c".} + {.compile: bearEcPath & "ec_c25519_m31.c".} + {.compile: bearEcPath & "ec_c25519_m64.c".} + {.compile: bearEcPath & "ec_p256_m31.c".} + {.compile: bearEcPath & "ec_p256_m64.c".} + {.compile: bearEcPath & "ec_curve25519.c".} + {.compile: bearEcPath & "ec_prime_i31.c".} + {.compile: bearEcPath & "ec_pubkey.c".} + {.compile: bearEcPath & "ec_secp256r1.c".} + {.compile: bearEcPath & "ec_secp384r1.c".} + {.compile: bearEcPath & "ec_secp521r1.c".} + {.compile: bearEcPath & "ecdsa_i31_bits.c".} + {.compile: bearEcPath & "ecdsa_i31_sign_raw.c".} + {.compile: bearEcPath & "ecdsa_i31_sign_asn1.c".} + {.compile: bearEcPath & "ecdsa_i31_vrfy_asn1.c".} + {.compile: bearEcPath & "ecdsa_i31_vrfy_raw.c".} + {.compile: bearEcPath & "ecdsa_rta.c".} + {.compile: bearEcPath & "ecdsa_atr.c".} + +elif sizeof(int) == 4: + + ## Codec sources + {.compile: bearCodecPath & "ccopy.c".} + {.compile: bearCodecPath & "enc64be.c".} + {.compile: bearCodecPath & "dec64be.c".} + {.compile: bearCodecPath & "enc32be.c".} + {.compile: bearCodecPath & "dec32be.c".} + {.compile: bearCodecPath & "pemenc.c".} + {.compile: bearCodecPath & "pemdec.c".} + + ## Big integer sources + {.compile: bearIntPath & "i31_add.c".} + {.compile: bearIntPath & "i31_bitlen.c".} + {.compile: bearIntPath & "i31_decmod.c".} + {.compile: bearIntPath & "i31_decode.c".} + {.compile: bearIntPath & "i31_decred.c".} + {.compile: bearIntPath & "i31_encode.c".} + {.compile: bearIntPath & "i31_fmont.c".} + {.compile: bearIntPath & "i31_iszero.c".} + {.compile: bearIntPath & "i31_moddiv.c".} + {.compile: bearIntPath & "i31_modpow.c".} + {.compile: bearIntPath & "i31_modpow2.c".} + {.compile: bearIntPath & "i31_montmul.c".} + {.compile: bearIntPath & "i31_mulacc.c".} + {.compile: bearIntPath & "i31_muladd.c".} + {.compile: bearIntPath & "i31_ninv31.c".} + {.compile: bearIntPath & "i31_reduce.c".} + {.compile: bearIntPath & "i31_rshift.c".} + {.compile: bearIntPath & "i31_sub.c".} + {.compile: bearIntPath & "i31_tmont.c".} + + ## Additional integer sources + {.compile: bearIntPath & "i32_div32.c".} + + ## Random generator sources + {.compile: bearRandPath & "sysrng.c".} + {.compile: bearRandPath & "hmac_drbg.c".} + {.compile: bearRandPath & "aesctr_drbg.c".} + + ## HMAC sources + {.compile: bearMacPath & "hmac.c".} + {.compile: bearMacPath & "hmac_ct.c".} + + ## HASH sources + {.compile: bearHashPath & "mgf1.c".} + {.compile: bearHashPath & "ghash_ctmul.c".} + {.compile: bearHashPath & "sha2small.c".} # SHA2-224/256 + {.compile: bearHashPath & "sha2big.c".} # SHA2-384/512 + + ## RSA sources + {.compile: bearRsaPath & "rsa_i31_keygen_inner.c".} + {.compile: bearRsaPath & "rsa_i31_keygen.c".} + {.compile: bearRsaPath & "rsa_i31_oaep_decrypt.c".} + {.compile: bearRsaPath & "rsa_i31_oaep_encrypt.c".} + {.compile: bearRsaPath & "rsa_i31_pkcs1_sign.c".} + {.compile: bearRsaPath & "rsa_i31_pkcs1_vrfy.c".} + {.compile: bearRsaPath & "rsa_i31_priv.c".} + {.compile: bearRsaPath & "rsa_i31_pub.c".} + {.compile: bearRsaPath & "rsa_oaep_pad.c".} + {.compile: bearRsaPath & "rsa_oaep_unpad.c".} + {.compile: bearRsaPath & "rsa_pkcs1_sig_pad.c".} + {.compile: bearRsaPath & "rsa_pkcs1_sig_unpad.c".} + {.compile: bearRsaPath & "rsa_ssl_decrypt.c".} + {.compile: bearRsaPath & "rsa_default_keygen.c".} + {.compile: bearRsaPath & "rsa_i31_modulus.c".} + {.compile: bearRsaPath & "rsa_i31_privexp.c".} + {.compile: bearRsaPath & "rsa_i31_pubexp.c".} + {.compile: bearRsaPath & "rsa_default_modulus.c".} + {.compile: bearRsaPath & "rsa_default_privexp.c".} + {.compile: bearRsaPath & "rsa_default_pubexp.c".} + + ## Elliptic Curve sources + {.compile: bearEcPath & "ec_all_m31.c".} + {.compile: bearEcPath & "ec_default.c".} + {.compile: bearEcPath & "ec_keygen.c".} + {.compile: bearEcPath & "ec_p256_m31.c".} + {.compile: bearEcPath & "ec_prime_i31.c".} + {.compile: bearEcPath & "ec_pubkey.c".} + {.compile: bearEcPath & "ec_secp256r1.c".} + {.compile: bearEcPath & "ec_secp384r1.c".} + {.compile: bearEcPath & "ec_secp521r1.c".} + {.compile: bearEcPath & "ecdsa_i31_bits.c".} + {.compile: bearEcPath & "ecdsa_i31_sign_raw.c".} + {.compile: bearEcPath & "ecdsa_i31_sign_asn1.c".} + {.compile: bearEcPath & "ecdsa_i31_vrfy_asn1.c".} + {.compile: bearEcPath & "ecdsa_i31_vrfy_raw.c".} + {.compile: bearEcPath & "ecdsa_rta.c".} + {.compile: bearEcPath & "ecdsa_atr.c".} + +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 + + BR_EC_KBUF_PRIV_MAX_SIZE* = 72 + 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 + desc* {.importc: "desc".}: uint32 + init* {.importc: "init".}: proc (ctx: ptr ptr BrHashClass) {.cdecl.} + update* {.importc: "update".}: proc (ctx: ptr ptr BrHashClass, + data: pointer, len: int) {.cdecl.} + output* {.importc: "out".}: proc (ctx: ptr ptr BrHashClass, + dst: pointer) {.cdecl.} + state* {.importc: "state".}: proc (ctx: ptr ptr BrHashClass, + dst: pointer): uint64 {.cdecl.} + setState* {.importc: "set_state".}: proc (ctx: ptr ptr BrHashClass, + stb: pointer, + count: uint64) {.cdecl.} + + BrMd5Context* {.importc: "br_md5_context", + header: "bearssl_hash.h", bycopy.} = object + vtable* {.importc: "vtable".}: ptr BrHashClass + buf* {.importc: "buf".}: array[64, cuchar] + count* {.importc: "count".}: uint64 + val* {.importc: "val".}: array[4, uint32] + + BrMd5sha1Context* {.importc: "br_md5sha1_context", + header: "bearssl_hash.h", bycopy.} = object + vtable* {.importc: "vtable".}: ptr BrHashClass + buf* {.importc: "buf".}: array[64, cuchar] + count* {.importc: "count".}: uint64 + valMd5* {.importc: "val_md5".}: array[4, uint32] + valSha1* {.importc: "val_sha1".}: array[5, uint32] + + Sha1Context* {.importc: "br_sha1_context", + header: "bearssl_hash.h", bycopy.} = object + vtable* {.importc: "vtable".}: ptr BrHashClass + buf* {.importc: "buf".}: array[64, cuchar] + count* {.importc: "count".}: uint64 + val* {.importc: "val".}: array[5, uint32] + + BrSha512Context* = BrSha384Context + BrSha384Context* {.importc: "br_sha384_context", + header: "bearssl_hash.h", bycopy.} = object + vtable* {.importc: "vtable".}: ptr BrHashClass + buf* {.importc: "buf".}: array[128, cuchar] + count* {.importc: "count".}: uint64 + val* {.importc: "val".}: array[8, uint64] + + BrSha256Context* = BrSha224Context + BrSha224Context* {.importc: "br_sha224_context", + header: "bearssl_hash.h", bycopy.} = object + vtable* {.importc: "vtable".}: ptr BrHashClass + buf* {.importc: "buf".}: array[64, cuchar] + count* {.importc: "count".}: uint64 + val* {.importc: "val".}: array[8, uint32] + + BrHashCompatContext* {.importc: "br_hash_compat_context", + header: "bearssl_hash.h", bycopy.} = object {.union.} + vtable* {.importc: "vtable".}: ptr BrHashClass + md5* {.importc: "md5".}: BrMd5Context + sha1* {.importc: "sha1".}: Sha1Context + sha224* {.importc: "sha224".}: BrSha224Context + sha256* {.importc: "sha256".}: BrSha256Context + sha384* {.importc: "sha384".}: BrSha384Context + sha512* {.importc: "sha512".}: BrSha512Context + md5sha1* {.importc: "md5sha1".}: BrMd5sha1Context + + BrPrngClass* {.importc: "br_prng_class", + header: "bearssl_rand.h", bycopy.} = object + contextSize* {.importc: "context_size".}: int + init* {.importc: "init".}: proc (ctx: ptr ptr BrPrngClass, params: pointer, + seed: pointer, seedLen: int) {.cdecl.} + generate* {.importc: "generate".}: proc (ctx: ptr ptr BrPrngClass, + output: pointer, + length: int) {.cdecl.} + update* {.importc: "update".}: proc (ctx: ptr ptr BrPrngClass, + seed: pointer, seedLen: int) {.cdecl.} + + BrHmacDrbgContext* {.importc: "br_hmac_drbg_context", + header: "bearssl_rand.h", bycopy.} = object + vtable* {.importc: "vtable".}: ptr BrPrngClass + k* {.importc: "K".}: array[64, cuchar] + v* {.importc: "V".}: array[64, cuchar] + digestClass* {.importc: "digest_class".}: ptr BrHashClass + + BrRsaPublicKey* {.importc: "br_rsa_public_key", + header: "bearssl_rsa.h", bycopy.} = object + n* {.importc: "n".}: ptr cuchar + nlen* {.importc: "nlen".}: int + e* {.importc: "e".}: ptr cuchar + elen* {.importc: "elen".}: int + + BrRsaPrivateKey* {.importc: "br_rsa_private_key", + header: "bearssl_rsa.h", bycopy.} = object + nBitlen* {.importc: "n_bitlen".}: uint32 + p* {.importc: "p".}: ptr cuchar + plen* {.importc: "plen".}: int + q* {.importc: "q".}: ptr cuchar + qlen* {.importc: "qlen".}: int + dp* {.importc: "dp".}: ptr cuchar + dplen* {.importc: "dplen".}: int + dq* {.importc: "dq".}: ptr cuchar + dqlen* {.importc: "dqlen".}: int + iq* {.importc: "iq".}: ptr cuchar + iqlen* {.importc: "iqlen".}: int + + BrEcPublicKey* {.importc: "br_ec_public_key", header: "bearssl_ec.h", + bycopy.} = object + curve* {.importc: "curve".}: cint + q* {.importc: "q".}: ptr cuchar + qlen* {.importc: "qlen".}: int + + BrEcPrivateKey* {.importc: "br_ec_private_key", header: "bearssl_ec.h", + bycopy.} = object + curve* {.importc: "curve".}: cint + 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 + generator* {.importc: "generator".}: proc (curve: cint, + length: ptr int): ptr cuchar {.cdecl.} + order* {.importc: "order".}: proc (curve: cint, + length: ptr int): ptr cuchar {.cdecl.} + xoff* {.importc: "xoff".}: proc (curve: cint, + length: ptr int): int {.cdecl.} + mul* {.importc: "mul".}: proc (g: ptr cuchar, glen: int, + x: ptr cuchar, xlen: int, + curve: cint): uint32 {.cdecl.} + mulgen* {.importc: "mulgen".}: proc (r: ptr cuchar, + x: ptr cuchar, xlen: int, + curve: cint): int {.cdecl.} + muladd* {.importc: "muladd".}: proc (a: ptr cuchar, b: ptr cuchar, + length: int, x: ptr cuchar, xlen: int, + y: ptr cuchar, ylen: int, + curve: cint): uint32 {.cdecl.} + + BrPrngSeeder* = proc (ctx: ptr ptr BrPrngClass): cint {.cdecl.} + BrRsaKeygen* = proc (ctx: ptr ptr BrPrngClass, + sk: ptr BrRsaPrivateKey, bufsec: ptr byte, + pk: ptr BrRsaPublicKey, bufpub: ptr byte, + size: cuint, pubexp: uint32): uint32 {.cdecl.} + BrRsaComputeModulus* = proc (n: pointer, + sk: ptr BrRsaPrivateKey): int {.cdecl.} + BrRsaComputePubexp* = proc (sk: ptr BrRsaPrivateKey): uint32 {.cdecl.} + BrRsaComputePrivexp* = proc (d: pointer, + sk: ptr BrRsaPrivateKey, + pubexp: uint32): int {.cdecl.} + BrRsaPkcs1Verify* = proc (x: ptr cuchar, xlen: int, + hash_oid: ptr cuchar, hash_len: int, + pk: ptr BrRsaPublicKey, + 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".} + +proc brPrngSeederSystem*(name: cstringArray): BrPrngSeeder {.cdecl, + importc: "br_prng_seeder_system", header: "bearssl_rand.h".} + +proc brHmacDrbgInit*(ctx: ptr BrHmacDrbgContext, digestClass: ptr BrHashClass, + seed: pointer, seedLen: int) {. + cdecl, importc: "br_hmac_drbg_init", header: "bearssl_rand.h".} + +proc brRsaKeygenGetDefault*(): BrRsaKeygen {. + cdecl, importc: "br_rsa_keygen_get_default", header: "bearssl_rsa.h".} + +proc brRsaComputeModulusGetDefault*(): BrRsaComputeModulus {. + cdecl, importc: "br_rsa_compute_modulus_get_default", + header: "bearssl_rsa.h".} + +proc brRsaComputePubexpGetDefault*(): BrRsaComputePubexp {. + cdecl, importc: "br_rsa_compute_pubexp_get_default", + header: "bearssl_rsa.h".} + +proc brRsaComputePrivexpGetDefault*(): BrRsaComputePrivexp {. + cdecl, importc: "br_rsa_compute_privexp_get_default", + header: "bearssl_rsa.h".} + +proc brEcGetDefault*(): ptr BrEcImplementation {. + cdecl, importc: "br_ec_get_default", header: "bearssl_ec.h".} + +proc brEcKeygen*(ctx: ptr ptr BrPrngClass, impl: ptr BrEcImplementation, + sk: ptr BrEcPrivateKey, keybuf: ptr byte, + curve: cint): int {.cdecl, + importc: "br_ec_keygen", header: "bearssl_ec.h".} + +proc brEcComputePublicKey*(impl: ptr BrEcImplementation, pk: ptr BrEcPublicKey, + kbuf: ptr byte, sk: ptr BrEcPrivateKey): int {. + cdecl, importc: "br_ec_compute_pub", header: "bearssl_ec.h".} + +proc brEcdsaSignRaw*(impl: ptr BrEcImplementation, hf: ptr BrHashClass, + value: pointer, sk: ptr BrEcPrivateKey, + sig: pointer): int {. + cdecl, importc: "br_ecdsa_i31_sign_raw", header: "bearssl_ec.h".} + +proc brEcdsaVerifyRaw*(impl: ptr BrEcImplementation, hash: pointer, + hashlen: int, pk: ptr BrEcPublicKey, sig: pointer, + siglen: int): uint32 {. + cdecl, importc: "br_ecdsa_i31_vrfy_raw", 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", + header: "bearssl_hash.h".}: BrHashClass +var sha512Vtable* {.importc: "br_sha512_vtable", + header: "bearssl_hash.h".}: BrHashClass + +template brRsaPrivateKeyBufferSize*(size: int): int = + # BR_RSA_KBUF_PRIV_SIZE(size) + (5 * ((size + 15) shr 4)) + +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 new file mode 100644 index 000000000..3901eaca3 --- /dev/null +++ b/libp2p/crypto/ecnist.nim @@ -0,0 +1,587 @@ +## 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 common +import nimcrypto/utils + +const + PubKey256Length* = 65 + PubKey384Length* = 97 + PubKey521Length* = 133 + SecKey256Length* = 32 + SecKey384Length* = 48 + SecKey521Length* = 66 + Sig256Length* = 64 + Sig384Length* = 96 + Sig521Length* = 132 + +type + EcPrivateKey* = ref object + buffer*: seq[byte] + key*: BrEcPrivateKey + + EcPublicKey* = ref object + buffer*: seq[byte] + key*: BrEcPublicKey + + EcKeyPair* = object + seckey*: EcPrivateKey + pubkey*: EcPublicKey + + EcSignature* = ref object + buffer*: seq[byte] + curve*: cint + + EcCurveKind* = enum + Secp256r1 = BR_EC_SECP256R1, + Secp384r1 = BR_EC_SECP384R1, + Secp521r1 = BR_EC_SECP521R1 + + EcPKI* = EcPrivateKey | EcPublicKey | EcSignature + + EcError* = object of Exception + EcKeyIncorrectError* = object of EcError + EcRngError* = object of EcError + EcPublicKeyError* = object of EcError + EcSignatureError = object of EcError + +const + EcSupportedCurvesCint* = {cint(Secp256r1), cint(Secp384r1), cint(Secp521r1)} + +proc `-`(x: uint32): uint32 {.inline.} = + result = (0xFFFF_FFFF'u32 - x) + 1'u32 + +proc GT(x, y: uint32): uint32 {.inline.} = + var z = cast[uint32](y - x) + result = (z xor ((x xor y) and (x xor z))) shr 31 + +proc CMP(x, y: uint32): int32 {.inline.} = + cast[int32](GT(x, y)) or -(cast[int32](GT(y, x))) + +proc EQ0(x: int32): uint32 {.inline.} = + var q = cast[uint32](x) + result = not(q or -q) shr 31 + +proc NEQ(x, y: uint32): uint32 {.inline.} = + var q = cast[uint32](x xor y) + result = ((q or -q) shr 31) + +proc LT0(x: int32): uint32 {.inline.} = + result = cast[uint32](x) shr 31 + +proc checkScalar(scalar: openarray[byte], curve: cint): uint32 = + ## Return ``1`` if all of the following hold: + ## - len(``scalar``) <= ``orderlen`` + ## - ``scalar`` != 0 + ## - ``scalar`` is lower than the curve ``order``. + ## + ## Otherwise, return ``0``. + var impl = brEcGetDefault() + var orderlen = 0 + var order = cast[ptr UncheckedArray[byte]](impl.order(curve, addr orderlen)) + + var z = 0'u32 + var c = 0'i32 + for u in scalar: + z = z or u + if len(scalar) == orderlen: + for i in 0.. uint(len(pubkey.buffer)): + result = -1 + else: + result = cast[int](o) + +proc getOffset(seckey: EcPrivateKey): int {.inline.} = + let o = cast[uint](seckey.key.x) - cast[uint](unsafeAddr seckey.buffer[0]) + if o + cast[uint](seckey.key.xlen) > uint(len(seckey.buffer)): + result = -1 + else: + result = cast[int](o) + +proc copyKey(dest: var openarray[byte], seckey: EcPrivateKey): bool {.inline.} = + let length = seckey.key.xlen + if length > 0: + if len(dest) >= length: + let offset = getOffset(seckey) + if offset >= 0: + copyMem(addr dest[0], unsafeAddr seckey.buffer[offset], length - offset) + result = true + +proc copyKey(dest: var openarray[byte], pubkey: EcPublicKey): bool {.inline.} = + let length = pubkey.key.qlen + if length > 0: + if len(dest) >= length: + let offset = getOffset(pubkey) + if offset >= 0: + copyMem(addr dest[0], unsafeAddr pubkey.buffer[offset], length - offset) + result = true + +template getSignatureLength*(curve: EcCurveKind): int = + case curve + of Secp256r1: + Sig256Length + of Secp384r1: + Sig384Length + of Secp521r1: + Sig521Length + +template getPublicKeyLength*(curve: EcCurveKind): int = + case curve + of Secp256r1: + PubKey256Length + of Secp384r1: + PubKey384Length + of Secp521r1: + PubKey521Length + +template getPrivateKeyLength*(curve: EcCurveKind): int = + case curve + of Secp256r1: + SecKey256Length + of Secp384r1: + SecKey384Length + of Secp521r1: + SecKey521Length + +proc copy*[T: EcPKI](dst: var T, src: T): bool = + ## Copy EC private key, public key or scalar ``src`` to ``dst``. + ## + ## Returns ``true`` on success, ``false`` otherwise. + dst = new T + when T is EcPrivateKey: + let length = src.key.xlen + if length > 0 and len(src.buffer) > 0: + let offset = getOffset(src) + if offset >= 0: + dst.buffer = src.buffer + dst.key.curve = src.key.curve + dst.key.xlen = length + dst.key.x = cast[ptr cuchar](addr dst.buffer[offset]) + result = true + elif T is EcPublicKey: + let length = src.key.qlen + if length > 0 and len(src.buffer) > 0: + let offset = getOffset(src) + if offset >= 0: + dst.buffer = src.buffer + dst.key.curve = src.key.curve + dst.key.qlen = length + dst.key.q = cast[ptr cuchar](addr dst.buffer[offset]) + result = true + else: + 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.} = + ## Returns copy of EC private key, public key or scalar ``src``. + if not copy(result, src): + raise newException(EcKeyIncorrectError, "Incorrect key or signature") + +proc clear*[T: EcPKI|EcKeyPair](pki: var T) = + ## Wipe and clear EC private key, public key or scalar object. + when T is EcPrivateKey: + burnMem(pki.buffer) + pki.buffer.setLen(0) + pki.key.x = nil + pki.key.xlen = 0 + pki.key.curve = 0 + elif T is EcPublicKey: + burnMem(pki.buffer) + pki.buffer.setLen(0) + pki.key.q = nil + pki.key.qlen = 0 + pki.key.curve = 0 + elif T is EcSignature: + burnMem(pki.buffer) + pki.buffer.setLen(0) + pki.curve = 0 + else: + burnMem(pki.seckey.buffer) + burnMem(pki.pubkey.buffer) + pki.seckey.buffer.setLen(0) + pki.pubkey.buffer.setLen(0) + pki.seckey.key.x = nil + pki.seckey.key.xlen = 0 + pki.seckey.key.curve = 0 + pki.pubkey.key.q = nil + pki.pubkey.key.qlen = 0 + pki.pubkey.key.curve = 0 + +proc random*(t: typedesc[EcPrivateKey], kind: EcCurveKind): EcPrivateKey = + ## Generate new random EC private key using BearSSL's HMAC-SHA256-DRBG + ## algorithm. + ## + ## ``kind`` elliptic curve kind of your choice (secp256r1, secp384r1 or + ## secp521r1). + var rng: BrHmacDrbgContext + var seeder = brPrngSeederSystem(nil) + brHmacDrbgInit(addr rng, addr sha256Vtable, nil, 0) + if seeder(addr rng.vtable) == 0: + raise newException(ValueError, "Could not seed RNG") + var ecimp = brEcGetDefault() + result = new EcPrivateKey + result.buffer = newSeq[byte](BR_EC_KBUF_PRIV_MAX_SIZE) + if brEcKeygen(addr rng.vtable, ecimp, + addr result.key, addr result.buffer[0], + cast[cint](kind)) == 0: + raise newException(ValueError, "Could not generate private key") + +proc getKey*(seckey: EcPrivateKey): EcPublicKey = + ## Calculate and return EC public key from private key ``seckey``. + var ecimp = brEcGetDefault() + if seckey.key.curve in EcSupportedCurvesCint: + var length = getPublicKeyLength(cast[EcCurveKind](seckey.key.curve)) + result = new EcPublicKey + result.buffer = newSeq[byte](length) + if brEcComputePublicKey(ecimp, addr result.key, + addr result.buffer[0], unsafeAddr seckey.key) == 0: + raise newException(EcKeyIncorrectError, "Could not calculate public key") + else: + raise newException(EcKeyIncorrectError, "Incorrect private key") + +proc random*(t: typedesc[EcKeyPair], kind: EcCurveKind): EcKeyPair {.inline.} = + ## Generate new random EC private and public keypair using BearSSL's + ## HMAC-SHA256-DRBG algorithm. + ## + ## ``kind`` elliptic curve kind of your choice (secp256r1, secp384r1 or + ## secp521r1). + result.seckey = EcPrivateKey.random(kind) + result.pubkey = result.seckey.getKey() + +proc `$`*(seckey: EcPrivateKey): string = + ## Return string representation of EC private key. + if seckey.key.curve == 0 or seckey.key.xlen == 0 or len(seckey.buffer) == 0: + result = "Empty key" + else: + if seckey.key.curve notin EcSupportedCurvesCint: + result = "Unknown key" + else: + let offset = seckey.getOffset() + if offset < 0: + result = "Corrupted key" + else: + let e = offset + cast[int](seckey.key.xlen) - 1 + result = toHex(seckey.buffer.toOpenArray(offset, e)) + +proc `$`*(pubkey: EcPublicKey): string = + ## Return string representation of EC public key. + if pubkey.key.curve == 0 or pubkey.key.qlen == 0 or len(pubkey.buffer) == 0: + result = "Empty key" + else: + if pubkey.key.curve notin EcSupportedCurvesCint: + result = "Unknown key" + else: + let offset = pubkey.getOffset() + if offset < 0: + result = "Corrupted key" + else: + let e = offset + cast[int](pubkey.key.qlen) - 1 + result = toHex(pubkey.buffer.toOpenArray(offset, e)) + +proc `$`*(sig: EcSignature): string = + ## Return hexadecimal string representation of EC signature. + if sig.curve == 0 or len(sig.buffer) == 0: + result = "Empty signature" + else: + if sig.curve notin EcSupportedCurvesCint: + result = "Unknown signature" + else: + result = toHex(sig.buffer) + +proc toBytes*(seckey: EcPrivateKey, data: var openarray[byte]): bool = + ## Serialize EC private key ``seckey`` to raw binary form and store it to + ## ``data``. + ## + ## If ``seckey`` curve is ``Secp256r1`` length of ``data`` array must be at + ## least ``SecKey256Length``. + ## + ## If ``seckey`` curve is ``Secp384r1`` length of ``data`` array must be at + ## least ``SecKey384Length``. + ## + ## If ``seckey`` curve is ``Secp521r1`` length of ``data`` array must be at + ## least ``SecKey521Length``. + ## + ## Procedure returns ``true`` if serialization successfull, ``false`` + ## otherwise. + if seckey.key.curve in EcSupportedCurvesCint: + if copyKey(data, seckey): + result = true + +proc toBytes*(pubkey: EcPublicKey, data: var openarray[byte]): bool = + ## Serialize EC public key ``pubkey`` to raw binary form and store it to + ## ``data``. + ## + ## If ``pubkey`` curve is ``Secp256r1`` length of ``data`` array must be at + ## least ``PubKey256Length``. + ## + ## If ``pubkey`` curve is ``Secp384r1`` length of ``data`` array must be at + ## least ``PubKey384Length``. + ## + ## If ``pubkey`` curve is ``Secp521r1`` length of ``data`` array must be at + ## least ``PubKey521Length``. + ## + ## Procedure returns ``true`` if serialization successfull, ``false`` + ## otherwise. + if pubkey.key.curve in EcSupportedCurvesCint: + if copyKey(data, pubkey): + result = true + +proc getBytes*(seckey: EcPrivateKey): seq[byte] = + ## Serialize EC private key ``seckey`` to raw binary form and return it. + if seckey.key.curve in EcSupportedCurvesCint: + result = newSeq[byte](seckey.key.xlen) + discard toBytes(seckey, result) + else: + raise newException(EcKeyIncorrectError, "Incorrect private key") + +proc getBytes*(pubkey: EcPublicKey): seq[byte] = + ## Serialize EC public key ``pubkey`` to raw binary form and return it. + if pubkey.key.curve in EcSupportedCurvesCint: + result = newSeq[byte](pubkey.key.qlen) + discard toBytes(pubkey, result) + else: + raise newException(EcKeyIncorrectError, "Incorrect public key") + +proc `==`*(pubkey1, pubkey2: EcPublicKey): bool = + ## Returns ``true`` if both keys ``pubkey1`` and ``pubkey2`` are equal. + if pubkey1.key.curve != pubkey2.key.curve: + return false + if pubkey1.key.qlen != pubkey2.key.qlen: + return false + let op1 = pubkey1.getOffset() + let op2 = pubkey2.getOffset() + if op1 == -1 or op2 == -1: + return false + result = equalMem(unsafeAddr pubkey1.buffer[op1], + unsafeAddr pubkey2.buffer[op2], pubkey1.key.qlen) + +proc `==`*(seckey1, seckey2: EcPrivateKey): bool = + ## Returns ``true`` if both keys ``seckey1`` and ``seckey2`` are equal. + if seckey1.key.curve != seckey2.key.curve: + return false + if seckey1.key.xlen != seckey2.key.xlen: + return false + let op1 = seckey1.getOffset() + let op2 = seckey2.getOffset() + if op1 == -1 or op2 == -1: + return false + result = equalMem(unsafeAddr seckey1.buffer[op1], + unsafeAddr seckey2.buffer[op2], seckey1.key.xlen) + +proc `==`*(sig1, sig2: EcSignature): bool = + ## Return ``true`` if both signatures ``sig1`` and ``sig2`` are equal. + if sig1.curve != sig2.curve: + return false + result = (sig1.buffer == sig2.buffer) + +proc init*(key: var EcPrivateKey, data: openarray[byte]): bool = + ## Initialize EC `private key` or `scalar` ``key`` from raw binary + ## representation ``data``. + ## + ## Length of ``data`` array must be ``SecKey256Length``, ``SecKey384Length`` + ## or ``SecKey521Length``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + var curve: cint + if len(data) == SecKey256Length: + curve = cast[cint](Secp256r1) + result = true + elif len(data) == SecKey384Length: + curve = cast[cint](Secp384r1) + result = true + elif len(data) == SecKey521Length: + curve = cast[cint](Secp521r1) + result = true + if result: + result = false + if checkScalar(data, curve) == 1'u32: + let length = len(data) + key = new EcPrivateKey + key.buffer = newSeq[byte](length) + copyMem(addr key.buffer[0], unsafeAddr data[0], length) + key.key.x = cast[ptr cuchar](addr key.buffer[0]) + key.key.xlen = length + key.key.curve = curve + result = true + +proc init*(pubkey: var EcPublicKey, data: openarray[byte]): bool = + ## Initialize EC public key ``pubkey`` from raw binary representation + ## ``data``. + ## + ## Length of ``data`` array must be ``PubKey256Length``, ``PubKey384Length`` + ## or ``PubKey521Length``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + var curve: cint + if len(data) > 0: + if data[0] == 0x04'u8: + if len(data) == PubKey256Length: + curve = cast[cint](Secp256r1) + result = true + elif len(data) == PubKey384Length: + curve = cast[cint](Secp384r1) + result = true + elif len(data) == PubKey521Length: + curve = cast[cint](Secp521r1) + result = true + if result: + result = false + if checkPublic(data, curve) != 0: + let length = len(data) + pubkey = new EcPublicKey + pubkey.buffer = newSeq[byte](length) + copyMem(addr pubkey.buffer[0], unsafeAddr data[0], length) + pubkey.key.q = cast[ptr cuchar](addr pubkey.buffer[0]) + pubkey.key.qlen = length + pubkey.key.curve = curve + result = true + +proc init*(sig: var EcSignature, data: openarray[byte]): bool = + ## Initialize EC signature ``sig`` from raw binary representation ``data``. + ## + ## Length of ``data`` array must be ``Sig256Length``, ``Sig384Length`` + ## or ``Sig521Length``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + var curve: cint + if len(data) > 0: + if len(data) == Sig256Length: + curve = cast[cint](Secp256r1) + result = true + elif len(data) == Sig384Length: + curve = cast[cint](Secp384r1) + result = true + elif len(data) == Sig521Length: + curve = cast[cint](Secp521r1) + result = true + if result: + sig = new EcSignature + sig.curve = curve + sig.buffer = @data + result = true + +proc init*[T: EcPKI](sospk: var T, data: string): bool {.inline.} = + ## Initialize EC `private key`, `public key` or `scalar` ``sospk`` from + ## hexadecimal string representation ``data``. + ## + ## Procedure returns ``true`` on success, ``false`` otherwise. + result = sospk.init(fromHex(data)) + +proc init*(t: typedesc[EcPrivateKey], data: openarray[byte]): EcPrivateKey = + ## Initialize EC private key from raw binary representation ``data`` and + ## return constructed object. + if not result.init(data): + raise newException(EcKeyIncorrectError, "Incorrect private key") + +proc init*(t: typedesc[EcPublicKey], data: openarray[byte]): EcPublicKey = + ## Initialize EC public key from raw binary representation ``data`` and + ## return constructed object. + if not result.init(data): + raise newException(EcKeyIncorrectError, "Incorrect public key") + +proc init*(t: typedesc[EcSignature], data: openarray[byte]): EcSignature = + ## Initialize EC signature from raw binary representation ``data`` and + ## return constructed object. + if not result.init(data): + raise newException(EcKeyIncorrectError, "Incorrect signature") + +proc init*[T: EcPKI](t: typedesc[T], data: string): T {.inline.} = + ## Initialize EC `private key`, `public key` or `scalar` from hexadecimal + ## string representation ``data`` and return constructed object. + result = t.init(fromHex(data)) + +proc scalarMul*(pub: EcPublicKey, sec: EcPrivateKey): EcPublicKey = + ## Return scalar multiplication of ``pub`` and ``sec``. + ## + ## Returns point in curve as ``pub * sec`` or ``nil`` otherwise. + var impl = brEcGetDefault() + if sec.key.curve in EcSupportedCurvesCint: + if pub.key.curve == sec.key.curve: + var key = new EcPublicKey + if key.copy(pub): + var slength = cint(0) + let poffset = key.getOffset() + let soffset = sec.getOffset() + if poffset >= 0 and soffset >= 0: + let res = impl.mul(cast[ptr cuchar](addr key.buffer[poffset]), + key.key.qlen, + cast[ptr cuchar](unsafeAddr sec.buffer[soffset]), + sec.key.xlen, + key.key.curve) + if res != 0: + result = key + +proc sign*[T: byte|char](seckey: EcPrivateKey, + message: openarray[T]): EcSignature = + ## Get ECDSA signature of data ``message`` using private key ``seckey``. + var hc: BrHashCompatContext + var hash: array[32, byte] + var impl = brEcGetDefault() + if len(message) > 0: + if seckey.key.curve in EcSupportedCurvesCint: + let length = getSignatureLength(cast[EcCurveKind](seckey.key.curve)) + result = new EcSignature + result.curve = seckey.key.curve + result.buffer = newSeq[byte](length) + var kv = addr sha256Vtable + kv.init(addr hc.vtable) + kv.update(addr hc.vtable, unsafeAddr message[0], len(message)) + kv.output(addr hc.vtable, addr hash[0]) + let res = brEcdsaSignRaw(impl, kv, addr hash[0], addr seckey.key, + addr result.buffer[0]) + if res != getSignatureLength(cast[EcCurveKind](result.curve)): + raise newException(EcSignatureError, "Could not make signature") + # Clear context with initial value + kv.init(addr hc.vtable) + else: + raise newException(EcKeyIncorrectError, "Incorrect private key") + else: + raise newException(EcSignatureError, "Empty message") + +proc verify*[T: byte|char](sig: EcSignature, message: openarray[T], + pubkey: EcPublicKey): bool {.inline.} = + ## Verify ECDSA signature ``sig`` using public key ``pubkey`` and data + ## ``message``. + ## + ## Return ``true`` if message verification succeeded, ``false`` if + ## verification failed. + var hc: BrHashCompatContext + var hash: array[32, byte] + var impl = brEcGetDefault() + if len(message) > 0: + if pubkey.key.curve in EcSupportedCurvesCint and + pubkey.key.curve == sig.curve: + var kv = addr sha256Vtable + kv.init(addr hc.vtable) + kv.update(addr hc.vtable, unsafeAddr message[0], len(message)) + kv.output(addr hc.vtable, addr hash[0]) + let res = brEcdsaVerifyRaw(impl, addr hash[0], 32, unsafeAddr pubkey.key, + addr sig.buffer[0], len(sig.buffer)) + # Clear context with initial value + kv.init(addr hc.vtable) + result = (res == 1) diff --git a/libp2p/crypto/rsa.nim b/libp2p/crypto/rsa.nim new file mode 100644 index 000000000..5567687b7 --- /dev/null +++ b/libp2p/crypto/rsa.nim @@ -0,0 +1,342 @@ +import strutils, hexdump, nimcrypto/utils +import common + +const + DefaultPublicExponent = 3'u32 + +type + RsaPrivateKey* = ref object + buffer*: seq[byte] + key*: BrRsaPrivateKey + + RsaPublicKey* = ref object + buffer*: seq[byte] + key*: BrRsaPublicKey + + RsaKeyPair* = object + seckey*: RsaPrivateKey + pubkey*: RsaPublicKey + + RsaModulus* = seq[byte] + RsaPublicExponent* = uint32 + RsaPrivateExponent* = seq[byte] + + RsaError = object of Exception + RsaRngError = object of RsaError + RsaGenError = object of RsaError + RsaIncorrectKeyError = 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") + +proc random*(t: typedesc[RsaPrivateKey], bits: int): RsaPrivateKey = + ## Generate new random RSA private 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(RsaRngError, "Could not seed RNG") + keygen = brRsaKeygenGetDefault() + result = new RsaPrivateKey + result.buffer = newSeq[byte](brRsaPrivateKeyBufferSize(bits)) + if keygen(addr rng.vtable, + addr result.seckey.key, addr result.seckey.buffer[0], + nil, nil, + cuint(bits), DefaultPublicExponent) == 0: + raise newException(RsaGenError, "Could not create private key") + +proc modulus*(seckey: RsaPrivateKey): RsaModulus = + ## Calculate and return RSA modulus. + let bytelen = (seckey.key.nBitlen + 7) shr 3 + let compute = brRsaComputeModulusGetDefault() + result = newSeq[byte](bytelen) + let length = compute(addr result[0], unsafeAddr seckey.key) + assert(length == len(result), $length) + +proc pubexp*(seckey: RsaPrivateKey): RsaPublicExponent = + ## Calculate and return RSA public exponent. + let compute = brRsaComputePubexpGetDefault() + result = compute(unsafeAddr seckey.key) + +proc privexp*(seckey: RsaPrivateKey, + pubexp: RsaPublicExponent): RsaPrivateExponent = + ## Calculate and return RSA private exponent. + let bytelen = (seckey.key.nBitlen + 7) shr 3 + let compute = brRsaComputePrivexpGetDefault() + result = newSeq[byte](bytelen) + let length = compute(addr result[0], unsafeAddr seckey.key, pubexp) + assert(length == len(result), $length) + +proc getKey*(seckey: RsaPrivateKey): RsaPublicKey = + ## Calculate and return RSA public key from RSA private key ``seckey``. + var ebuf: array[4, byte] + var modulus = seckey.modulus() + var pubexp = seckey.pubexp() + let modlen = len(modulus) + result = new RsaPublicKey + result.buffer = newSeq[byte](modlen + sizeof(ebuf)) + 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)) + + + +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 brAsn1EncodeBitString(data: var openarray[byte], bytesize: int): int = + result = 1 + brAsn1EncodeLength(nil, bytesize + 1) + 1 + if len(data) >= result: + var offset = 1 + data[0] = 0x03'u8 + offset += brAsn1EncodeLength(addr data[offset], bytesize + 1) + data[offset] = 0'u8 + +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" + + 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) + + var res = brEncodePublicRsaRawDer(pubkey, result.toOpenArray(offset, + len(result) - 1)) + echo "AFTER RAWDER" + echo dumpHex(result) + offset += res + + +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) + +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) + +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) + +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 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) + +proc unmarshalBin*(rt: typedesc[RsaPrivateKey], + data: openarray[byte]): RsaPrivateKey {.inline.} = + let res = tryUnmarshalBin(result, data) + if res != X509Status.OK: + raise newException(ValueError, $res) + +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 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) + 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) + +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 + 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") + +when isMainModule: + var pk = RsaKeyPair.random() +