summaryrefslogtreecommitdiffstats
path: root/comm/third_party/libotr/src/dh.c
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/libotr/src/dh.c')
-rw-r--r--comm/third_party/libotr/src/dh.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/comm/third_party/libotr/src/dh.c b/comm/third_party/libotr/src/dh.c
new file mode 100644
index 0000000000..d8bc45d10d
--- /dev/null
+++ b/comm/third_party/libotr/src/dh.c
@@ -0,0 +1,476 @@
+/*
+ * Off-the-Record Messaging library
+ * Copyright (C) 2004-2014 Ian Goldberg, David Goulet, Rob Smits,
+ * Chris Alexander, Willy Lew, Lisa Du,
+ * Nikita Borisov
+ * <otr@cypherpunks.ca>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General
+ * Public License as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* system headers */
+#include <stdlib.h>
+
+/* libgcrypt headers */
+#include <gcrypt.h>
+
+/* libotr headers */
+#include "dh.h"
+
+
+static const char* DH1536_MODULUS_S = "0x"
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+static const char *DH1536_GENERATOR_S = "0x02";
+static const int DH1536_MOD_LEN_BITS = 1536;
+static const int DH1536_MOD_LEN_BYTES = 192;
+
+static gcry_mpi_t DH1536_MODULUS = NULL;
+static gcry_mpi_t DH1536_MODULUS_MINUS_2 = NULL;
+static gcry_mpi_t DH1536_GENERATOR = NULL;
+
+/*
+ * Call this once, at plugin load time. It sets up the modulus and
+ * generator MPIs.
+ */
+void otrl_dh_init(void)
+{
+ gcry_mpi_scan(&DH1536_MODULUS, GCRYMPI_FMT_HEX,
+ (const unsigned char *)DH1536_MODULUS_S, 0, NULL);
+ gcry_mpi_scan(&DH1536_GENERATOR, GCRYMPI_FMT_HEX,
+ (const unsigned char *)DH1536_GENERATOR_S, 0, NULL);
+ DH1536_MODULUS_MINUS_2 = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ gcry_mpi_sub_ui(DH1536_MODULUS_MINUS_2, DH1536_MODULUS, 2);
+}
+
+/*
+ * Initialize the fields of a DH keypair.
+ */
+void otrl_dh_keypair_init(DH_keypair *kp)
+{
+ kp->groupid = 0;
+ kp->priv = NULL;
+ kp->pub = NULL;
+}
+
+/*
+ * Copy a DH_keypair.
+ */
+void otrl_dh_keypair_copy(DH_keypair *dst, const DH_keypair *src)
+{
+ dst->groupid = src->groupid;
+ dst->priv = gcry_mpi_copy(src->priv);
+ dst->pub = gcry_mpi_copy(src->pub);
+}
+
+/*
+ * Deallocate the contents of a DH_keypair (but not the DH_keypair
+ * itself)
+ */
+void otrl_dh_keypair_free(DH_keypair *kp)
+{
+ gcry_mpi_release(kp->priv);
+ gcry_mpi_release(kp->pub);
+ kp->priv = NULL;
+ kp->pub = NULL;
+}
+
+/*
+ * Generate a DH keypair for a specified group.
+ */
+gcry_error_t otrl_dh_gen_keypair(unsigned int groupid, DH_keypair *kp)
+{
+ unsigned char *secbuf = NULL;
+ gcry_mpi_t privkey = NULL;
+
+ if (groupid != DH1536_GROUP_ID) {
+ /* Invalid group id */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Generate the secret key: a random 320-bit value */
+ secbuf = gcry_random_bytes_secure(40, GCRY_STRONG_RANDOM);
+ gcry_mpi_scan(&privkey, GCRYMPI_FMT_USG, secbuf, 40, NULL);
+ gcry_free(secbuf);
+
+ kp->groupid = groupid;
+ kp->priv = privkey;
+ kp->pub = gcry_mpi_new(DH1536_MOD_LEN_BITS);
+ gcry_mpi_powm(kp->pub, DH1536_GENERATOR, privkey, DH1536_MODULUS);
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Construct session keys from a DH keypair and someone else's public
+ * key.
+ */
+gcry_error_t otrl_dh_session(DH_sesskeys *sess, const DH_keypair *kp,
+ gcry_mpi_t y)
+{
+ gcry_mpi_t gab;
+ size_t gablen;
+ unsigned char *gabdata;
+ unsigned char *hashdata;
+ unsigned char sendbyte, rcvbyte;
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+
+ otrl_dh_session_blank(sess);
+
+ if (kp->groupid != DH1536_GROUP_ID) {
+ /* Invalid group id */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Calculate the shared secret MPI */
+ gab = gcry_mpi_snew(DH1536_MOD_LEN_BITS);
+ gcry_mpi_powm(gab, y, kp->priv, DH1536_MODULUS);
+
+ /* Output it in the right format */
+ gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &gablen, gab);
+ gabdata = gcry_malloc_secure(gablen + 5);
+ if (!gabdata) {
+ gcry_mpi_release(gab);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ gabdata[1] = (gablen >> 24) & 0xff;
+ gabdata[2] = (gablen >> 16) & 0xff;
+ gabdata[3] = (gablen >> 8) & 0xff;
+ gabdata[4] = gablen & 0xff;
+ gcry_mpi_print(GCRYMPI_FMT_USG, gabdata+5, gablen, NULL, gab);
+ gcry_mpi_release(gab);
+
+ hashdata = gcry_malloc_secure(20);
+ if (!hashdata) {
+ gcry_free(gabdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+
+ /* Are we the "high" or "low" end of the connection? */
+ if ( gcry_mpi_cmp(kp->pub, y) > 0 ) {
+ sendbyte = 0x01;
+ rcvbyte = 0x02;
+ } else {
+ sendbyte = 0x02;
+ rcvbyte = 0x01;
+ }
+
+ /* Calculate the sending encryption key */
+ gabdata[0] = sendbyte;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashdata, gabdata, gablen+5);
+ err = gcry_cipher_open(&(sess->sendenc), GCRY_CIPHER_AES,
+ GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (err) goto err;
+ err = gcry_cipher_setkey(sess->sendenc, hashdata, 16);
+ if (err) goto err;
+
+ /* Calculate the sending MAC key */
+ gcry_md_hash_buffer(GCRY_MD_SHA1, sess->sendmackey, hashdata, 16);
+ err = gcry_md_open(&(sess->sendmac), GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(sess->sendmac, sess->sendmackey, 20);
+ if (err) goto err;
+
+ /* Calculate the receiving encryption key */
+ gabdata[0] = rcvbyte;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashdata, gabdata, gablen+5);
+ err = gcry_cipher_open(&(sess->rcvenc), GCRY_CIPHER_AES,
+ GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (err) goto err;
+ err = gcry_cipher_setkey(sess->rcvenc, hashdata, 16);
+ if (err) goto err;
+
+ /* Calculate the receiving MAC key (and save it in the DH_sesskeys
+ * struct, so we can reveal it later) */
+ gcry_md_hash_buffer(GCRY_MD_SHA1, sess->rcvmackey, hashdata, 16);
+ err = gcry_md_open(&(sess->rcvmac), GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(sess->rcvmac, sess->rcvmackey, 20);
+ if (err) goto err;
+
+ /* Calculate the extra key (used if applications wish to extract a
+ * symmetric key for transferring files, or something like that) */
+ gabdata[0] = 0xff;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, sess->extrakey, gabdata, gablen+5);
+
+ gcry_free(gabdata);
+ gcry_free(hashdata);
+ return gcry_error(GPG_ERR_NO_ERROR);
+err:
+ otrl_dh_session_free(sess);
+ gcry_free(gabdata);
+ gcry_free(hashdata);
+ return err;
+}
+
+/*
+ * Compute the secure session id, two encryption keys, and four MAC keys
+ * given our DH key and their DH public key.
+ */
+gcry_error_t otrl_dh_compute_v2_auth_keys(const DH_keypair *our_dh,
+ gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp,
+ gcry_cipher_hd_t *enc_c, gcry_cipher_hd_t *enc_cp,
+ gcry_md_hd_t *mac_m1, gcry_md_hd_t *mac_m1p,
+ gcry_md_hd_t *mac_m2, gcry_md_hd_t *mac_m2p)
+{
+ gcry_mpi_t s;
+ size_t slen;
+ unsigned char *sdata;
+ unsigned char *hashdata;
+ unsigned char ctr[16];
+ gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
+
+ *enc_c = NULL;
+ *enc_cp = NULL;
+ *mac_m1 = NULL;
+ *mac_m1p = NULL;
+ *mac_m2 = NULL;
+ *mac_m2p = NULL;
+ memset(ctr, 0, 16);
+
+ if (our_dh->groupid != DH1536_GROUP_ID) {
+ /* Invalid group id */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Check that their_pub is in range */
+ if (gcry_mpi_cmp_ui(their_pub, 2) < 0 ||
+ gcry_mpi_cmp(their_pub, DH1536_MODULUS_MINUS_2) > 0) {
+ /* Invalid pubkey */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Calculate the shared secret MPI */
+ s = gcry_mpi_snew(DH1536_MOD_LEN_BITS);
+ gcry_mpi_powm(s, their_pub, our_dh->priv, DH1536_MODULUS);
+
+ /* Output it in the right format */
+ gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &slen, s);
+ sdata = gcry_malloc_secure(slen + 5);
+ if (!sdata) {
+ gcry_mpi_release(s);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[1] = (slen >> 24) & 0xff;
+ sdata[2] = (slen >> 16) & 0xff;
+ sdata[3] = (slen >> 8) & 0xff;
+ sdata[4] = slen & 0xff;
+ gcry_mpi_print(GCRYMPI_FMT_USG, sdata+5, slen, NULL, s);
+ gcry_mpi_release(s);
+
+ /* Calculate the session id */
+ hashdata = gcry_malloc_secure(32);
+ if (!hashdata) {
+ gcry_free(sdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[0] = 0x00;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ memmove(sessionid, hashdata, 8);
+ *sessionidlenp = 8;
+
+ /* Calculate the encryption keys */
+ sdata[0] = 0x01;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+
+ err = gcry_cipher_open(enc_c, GCRY_CIPHER_AES,
+ GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (err) goto err;
+ err = gcry_cipher_setkey(*enc_c, hashdata, 16);
+ if (err) goto err;
+ err = gcry_cipher_setctr(*enc_c, ctr, 16);
+ if (err) goto err;
+
+ err = gcry_cipher_open(enc_cp, GCRY_CIPHER_AES,
+ GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (err) goto err;
+ err = gcry_cipher_setkey(*enc_cp, hashdata+16, 16);
+ if (err) goto err;
+ err = gcry_cipher_setctr(*enc_cp, ctr, 16);
+ if (err) goto err;
+
+ /* Calculate the MAC keys */
+ sdata[0] = 0x02;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m1, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m1, hashdata, 32);
+ if (err) goto err;
+
+ sdata[0] = 0x03;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m2, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m2, hashdata, 32);
+ if (err) goto err;
+
+ sdata[0] = 0x04;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m1p, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m1p, hashdata, 32);
+ if (err) goto err;
+
+ sdata[0] = 0x05;
+ gcry_md_hash_buffer(GCRY_MD_SHA256, hashdata, sdata, slen+5);
+ err = gcry_md_open(mac_m2p, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err) goto err;
+ err = gcry_md_setkey(*mac_m2p, hashdata, 32);
+ if (err) goto err;
+
+ gcry_free(sdata);
+ gcry_free(hashdata);
+ return gcry_error(GPG_ERR_NO_ERROR);
+
+err:
+ gcry_cipher_close(*enc_c);
+ gcry_cipher_close(*enc_cp);
+ gcry_md_close(*mac_m1);
+ gcry_md_close(*mac_m1p);
+ gcry_md_close(*mac_m2);
+ gcry_md_close(*mac_m2p);
+ *enc_c = NULL;
+ *enc_cp = NULL;
+ *mac_m1 = NULL;
+ *mac_m1p = NULL;
+ *mac_m2 = NULL;
+ *mac_m2p = NULL;
+ gcry_free(sdata);
+ gcry_free(hashdata);
+ return err;
+}
+
+/*
+ * Compute the secure session id, given our DH key and their DH public
+ * key.
+ */
+gcry_error_t otrl_dh_compute_v1_session_id(const DH_keypair *our_dh,
+ gcry_mpi_t their_pub, unsigned char *sessionid, size_t *sessionidlenp,
+ OtrlSessionIdHalf *halfp)
+{
+ gcry_mpi_t s;
+ size_t slen;
+ unsigned char *sdata;
+ unsigned char *hashdata;
+
+ if (our_dh->groupid != DH1536_GROUP_ID) {
+ /* Invalid group id */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Check that their_pub is in range */
+ if (gcry_mpi_cmp_ui(their_pub, 2) < 0 ||
+ gcry_mpi_cmp(their_pub, DH1536_MODULUS_MINUS_2) > 0) {
+ /* Invalid pubkey */
+ return gcry_error(GPG_ERR_INV_VALUE);
+ }
+
+ /* Calculate the shared secret MPI */
+ s = gcry_mpi_snew(DH1536_MOD_LEN_BITS);
+ gcry_mpi_powm(s, their_pub, our_dh->priv, DH1536_MODULUS);
+
+ /* Output it in the right format */
+ gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &slen, s);
+ sdata = gcry_malloc_secure(slen + 5);
+ if (!sdata) {
+ gcry_mpi_release(s);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[1] = (slen >> 24) & 0xff;
+ sdata[2] = (slen >> 16) & 0xff;
+ sdata[3] = (slen >> 8) & 0xff;
+ sdata[4] = slen & 0xff;
+ gcry_mpi_print(GCRYMPI_FMT_USG, sdata+5, slen, NULL, s);
+ gcry_mpi_release(s);
+
+ /* Calculate the session id */
+ hashdata = gcry_malloc_secure(20);
+ if (!hashdata) {
+ gcry_free(sdata);
+ return gcry_error(GPG_ERR_ENOMEM);
+ }
+ sdata[0] = 0x00;
+ gcry_md_hash_buffer(GCRY_MD_SHA1, hashdata, sdata, slen+5);
+ memmove(sessionid, hashdata, 20);
+ *sessionidlenp = 20;
+
+ /* Which half should be bold? */
+ if (gcry_mpi_cmp(our_dh->pub, their_pub) > 0) {
+ *halfp = OTRL_SESSIONID_SECOND_HALF_BOLD;
+ } else {
+ *halfp = OTRL_SESSIONID_FIRST_HALF_BOLD;
+ }
+
+ gcry_free(hashdata);
+ gcry_free(sdata);
+ return gcry_error(GPG_ERR_NO_ERROR);
+}
+
+/*
+ * Deallocate the contents of a DH_sesskeys (but not the DH_sesskeys
+ * itself)
+ */
+void otrl_dh_session_free(DH_sesskeys *sess)
+{
+ gcry_cipher_close(sess->sendenc);
+ gcry_cipher_close(sess->rcvenc);
+ gcry_md_close(sess->sendmac);
+ gcry_md_close(sess->rcvmac);
+
+ otrl_dh_session_blank(sess);
+}
+
+/*
+ * Blank out the contents of a DH_sesskeys (without releasing it)
+ */
+void otrl_dh_session_blank(DH_sesskeys *sess)
+{
+ sess->sendenc = NULL;
+ sess->sendmac = NULL;
+ sess->rcvenc = NULL;
+ sess->rcvmac = NULL;
+ memset(sess->sendctr, 0, 16);
+ memset(sess->rcvctr, 0, 16);
+ memset(sess->sendmackey, 0, 20);
+ memset(sess->rcvmackey, 0, 20);
+ sess->sendmacused = 0;
+ sess->rcvmacused = 0;
+ memset(sess->extrakey, 0, OTRL_EXTRAKEY_BYTES);
+}
+
+/* Increment the top half of a counter block */
+void otrl_dh_incctr(unsigned char *ctr)
+{
+ int i;
+ for (i=8;i;--i) {
+ if (++ctr[i-1]) break;
+ }
+}
+
+/* Compare two counter values (8 bytes each). Return 0 if ctr1 == ctr2,
+ * < 0 if ctr1 < ctr2 (as unsigned 64-bit values), > 0 if ctr1 > ctr2. */
+int otrl_dh_cmpctr(const unsigned char *ctr1, const unsigned char *ctr2)
+{
+ int i;
+ for (i=0;i<8;++i) {
+ int c = ctr1[i] - ctr2[i];
+ if (c) return c;
+ }
+ return 0;
+}