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.
diff --git a/docs/HACKING.CRYPTO b/docs/HACKING.CRYPTO
index 21c99c3..381b2a9 100644
--- a/docs/HACKING.CRYPTO
+++ b/docs/HACKING.CRYPTO
@@ -88,7 +88,7 @@
 #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 @@
 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 @@
 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 @@
 #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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 @@
 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 1d47a1d..a18e24c 100644
--- a/os400/initscript.sh
+++ b/os400/initscript.sh
@@ -49,7 +49,7 @@
 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 @@
         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 95e4d91..c0f9260 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 @@
         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 @@
         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 @@
         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 @@
     }
 
   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 @@
         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 @@
         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 @@
         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 @@
     }
 
   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 11d6ad2..2a3f8d1 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 b4296a2..0dab735 100644
--- a/src/libssh2_priv.h
+++ b/src/libssh2_priv.h
@@ -248,7 +248,7 @@
     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 3ca71fa..fba0454 100644
--- a/src/openssl.h
+++ b/src/openssl.h
@@ -287,6 +287,8 @@
 #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 f8e46ab..74fe64b 100644
--- a/src/os400qc3.c
+++ b/src/os400qc3.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Patrick Monnerat, D+H <patrick.monnerat@dh.com>
+ * Copyright (C) 2015-2016 Patrick Monnerat, D+H <patrick.monnerat@dh.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms,
@@ -115,6 +115,9 @@
                          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 @@
     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 @@
 #include <qc3sigvr.h>
 #include <qc3sigcl.h>
 #include <qc3pbext.h>
+#include <qc3dh.h>
 
 static Qc3_Format_KEYD0100_T    nulltoken = {""};
 
@@ -883,220 +887,6 @@
         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;
-}
-
 
 /*******************************************************************
  *
@@ -1445,6 +1235,101 @@
 
 /*******************************************************************
  *
+ * 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 dbaa581..2e2f56a 100644
--- a/src/os400qc3.h
+++ b/src/os400qc3.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Patrick Monnerat, D+H <patrick.monnerat@dh.com>
+ * Copyright (C) 2015-2016 Patrick Monnerat, D+H <patrick.monnerat@dh.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms,
@@ -210,6 +210,10 @@
     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 @@
 #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 @@
             _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_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 @@
                                                  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 5219db7..1e9c3ba 100755
--- a/src/wincng.h
+++ b/src/wincng.h
@@ -374,6 +374,8 @@
 #define _libssh2_bn_free(bn) \
   _libssh2_wincng_bignum_free(bn)
 
+#define _libssh2_dh_ctx _libssh2_bn *
+
 /*******************************************************************/
 /*
  * Windows CNG backend: forward declarations