From c8c1b4a050e14479d4435cb3fd37b6a8ee8d549e Mon Sep 17 00:00:00 2001 From: monnerat Date: Sat, 12 Nov 2016 19:15:49 +0100 Subject: [PATCH] Make libssh2 work again on os400. (#118) * os400: minimum supported OS version is now V6R1. Do not log compiler informational messages. * Implement crypto backend specific Diffie-Hellman computation. This feature is now needed on os400 because the QC3 library does not implement bn_mod_exp() natively. Up to now, this function was emulated using an RSA encryption, but commits ca5222ea819cc5ed797860070b4c6c1aeeb28420 and 7934c9ce2a029c43e3642a492d3b9e494d1542be (CVE-2016-0787) broke the emulation because QC3 only supports RSA exponents up to 512 bits. Happily, QC3 supports a native API for Diffie-Hellman computation, with opaque random value: this commit implements the use of this API and, as a side effect, enables support of this feature for any other crypto backend that would use it. A "generic" Diffie-Hellman computation internal API supports crypto backends not implementing their own: this generic API uses the same functions as before. * Fix typos in docs/HACKING.CRYPTO. --- docs/HACKING.CRYPTO | 64 +++++++-- os400/initscript.sh | 4 +- src/kex.c | 66 ++++++--- src/libgcrypt.h | 2 + src/libssh2_priv.h | 2 +- src/openssl.h | 2 + src/os400qc3.c | 317 ++++++++++++++------------------------------ src/os400qc3.h | 29 +++- src/wincng.h | 2 + 9 files changed, 235 insertions(+), 253 deletions(-) diff --git a/docs/HACKING.CRYPTO b/docs/HACKING.CRYPTO index 21c99c3a..381b2a9c 100644 --- a/docs/HACKING.CRYPTO +++ b/docs/HACKING.CRYPTO @@ -88,7 +88,7 @@ SHA_DIGEST_LENGTH #define to 20, the SHA-1 digest length. libssh2_sha1_ctx -Type of an SHA1 computation context. Generally a struct. +Type of an SHA-1 computation context. Generally a struct. int libssh2_sha1_init(libssh2_sha1_ctx *x); Initializes the SHA-1 computation context at x. @@ -102,7 +102,7 @@ Note: if the ctx parameter is modified by the underlying code, this procedure must be implemented as a macro to map ctx --> &ctx. void libssh2_sha1_final(libssh2_sha1_ctx ctx, - unsigned char output[SHA1_DIGEST_LEN]); + unsigned char output[SHA_DIGEST_LEN]); Get the computed SHA-1 signature from context ctx and store it into the output buffer. Release the context. @@ -223,7 +223,7 @@ the keylen-byte key. Is invoked just after libssh2_hmac_ctx_init(). Returns 1 for success and 0 for failure. -4) Bidirectional Key ciphers. +4) Bidirectional key ciphers. _libssh2_cipher_ctx Type of a cipher computation context. @@ -332,10 +332,50 @@ TripleDES-CBC algorithm identifier initializer. #define with constant value of type _libssh2_cipher_type(). -5) Big numbers. +5) Diffie-Hellman support. +If the crypto-library supports opaque Diffie-Hellman computations, symbol +`libssh2_dh_key_pair' should be #defined as described below and the rest of +this section applies. +Else, the Diffie-Hellman context MUST be defined as `_libssh2_bn *' and +the computation is emulated via calls to _libssh2_bn_rand() and +_libssh2_bn_mod_exp(): all other symbols in this section are unused in this +case. + +5.1) Diffie-Hellman context. +_libssh2_dh_ctx +Type of a Diffie-Hellman computation context. +Must always be defined. + +5.2) Diffie-Hellman computation procedures. +void libssh2_dh_init(_libssh2_dh_ctx *dhctx); +Initializes the Diffie-Hellman context at `dhctx'. No effective context +creation needed here. + +int libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, + _libssh2_bn *g, _libssh2_bn *p, int group_order, + _libssh2_bn_ctx *bnctx); +Generates a Diffie-Hellman key pair using base `g', prime `p' and the given +`group_order'. Can use the given big number context `bnctx' if needed. +The private key is stored as opaque in the Diffie-Hellman context `*dhctx' and +the public key is returned in `public'. +0 is returned upon success, else -1. +If defined, this procedure MUST be implemented as a #define'd macro. + +int libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, + _libssh2_bn *f, _libssh2_bn *p, _libssh2_bn_ctx * bnctx) +Computes the Diffie-Hellman secret from the previouly created context `*dhctx', +the public key `f' from the other party and the same prime `p' used at +context creation. The result is stored in `secret'. +0 is returned upon success, else -1. + +void libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) +Destroys Diffie-Hellman context at `dhctx' and resets its storage. + + +6) Big numbers. Positive multi-byte integers support is sufficient. -5.1) Computation contexts. +6.1) Computation contexts. This has a real meaning if the big numbers computations need some context storage. If not, use a dummy type and functions (macros). @@ -349,7 +389,7 @@ Returns a new multiple precision computation context. void _libssh2_bn_ctx_free(_libssh2_bn_ctx ctx); Releases a multiple precision computation context. -5.2) Computation support. +6.2) Computation support. _libssh2_bn Type of multiple precision numbers (aka bignumbers or huge integers) for the crypto library. @@ -396,15 +436,17 @@ random number can be zero. If top is 0, it is set to 1, and if top is 1, the two most significant bits of the number will be set to 1, so that the product of two such random numbers will always have 2*bits length. If bottom is true, the number will be odd. +This procedure is only needed if no specific Diffie-Hellman support is provided. void _libssh2_bn_mod_exp(_libssh2_bn *r, _libssh2_bn *a, _libssh2_bn *p, _libssh2_bn *m, _libssh2_bn_ctx *ctx); Computes a to the p-th power modulo m and stores the result into r (r=a^p % m). May use the given context. +This procedure is only needed if no specific Diffie-Hellman support is provided. -6) Private key algorithms. +7) Private key algorithms. Format of an RSA public key: a) "ssh-rsa". b) RSA exponent, MSB first, with high order bit = 0. @@ -448,7 +490,7 @@ Both buffers have to be allocated using LIBSSH2_ALLOC(). Returns 0 if OK, else -1. This procedure is already prototyped in crypto.h. -6.1) RSA +7.1) RSA LIBSSH2_RSA #define as 1 if the crypto library supports RSA, else 0. If defined as 0, the rest of this section can be omitted. @@ -542,7 +584,7 @@ void _libssh2_rsa_free(libssh2_rsa_ctx *rsactx); Releases the RSA computation context at rsactx. -6.2) DSA +7.2) DSA LIBSSH2_DSA #define as 1 if the crypto library supports DSA, else 0. If defined as 0, the rest of this section can be omitted. @@ -592,7 +634,7 @@ This procedure is already prototyped in crypto.h. int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx *dsactx, const unsigned char *sig, const unsigned char *m, unsigned long m_len); -Verify (sig, siglen) signature of (m, m_len) using an SHA1 hash and the +Verify (sig, siglen) signature of (m, m_len) using an SHA-1 hash and the DSA context. Returns 0 if OK, else -1. This procedure is already prototyped in crypto.h. @@ -608,7 +650,7 @@ void _libssh2_dsa_free(libssh2_dsa_ctx *dsactx); Releases the DSA computation context at dsactx. -7) Miscellaneous +8) Miscellaneous void libssh2_prepare_iovec(struct iovec *vector, unsigned int len); Prepare len consecutive iovec slots before using them. diff --git a/os400/initscript.sh b/os400/initscript.sh index 1d47a1dd..a18e24cf 100644 --- a/os400/initscript.sh +++ b/os400/initscript.sh @@ -49,7 +49,7 @@ setenv TGTCCSID '500' # Target CCSID of objects. setenv DEBUG '*ALL' # Debug level. setenv OPTIMIZE '10' # Optimisation level setenv OUTPUT '*NONE' # Compilation output option. -setenv TGTRLS 'V5R3M0' # Target OS release. +setenv TGTRLS 'V6R1M0' # Target OS release. setenv IFSDIR '/libssh2' # Installation IFS directory. # Define ZLIB availability and locations. @@ -180,7 +180,7 @@ make_module() CMD="CRTCMOD MODULE(${TARGETLIB}/${1}) SRCSTMF('__tmpsrcf.c')" # CMD="${CMD} SYSIFCOPT(*IFS64IO) OPTION(*INCDIRFIRST *SHOWINC *SHOWSYS)" CMD="${CMD} SYSIFCOPT(*IFS64IO) OPTION(*INCDIRFIRST)" - CMD="${CMD} LOCALETYPE(*LOCALE)" + CMD="${CMD} LOCALETYPE(*LOCALE) FLAG(10)" CMD="${CMD} INCDIR('${TOPDIR}/os400/include'" CMD="${CMD} '/QIBM/ProdData/qadrt/include' '${TOPDIR}/include'" CMD="${CMD} '${TOPDIR}/os400' '${SRCDIR}'" diff --git a/src/kex.c b/src/kex.c index 95e4d918..c0f92609 100644 --- a/src/kex.c +++ b/src/kex.c @@ -98,6 +98,44 @@ } \ } +/* + * Generic Diffie-Hellman computation support. + * + * DH context should be a _libssh2_bn *. + */ + +#ifndef libssh2_dh_key_pair +static void libssh2_dh_init(_libssh2_dh_ctx *dhctx) +{ + *dhctx = _libssh2_bn_init(); /* Random from client */ +} + +static int libssh2_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, + _libssh2_bn *g, _libssh2_bn *p, int group_order, + _libssh2_bn_ctx *bnctx) +{ + /* Generate x and e */ + _libssh2_bn_rand(*dhctx, group_order * 8 - 1, 0, -1); + _libssh2_bn_mod_exp(public, g, *dhctx, p, bnctx); + return 0; +} + +static int libssh2_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, + _libssh2_bn *f, _libssh2_bn *p, + _libssh2_bn_ctx * bnctx) +{ + /* Compute the shared secret */ + _libssh2_bn_mod_exp(secret, f, *dhctx, p, bnctx); + return 0; +} + +static void libssh2_dh_dtor(_libssh2_dh_ctx *dhctx) +{ + _libssh2_bn_free(*dhctx); + *dhctx = NULL; +} +#endif + /* * diffie_hellman_sha1 @@ -124,7 +162,7 @@ static int diffie_hellman_sha1(LIBSSH2_SESSION *session, exchange_state->s_packet = NULL; exchange_state->k_value = NULL; exchange_state->ctx = _libssh2_bn_ctx_new(); - exchange_state->x = _libssh2_bn_init(); /* Random from client */ + libssh2_dh_init(&exchange_state->x); exchange_state->e = _libssh2_bn_init(); /* g^x mod p */ exchange_state->f = _libssh2_bn_init_from_bin(); /* g^(Random from server) mod p */ exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */ @@ -133,9 +171,8 @@ static int diffie_hellman_sha1(LIBSSH2_SESSION *session, memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t)); /* Generate x and e */ - _libssh2_bn_rand(exchange_state->x, group_order * 8 - 1, 0, -1); - _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p, - exchange_state->ctx); + libssh2_dh_key_pair(&exchange_state->x, exchange_state->e, g, p, + group_order, exchange_state->ctx); /* Send KEX init */ /* packet_type(1) + String Length(4) + leading 0(1) */ @@ -325,8 +362,8 @@ static int diffie_hellman_sha1(LIBSSH2_SESSION *session, exchange_state->h_sig = exchange_state->s; /* Compute the shared secret */ - _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f, - exchange_state->x, p, exchange_state->ctx); + libssh2_dh_secret(&exchange_state->x, exchange_state->k, + exchange_state->f, p, exchange_state->ctx); exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5; if (_libssh2_bn_bits(exchange_state->k) % 8) { /* don't need leading 00 */ @@ -693,8 +730,7 @@ static int diffie_hellman_sha1(LIBSSH2_SESSION *session, } clean_exit: - _libssh2_bn_free(exchange_state->x); - exchange_state->x = NULL; + libssh2_dh_dtor(&exchange_state->x); _libssh2_bn_free(exchange_state->e); exchange_state->e = NULL; _libssh2_bn_free(exchange_state->f); @@ -750,7 +786,7 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session, exchange_state->s_packet = NULL; exchange_state->k_value = NULL; exchange_state->ctx = _libssh2_bn_ctx_new(); - exchange_state->x = _libssh2_bn_init(); /* Random from client */ + libssh2_dh_init(&exchange_state->x); exchange_state->e = _libssh2_bn_init(); /* g^x mod p */ exchange_state->f = _libssh2_bn_init_from_bin(); /* g^(Random from server) mod p */ exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */ @@ -759,9 +795,8 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session, memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t)); /* Generate x and e */ - _libssh2_bn_rand(exchange_state->x, group_order * 8 - 1, 0, -1); - _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p, - exchange_state->ctx); + libssh2_dh_key_pair(&exchange_state->x, exchange_state->e, g, p, + group_order, exchange_state->ctx); /* Send KEX init */ /* packet_type(1) + String Length(4) + leading 0(1) */ @@ -951,8 +986,8 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session, exchange_state->h_sig = exchange_state->s; /* Compute the shared secret */ - _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f, - exchange_state->x, p, exchange_state->ctx); + libssh2_dh_secret(&exchange_state->x, exchange_state->k, + exchange_state->f, p, exchange_state->ctx); exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5; if (_libssh2_bn_bits(exchange_state->k) % 8) { /* don't need leading 00 */ @@ -1321,8 +1356,7 @@ static int diffie_hellman_sha256(LIBSSH2_SESSION *session, } clean_exit: - _libssh2_bn_free(exchange_state->x); - exchange_state->x = NULL; + libssh2_dh_dtor(&exchange_state->x); _libssh2_bn_free(exchange_state->e); exchange_state->e = NULL; _libssh2_bn_free(exchange_state->f); diff --git a/src/libgcrypt.h b/src/libgcrypt.h index 11d6ad2d..2a3f8d1f 100644 --- a/src/libgcrypt.h +++ b/src/libgcrypt.h @@ -181,3 +181,5 @@ #define _libssh2_bn_bits(bn) gcry_mpi_get_nbits (bn) #define _libssh2_bn_free(bn) gcry_mpi_release(bn) +#define _libssh2_dh_ctx _libssh2_bn * + diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index b4296a22..0dab7357 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -248,7 +248,7 @@ typedef struct kmdhgGPshakex_state_t size_t s_packet_len; size_t tmp_len; _libssh2_bn_ctx *ctx; - _libssh2_bn *x; + _libssh2_dh_ctx x; _libssh2_bn *e; _libssh2_bn *f; _libssh2_bn *k; diff --git a/src/openssl.h b/src/openssl.h index 3ca71fa8..fba04547 100644 --- a/src/openssl.h +++ b/src/openssl.h @@ -287,6 +287,8 @@ int _libssh2_md5_init(libssh2_md5_ctx *ctx); #define _libssh2_bn_bits(bn) BN_num_bits(bn) #define _libssh2_bn_free(bn) BN_clear_free(bn) +#define _libssh2_dh_ctx _libssh2_bn * + const EVP_CIPHER *_libssh2_EVP_aes_128_ctr(void); const EVP_CIPHER *_libssh2_EVP_aes_192_ctr(void); const EVP_CIPHER *_libssh2_EVP_aes_256_ctr(void); diff --git a/src/os400qc3.c b/src/os400qc3.c index f8e46aba..74fe64b4 100644 --- a/src/os400qc3.c +++ b/src/os400qc3.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Patrick Monnerat, D+H + * Copyright (C) 2015-2016 Patrick Monnerat, D+H * All rights reserved. * * Redistribution and use in source and binary forms, @@ -115,6 +115,9 @@ static int sshdsapubkey(LIBSSH2_SESSION *session, char **sshpubkey, const char *method); #endif +static unsigned char OID_dhKeyAgreement[] = + {9, 40 + 2, 0x86, 0x48, 0x86, 0xF7, 0x0D, 1, 3, 1}; + /* PKCS#5 support. */ @@ -206,7 +209,7 @@ static const pkcs5algo des_EDE3_CBC = { OID_des_EDE3_CBC, parse_iv, Qc3_TDES, 8, Qc3_CBC, Qc3_Pad_Counter, '\0', 24, 0, 0, 8, 8, 0 }; - + /* rc2CBC OID: 1.2.840.113549.3.2 */ static const unsigned char OID_rc2CBC[] = { 8, 40 + 2, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x02 @@ -330,6 +333,7 @@ static const char fopenrbmode[] = "rb"; #include #include #include +#include static Qc3_Format_KEYD0100_T nulltoken = {""}; @@ -883,220 +887,6 @@ _libssh2_random(unsigned char *buf, int len) Qc3PRN_TYPE_NORMAL, Qc3PRN_NO_PARITY, (char *) &ecnull); } -int -_libssh2_bn_rand(_libssh2_bn *bn, int bits, int top, int bottom) -{ - int len; - int i; - - if (!bn || bits <= 0) - return -1; - len = (bits + 7) >> 3; - if (_libssh2_bn_resize(bn, len)) - return -1; - _libssh2_random(bn->bignum, len); - i = ((bits - 1) & 07) + 1; - bn->bignum[len - 1] &= (1 << i) - 1; - switch (top) { - case 1: - if (bits > 1) - if (i > 1) - bn->bignum[len - 1] |= 1 << (i - 2); - else - bn->bignum[len - 2] |= 0x80; - /* Fall into. */ - case 0: - bn->bignum[len - 1] |= 1 << (i - 1); - break; - } - if (bottom) - *bn->bignum |= 0x01; - return 0; -} - -static int -_libssh2_bn_lshift(_libssh2_bn *bn) -{ - int i; - int c = 0; - - if (!bn) - return -1; - - if (_libssh2_bn_resize(bn, (_libssh2_bn_bits(bn) + 8) >> 3)) - return -1; - - for (i = 0; i < bn->length; i++) { - if (bn->bignum[i] & 0x80) - c |= 0x02; - bn->bignum[i] = (bn->bignum[i] << 1) | (c & 0x01); - c >>= 1; - } - - return 0; -} - -static int -_libssh2_bn_rshift(_libssh2_bn *bn) -{ - int i; - int c = 0; - - if (!bn) - return -1; - - for (i = bn->length; i--;) { - if (bn->bignum[i] & 0x01) - c |= 0x100; - bn->bignum[i] = (bn->bignum[i] >> 1) | (c & 0x80); - c >>= 1; - } - - if (_libssh2_bn_resize(bn, (_libssh2_bn_bits(bn) + 7) >> 3)) - return -1; - - return 0; -} - -static void -_libssh2_bn_swap(_libssh2_bn *bn1, _libssh2_bn *bn2) -{ - _libssh2_bn t = *bn1; - - *bn1 = *bn2; - *bn2 = t; -} - -static int -_libssh2_bn_subtract(_libssh2_bn *d, _libssh2_bn *bn1, _libssh2_bn *bn2) -{ - int c = 0; - int i; - - if (bn1->length < bn2->length) - return -1; - - if (_libssh2_bn_resize(d, bn1->length)) - return -1; - - for (i = 0; i < bn2->length; i++) { - c += (int) bn1->bignum[i] - (int) bn2->bignum[i]; - d->bignum[i] = c; - c = c < 0? -1: 0; - } - - for (; c && i < bn1->length; i++) { - c += (int) bn1->bignum[i]; - d->bignum[i] = c; - c = c < 0? -1: 0; - } - - if (_libssh2_bn_resize(d, (_libssh2_bn_bits(d) + 7) >> 3)) - return -1; - - return c; -} - -int -_libssh2_os400qc3_bn_mod_exp(_libssh2_bn *r, _libssh2_bn *a, _libssh2_bn *p, - _libssh2_bn *m) -{ - _libssh2_bn *mp; - _libssh2_bn *rp; - asn1Element *rsapubkey; - asn1Element *subjpubkeyinfo; - unsigned char *av; - unsigned char *rv; - char *keydbuf; - Qc3_Format_ALGD0400_T algd; - Qc3_Format_KEYD0200_T *keyd; - Qus_EC_t errcode; - int sc; - int outlen; - int ret = -1; - - /* There is no support for this function in the Qc3 crypto-library. - Since a RSA encryption performs this function, we can emulate it - by creating an RSA public key in ASN.1 SubjectPublicKeyInfo format - from p (exponent) and m (modulus) and encrypt a with this key. The - encryption output is the function result. - Problem: the Qc3EncryptData procedure only succeeds if the data bit - count is less than the modulus bit count. To satisfy this condition, - we multiply the modulus by a power of two and adjust the result - accordingly. */ - - if (!r || !a || !p) - return ret; - - mp = _libssh2_bn_init(); - if (!mp) - return ret; - if (_libssh2_bn_from_bn(mp, m)) { - _libssh2_bn_free(mp); - return ret; - } - for (sc = 0; _libssh2_bn_bits(mp) <= 8 * a->length; sc++) - if (_libssh2_bn_lshift(mp)) { - _libssh2_bn_free(mp); - return ret; - } - - rsapubkey = rsapublickey(p, mp); - subjpubkeyinfo = rsasubjectpublickeyinfo(rsapubkey); - asn1delete(rsapubkey); - - if (!rsapubkey || !subjpubkeyinfo) { - asn1delete(rsapubkey); - asn1delete(subjpubkeyinfo); - _libssh2_bn_free(mp); - return ret; - } - - av = (unsigned char *) alloca(a->length); - rv = (unsigned char *) alloca(mp->length); - keydbuf = alloca(sizeof *keyd + - subjpubkeyinfo->end - subjpubkeyinfo->header); - - if (av && rv && keydbuf) { - _libssh2_bn_to_bin(a, av); - algd.Public_Key_Alg = Qc3_RSA; - algd.PKA_Block_Format = Qc3_Zero_Pad; - memset(algd.Reserved, 0, sizeof algd.Reserved); - algd.Signing_Hash_Alg = 0; - keyd = (Qc3_Format_KEYD0200_T *) keydbuf; - keyd->Key_Type = Qc3_RSA_Public; - keyd->Key_String_Len = subjpubkeyinfo->end - subjpubkeyinfo->header; - keyd->Key_Format = Qc3_BER_String; - memset(keyd->Reserved, 0, sizeof keyd->Reserved); - memcpy(keydbuf + sizeof *keyd, subjpubkeyinfo->header, - keyd->Key_String_Len); - set_EC_length(errcode, sizeof errcode); - Qc3EncryptData(av, (int *) &a->length, Qc3_Data, (char *) &algd, - Qc3_Alg_Public_Key, keydbuf, Qc3_Key_Parms, anycsp, - NULL, rv, (int *) &mp->length, &outlen, &errcode); - if (!errcode.Bytes_Available) { - _libssh2_bn_from_bin(r, outlen, rv); - if (!sc) - ret = 0; - else { - rp = _libssh2_bn_init(); - if (rp) { - do { - _libssh2_bn_rshift(mp); - if (!_libssh2_bn_subtract(rp, r, mp)) - _libssh2_bn_swap(r, rp); - } while (--sc); - _libssh2_bn_free(rp); - ret = 0; - } - } - } - } - asn1delete(subjpubkeyinfo); - _libssh2_bn_free(mp); - return ret; -} - /******************************************************************* * @@ -1443,6 +1233,101 @@ _libssh2_rsa_new(libssh2_rsa_ctx **rsa, } +/******************************************************************* + * + * OS/400 QC3 crypto-library backend: Diffie-Hellman support. + * + *******************************************************************/ + +void +_libssh2_os400qc3_dh_init(_libssh2_dh_ctx *dhctx) +{ + memset((char *) dhctx, 0, sizeof *dhctx); +} + +int +_libssh2_os400qc3_dh_key_pair(_libssh2_dh_ctx *dhctx, _libssh2_bn *public, + _libssh2_bn *g, _libssh2_bn *p, int group_order) +{ + asn1Element *prime; + asn1Element *base; + asn1Element *dhparameter; + asn1Element *dhkeyagreement; + asn1Element *pkcs3; + int pkcs3len; + char *pubkey; + int pubkeysize; + int pubkeylen; + Qus_EC_t errcode; + + (void) group_order; + + /* Build the PKCS#3 structure. */ + + base = asn1uint(g); + prime = asn1uint(p); + dhparameter = asn1container(ASN1_SEQ | ASN1_CONSTRUCTED, + prime, base, NULL); + asn1delete(base); + asn1delete(prime); + dhkeyagreement = asn1bytes(ASN1_OBJ_ID, + OID_dhKeyAgreement + 1, OID_dhKeyAgreement[0]); + pkcs3 = asn1container(ASN1_SEQ | ASN1_CONSTRUCTED, + dhkeyagreement, dhparameter, NULL); + asn1delete(dhkeyagreement); + asn1delete(dhparameter); + if (!base || !prime || !dhparameter || + !dhkeyagreement || !dhparameter || !pkcs3) { + asn1delete(pkcs3); + return -1; + } + pkcs3len = pkcs3->end - pkcs3->header; + pubkeysize = (_libssh2_bn_bits(p) + 7) >> 3; + pubkey = alloca(pubkeysize); + set_EC_length(errcode, sizeof errcode); + Qc3GenDHKeyPair((char *) pkcs3->header, &pkcs3len, anycsp, NULL, + dhctx->token, pubkey, &pubkeysize, &pubkeylen, &errcode); + asn1delete(pkcs3); + if (errcode.Bytes_Available) + return -1; + return _libssh2_bn_from_bin(public, pubkeylen, (unsigned char *) pubkey); +} + +int +_libssh2_os400qc3_dh_secret(_libssh2_dh_ctx *dhctx, _libssh2_bn *secret, + _libssh2_bn *f, _libssh2_bn *p) +{ + char *pubkey; + int pubkeysize; + char *secretbuf; + int secretbufsize; + int secretbuflen; + Qus_EC_t errcode; + + pubkeysize = (_libssh2_bn_bits(f) + 7) >> 3; + pubkey = alloca(pubkeysize); + _libssh2_bn_to_bin(f, pubkey); + secretbufsize = (_libssh2_bn_bits(p) + 7) >> 3; + secretbuf = alloca(pubkeysize); + set_EC_length(errcode, sizeof errcode); + Qc3CalculateDHSecretKey(dhctx->token, pubkey, &pubkeysize, + secretbuf, &secretbufsize, &secretbuflen, &errcode); + if (errcode.Bytes_Available) + return -1; + return _libssh2_bn_from_bin(secret, + secretbuflen, (unsigned char *) secretbuf); +} + +void +_libssh2_os400qc3_dh_dtor(_libssh2_dh_ctx *dhctx) +{ + if (!null_token(dhctx->token)) { + Qc3DestroyAlgorithmContext(dhctx->token, (char *) &ecnull); + memset((char *) dhctx, 0, sizeof *dhctx); + } +} + + /******************************************************************* * * OS/400 QC3 crypto-library backend: PKCS#5 supplement. diff --git a/src/os400qc3.h b/src/os400qc3.h index dbaa581f..2e2f56a9 100644 --- a/src/os400qc3.h +++ b/src/os400qc3.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Patrick Monnerat, D+H + * Copyright (C) 2015-2016 Patrick Monnerat, D+H * All rights reserved. * * Redistribution and use in source and binary forms, @@ -210,6 +210,10 @@ typedef struct { /* Algorithm description. */ int keylen; /* Key length. */ } _libssh2_os400qc3_cipher_t; +typedef struct { /* Diffie-Hellman context. */ + char token[8]; /* Context token. */ +} _libssh2_os400qc3_dh_ctx; + /******************************************************************* * * OS/400 QC3 crypto-library backend: Define global types/codes. @@ -277,8 +281,6 @@ typedef struct { /* Algorithm description. */ #define _libssh2_bn_ctx_free(bnctx) ((void) 0) #define _libssh2_bn_init_from_bin() _libssh2_bn_init() -#define _libssh2_bn_mod_exp(r, a, p, m, ctx) \ - _libssh2_os400qc3_bn_mod_exp(r, a, p, m) #define _libssh2_bn_bytes(bn) ((bn)->length) #define _libssh2_cipher_type(name) _libssh2_os400qc3_cipher_t name @@ -309,6 +311,14 @@ typedef struct { /* Algorithm description. */ _libssh2_os400qc3_rsa_sha1_signv(session, sig, siglen, \ count, vector, ctx) +#define _libssh2_dh_ctx _libssh2_os400qc3_dh_ctx +#define libssh2_dh_init(dhctx) _libssh2_os400qc3_dh_init(dhctx) +#define libssh2_dh_key_pair(dhctx, public, g, p, group_order, bnctx) \ + _libssh2_os400qc3_dh_key_pair(dhctx, public, g, p, group_order) +#define libssh2_dh_secret(dhctx, secret, f, p, bnctx) \ + _libssh2_os400qc3_dh_secret(dhctx, secret, f, p) +#define libssh2_dh_dtor(dhctx) _libssh2_os400qc3_dh_dtor(dhctx) + /******************************************************************* * @@ -324,10 +334,6 @@ extern int _libssh2_bn_from_bin(_libssh2_bn *bn, int len, extern int _libssh2_bn_set_word(_libssh2_bn *bn, unsigned long val); extern int _libssh2_bn_to_bin(_libssh2_bn *bn, unsigned char *val); extern void _libssh2_random(unsigned char *buf, int len); -extern int _libssh2_bn_rand(_libssh2_bn *bn, int bits, - int top, int bottom); -extern int _libssh2_os400qc3_bn_mod_exp(_libssh2_bn *r, _libssh2_bn *a, - _libssh2_bn *p, _libssh2_bn *m); extern void _libssh2_os400qc3_crypto_dtor(_libssh2_os400qc3_crypto_ctx *x); extern int libssh2_os400qc3_hash_init(Qc3_Format_ALGD0100_T *x, unsigned int algo); @@ -352,6 +358,15 @@ extern int _libssh2_os400qc3_rsa_sha1_signv(LIBSSH2_SESSION *session, int veccount, const struct iovec vector[], libssh2_rsa_ctx *ctx); +extern void _libssh2_os400qc3_dh_init(_libssh2_dh_ctx *dhctx); +extern int _libssh2_os400qc3_dh_key_pair(_libssh2_dh_ctx *dhctx, + _libssh2_bn *public, + _libssh2_bn *g, + _libssh2_bn *p, int group_order); +extern int _libssh2_os400qc3_dh_secret(_libssh2_dh_ctx *dhctx, + _libssh2_bn *secret, + _libssh2_bn *f, _libssh2_bn *p); +extern void _libssh2_os400qc3_dh_dtor(_libssh2_dh_ctx *dhctx); #endif diff --git a/src/wincng.h b/src/wincng.h index 5219db7f..1e9c3ba6 100755 --- a/src/wincng.h +++ b/src/wincng.h @@ -374,6 +374,8 @@ _libssh2_bn *_libssh2_wincng_bignum_init(void); #define _libssh2_bn_free(bn) \ _libssh2_wincng_bignum_free(bn) +#define _libssh2_dh_ctx _libssh2_bn * + /*******************************************************************/ /* * Windows CNG backend: forward declarations