123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- module secured.ecc;
- import std.stdio;
- import std.string;
- import deimos.openssl.evp;
- import deimos.openssl.rand;
- import deimos.openssl.pem;
- import deimos.openssl.bio;
- import secured.hash;
- import secured.kdf;
- import secured.random;
- import secured.util;
- public enum EccCurve
- {
- P256,
- P384,
- P521,
- }
- @trusted:
- public class EllipticCurve
- {
- private EVP_PKEY_CTX* paramsctx;
- private EVP_PKEY* params;
- private EVP_PKEY_CTX* keyctx;
- private EVP_PKEY* key;
- private bool _hasPrivateKey;
- public @property bool hasPrivateKey() { return _hasPrivateKey; }
- public this(EccCurve curve = EccCurve.P384)
- {
- //Reseed the OpenSSL RNG every time we create a new ECC Key to ensure that the result is truely random in threading/forking scenarios.
- ubyte[] seedbuf = random(32);
- RAND_seed(seedbuf.ptr, cast(int)seedbuf.length);
- //Generate the key parameters
- paramsctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, null);
- if (paramsctx is null) {
- throw new CryptographicException("Cannot get an OpenSSL public key context.");
- }
- if (EVP_PKEY_paramgen_init(paramsctx) < 1) {
- throw new CryptographicException("Cannot initialize the OpenSSL public key context.");
- }
- if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(paramsctx, getOpenSSLCurveId(curve)) < 1) {
- throw new CryptographicException("Cannot set the requested curve.");
- }
- if (EVP_PKEY_paramgen(paramsctx, ¶ms) < 1) {
- throw new CryptographicException("Unable to generate the key parameters.");
- }
- //Generate the public and private keys
- keyctx = EVP_PKEY_CTX_new(params, null);
- if (keyctx is null) {
- throw new CryptographicException("Cannot get an OpenSSL private key context.");
- }
- if (EVP_PKEY_keygen_init(keyctx) < 1) {
- throw new CryptographicException("Cannot initialize the OpenSSL private key context.");
- }
- if (EVP_PKEY_keygen(keyctx, &key) < 1) {
- throw new CryptographicException("Unable to generate the private key.");
- }
- _hasPrivateKey = true;
- }
- public this(string privateKey, string password)
- {
- //Reseed the OpenSSL RNG every time we load an existing ECC Key to ensure that the result is truely random in threading/forking scenarios.
- ubyte[] seedbuf = random(32);
- RAND_seed(seedbuf.ptr, cast(int)seedbuf.length);
- _hasPrivateKey = true;
- ubyte[] pk = cast(ubyte[])privateKey;
- BIO* bio = BIO_new_mem_buf(pk.ptr, cast(int)pk.length);
- if (password is null) {
- key = PEM_read_bio_PrivateKey(bio, null, null, null);
- } else {
- ubyte[] pwd = cast(ubyte[])password;
- pwd = pwd ~ '\0';
- key = PEM_read_bio_PrivateKey(bio, null, null, pwd.ptr);
- }
- BIO_free_all(bio);
- }
- public this(string publicKey)
- {
- //Reseed the OpenSSL RNG every time we load an existing ECC Key to ensure that the result is truely random in threading/forking scenarios.
- ubyte[] seedbuf = random(32);
- RAND_seed(seedbuf.ptr, cast(int)seedbuf.length);
- _hasPrivateKey = false;
- ubyte[] pk = cast(ubyte[])publicKey;
- BIO* bio = BIO_new_mem_buf(pk.ptr, cast(int)pk.length);
- key = PEM_read_bio_PUBKEY(bio, null, null, null);
- BIO_free_all(bio);
- }
- public ~this()
- {
- if (key !is null) {
- EVP_PKEY_free(key);
- }
- if (keyctx !is null) {
- EVP_PKEY_CTX_free(keyctx);
- }
- if (params !is null) {
- EVP_PKEY_free(params);
- }
- if (paramsctx !is null) {
- EVP_PKEY_CTX_free(paramsctx);
- }
- }
- public ubyte[] derive(string peerKey)
- {
- ubyte[] pk = cast(ubyte[])peerKey;
- BIO* bio = BIO_new_mem_buf(pk.ptr, cast(int)pk.length);
- EVP_PKEY* peer = PEM_read_bio_PUBKEY(bio, null, null, null);
- BIO_free_all(bio);
- //Initialize the key derivation context.
- EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(key, null);
- if (ctx is null) {
- throw new CryptographicException("Unable to create the key derivation context.");
- }
- if (EVP_PKEY_derive_init(ctx) <= 0) {
- throw new CryptographicException("Unable to initialize the key derivation context.");
- }
- if (EVP_PKEY_derive_set_peer(ctx, peer) <= 0) {
- throw new CryptographicException("Unable to set the peer key.");
- }
- //Derive the key
- size_t dklen = 0;
- if (EVP_PKEY_derive(ctx, null, &dklen) <= 0) {
- throw new CryptographicException("Unable to determine the length of the derived key.");
- }
- ubyte[] derivedKey = new ubyte[dklen];
- if (EVP_PKEY_derive(ctx, derivedKey.ptr, &dklen) <= 0) {
- throw new CryptographicException("Unable to determine the length of the derived key.");
- }
- return derivedKey;
- }
- public ubyte[] sign(ubyte[] data, bool useSha256 = false)
- {
- EVP_PKEY_CTX* pkeyctx = null;
- pkeyctx = EVP_PKEY_CTX_new(key, null);
- if (pkeyctx is null) {
- throw new CryptographicException("Unable to create the key signing context.");
- }
- scope(exit) {
- if (pkeyctx !is null) {
- EVP_PKEY_CTX_free(pkeyctx);
- }
- }
- if (EVP_PKEY_sign_init(pkeyctx) <= 0) {
- throw new CryptographicException("Unable to initialize the signing digest.");
- }
- if (EVP_PKEY_CTX_set_signature_md(pkeyctx, cast(void*)(!useSha256 ? EVP_sha384() : EVP_sha256())) <= 0) {
- throw new CryptographicException("Unable to set the signing digest.");
- }
- size_t signlen = 0;
- if (EVP_PKEY_sign(pkeyctx, null, &signlen, data.ptr, data.length) <= 0) {
- throw new CryptographicException("Unable to calculate signature length.");
- }
- ubyte[] sign = new ubyte[signlen];
- if (EVP_PKEY_sign(pkeyctx, sign.ptr, &signlen, data.ptr, data.length) <= 0) {
- throw new CryptographicException("Unable to calculate signature.");
- }
- return sign;
- }
- public bool verify(ubyte[] data, ubyte[] signature, bool useSha256 = false)
- {
- EVP_PKEY_CTX* pkeyctx = null;
- pkeyctx = EVP_PKEY_CTX_new(key, null);
- if (pkeyctx is null) {
- throw new CryptographicException("Unable to create the key signing context.");
- }
- scope(exit) {
- if (pkeyctx !is null) {
- EVP_PKEY_CTX_free(pkeyctx);
- }
- }
- if (EVP_PKEY_verify_init(pkeyctx) <= 0) {
- throw new CryptographicException("Unable to initialize the signing digest.");
- }
- if (EVP_PKEY_CTX_set_signature_md(pkeyctx, cast(void*)(!useSha256 ? EVP_sha384() : EVP_sha256())) <= 0) {
- throw new CryptographicException("Unable to set the signing digest.");
- }
- int ret = EVP_PKEY_verify(pkeyctx, data.ptr, cast(long)data.length, signature.ptr, cast(long)signature.length);
- return ret != 1;
- }
- public string getPublicKey()
- {
- BIO* bio = BIO_new(BIO_s_mem());
- PEM_write_bio_PUBKEY(bio, key);
- ubyte[] buffer = new ubyte[BIO_ctrl_pending(bio)];
- BIO_read(bio, buffer.ptr, cast(int)buffer.length);
- BIO_free_all(bio);
- return cast(string)buffer;
- }
- public string getPrivateKey(string password, bool use3Des = false)
- {
- if (!_hasPrivateKey) {
- return null;
- }
- BIO* bio = BIO_new(BIO_s_mem());
- if (password is null) {
- PEM_write_bio_PKCS8PrivateKey(bio, key, null, null, 0, null, null);
- } else {
- ubyte[] pwd = cast(ubyte[])password;
- pwd = pwd ~ '\0';
- PEM_write_bio_PKCS8PrivateKey(
- bio,
- key,
- !use3Des ? EVP_aes_256_cbc() : EVP_des_ede3_cbc(),
- null,
- 0,
- null,
- pwd.ptr);
- }
- if(BIO_ctrl_pending(bio) == 0) {
- throw new CryptographicException("No private key written.");
- }
- ubyte[] buffer = new ubyte[BIO_ctrl_pending(bio)];
- BIO_read(bio, buffer.ptr, cast(int)buffer.length);
- BIO_free_all(bio);
- return cast(string)buffer;
- }
- }
- unittest
- {
- import std.digest;
- writeln("Testing EllipticCurve Private Key Extraction/Recreation:");
- EllipticCurve eckey = new EllipticCurve();
- string pub = eckey.getPublicKey();
- writeln("Extracting No Password");
- string pkNoPwd = eckey.getPrivateKey(null);
- writeln("Extracting With Password");
- string pkPwd = eckey.getPrivateKey("Test Password");
- writeln("Private Key Without Password: ");
- writeln(pkNoPwd);
- writeln("Private Key With Password:");
- writeln(pkPwd);
- assert(pkNoPwd !is null);
- assert(pkPwd !is null);
- EllipticCurve eckeyr1 = new EllipticCurve(pkNoPwd, null);
- EllipticCurve eckeyr2 = new EllipticCurve(pkPwd, "Test Password");
- string pkRecPwd = eckeyr2.getPrivateKey("Test Password");
- string pkRecNoPwd = eckeyr1.getPrivateKey(null);
- writeln("Recreated Private Key Without Password: ");
- writeln(pkRecNoPwd);
- writeln("Recreated Private Key With Password:");
- writeln(pkRecPwd);
- assert(pkNoPwd == pkRecNoPwd);
- }
- unittest
- {
- import std.digest;
- writeln("Testing EllipticCurve Key Derivation:");
- EllipticCurve eckey1 = new EllipticCurve();
- writeln("Created Key 1");
- EllipticCurve eckey2 = new EllipticCurve();
- writeln("Created Key 2");
- string privKey1 = eckey1.getPrivateKey(null);
- writeln("Retrieved Private Key 1");
- string pubKey1 = eckey1.getPublicKey();
- writeln("Retrieved Public Key 1");
- string pubKey2 = eckey2.getPublicKey();
- writeln("Retrieved Public Key 2");
- ubyte[] key1 = eckey1.derive(pubKey2);
- writeln("Derived Key 1");
- ubyte[] key2 = eckey2.derive(pubKey1);
- writeln("Derived Key 2");
- writeln("Derived Key 1: ", toHexString!(LetterCase.lower)(key1));
- writeln("Derived Key 2: ", toHexString!(LetterCase.lower)(key2));
- assert(key1 !is null);
- assert(key2 !is null);
- assert(constantTimeEquality(key1, key2));
- }
- unittest
- {
- import std.digest;
- writeln("Testing EllipticCurve Signing/Verification:");
- EllipticCurve eckey = new EllipticCurve();
- ubyte[48] data = [ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
- 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
- 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF ];
- ubyte[] sig = eckey.sign(data);
- writeln("Signature: ", toHexString!(LetterCase.lower)(sig));
- assert(eckey.verify(data, sig));
- }
- private int getOpenSSLCurveId(EccCurve curve) {
- import std.conv;
- import std.format;
- switch (curve) {
- case EccCurve.P256: return NID_secp256k1;
- case EccCurve.P384: return NID_secp384r1;
- case EccCurve.P521: return NID_secp521r1;
- default:
- throw new CryptographicException(format("ECC Curve '%s' not supported.", to!string(curve)));
- }
- }
|