Restore libgcrypt support.
authorKirill Isakov <is-kir@ya.ru>
Wed, 18 Aug 2021 08:51:10 +0000 (14:51 +0600)
committerKirill Isakov <is-kir@ya.ru>
Wed, 18 Aug 2021 08:51:18 +0000 (14:51 +0600)
m4/libgcrypt.m4
src/Makefile.am
src/gcrypt/cipher.c
src/gcrypt/digest.c
src/gcrypt/pem.c [new file with mode: 0644]
src/gcrypt/pem.h [new file with mode: 0644]
src/gcrypt/rsa.c
src/gcrypt/rsa.h
src/gcrypt/rsagen.c
src/gcrypt/rsagen.h [deleted file]
test/algorithms.test

index 42efd60..c38752a 100644 (file)
@@ -27,8 +27,9 @@ AC_DEFUN([tinc_LIBGCRYPT],
   )
 
   AC_CHECK_LIB(gcrypt, gcry_cipher_encrypt,
-    [LIBS="-lgcrypt $LIBS"],
-    [AC_MSG_ERROR([libgcrypt libraries not found.])]
+    [LIBS="-lgcrypt -lgpg-error $LIBS"],
+    [AC_MSG_ERROR([libgcrypt libraries not found.])],
+    [-lgpg-error]
   )
 
   AC_DEFINE(HAVE_LIBGCRYPT, 1, [enable libgcrypt support])
index a1d3423..9ee5f67 100644 (file)
@@ -244,12 +244,14 @@ tincd_SOURCES += \
        gcrypt/cipher.c \
        gcrypt/crypto.c \
        gcrypt/digest.c gcrypt/digest.h \
+       gcrypt/pem.c gcrypt/pem.h \
        gcrypt/prf.c \
        gcrypt/rsa.c
 tinc_SOURCES += \
        gcrypt/cipher.c \
        gcrypt/crypto.c \
        gcrypt/digest.c gcrypt/digest.h \
+       gcrypt/pem.c gcrypt/pem.h \
        gcrypt/prf.c \
        gcrypt/rsa.c \
        gcrypt/rsagen.c
@@ -259,11 +261,11 @@ sptps_test_SOURCES += \
        gcrypt/digest.c gcrypt/digest.h \
        gcrypt/prf.c
 sptps_keypair_SOURCES += \
-       openssl/crypto.c
+       gcrypt/crypto.c
 sptps_speed_SOURCES += \
-       openssl/crypto.c \
-       openssl/digest.c openssl/digest.h \
-       openssl/prf.c
+       gcrypt/crypto.c \
+       gcrypt/digest.c gcrypt/digest.h \
+       gcrypt/prf.c
 else
 tincd_SOURCES += \
        nolegacy/crypto.c \
index d8e4cc5..0f7b008 100644 (file)
@@ -36,20 +36,20 @@ static struct {
        {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB, 93},
        {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB, 94},
 
-       {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 418},
-       {"aes", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 419},
-       {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CFB, 421},
-       {NULL, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OFB, 420},
-
-       {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, 422},
-       {"aes192", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, 423},
-       {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, 425},
-       {NULL, GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, 424},
-
-       {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 426},
-       {"aes256", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 427},
-       {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 429},
-       {NULL, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 428},
+       {"aes-128-ecb", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 418},
+       {"aes-128-cbc", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 419},
+       {"aes-128-cfb", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CFB, 421},
+       {"aes-128-ofb", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OFB, 420},
+
+       {"aes-192-ecb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, 422},
+       {"aes-192-cbc", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, 423},
+       {"aes-192-cfb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, 425},
+       {"aes-192-ofb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, 424},
+
+       {"aes-256-ecb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 426},
+       {"aes-256-cbc", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 427},
+       {"aes-256-cfb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 429},
+       {"aes-256-ofb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 428},
 };
 
 static bool nametocipher(const char *name, int *algo, int *mode) {
@@ -152,9 +152,39 @@ void cipher_close(cipher_t *cipher) {
 }
 
 size_t cipher_keylength(const cipher_t *cipher) {
+       if(!cipher) {
+               return 0;
+       }
+
        return cipher->keylen + cipher->blklen;
 }
 
+uint64_t cipher_budget(const cipher_t *cipher) {
+       if(!cipher) {
+               return UINT64_MAX; // NULL cipher
+       }
+
+       size_t ivlen = cipher->blklen;
+       size_t blklen = cipher->blklen;
+
+       size_t len = blklen > 1
+                    ? blklen
+                    : ivlen > 1 ? ivlen : 8;
+       size_t bits = len * 4 - 1;
+
+       return bits < 64
+              ? UINT64_C(1) << bits
+              : UINT64_MAX;
+}
+
+size_t cipher_blocksize(const cipher_t *cipher) {
+       if(!cipher || !cipher->blklen) {
+               return 1;
+       }
+
+       return cipher->blklen;
+}
+
 void cipher_get_key(const cipher_t *cipher, void *key) {
        memcpy(key, cipher->key, cipher->keylen + cipher->blklen);
 }
@@ -169,10 +199,14 @@ bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
 }
 
 bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) {
-       memcpy(cipher->key, key + len - cipher->keylen, cipher->keylen + cipher->blklen);
-       memcpy(cipher->key + cipher->keylen, key + len - cipher->keylen - cipher->blklen, cipher->blklen);
-
+       memcpy(cipher->key,
+              key + len - cipher->keylen,
+              cipher->keylen);
        gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
+
+       memcpy(cipher->key + cipher->keylen,
+              key + len - cipher->blklen - cipher->keylen,
+              cipher->blklen);
        gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
 
        return true;
@@ -277,6 +311,10 @@ bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *ou
 }
 
 int cipher_get_nid(const cipher_t *cipher) {
+       if(!cipher || !cipher->nid) {
+               return 0;
+       }
+
        return cipher->nid;
 }
 
index ad1f31d..c8d4b31 100644 (file)
@@ -166,13 +166,21 @@ bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const voi
 }
 
 int digest_get_nid(const digest_t *digest) {
+       if(!digest || !digest->nid) {
+               return 0;
+       }
+
        return digest->nid;
 }
 
 size_t digest_length(const digest_t *digest) {
+       if(!digest) {
+               return 0;
+       }
+
        return digest->maclength;
 }
 
 bool digest_active(const digest_t *digest) {
-       return digest->algo != GCRY_MD_NONE;
+       return digest && digest->algo != GCRY_MD_NONE;
 }
diff --git a/src/gcrypt/pem.c b/src/gcrypt/pem.c
new file mode 100644 (file)
index 0000000..b22d0e4
--- /dev/null
@@ -0,0 +1,141 @@
+#include "pem.h"
+#include "utils.h"
+
+// Base64 decoding table
+
+static const uint8_t b64dec[128] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+       0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+       0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+       0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+       0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+       0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+       0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+       0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+       0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+       0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
+       0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+       0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
+       0xff, 0xff
+};
+
+static const char b64enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                             "abcdefghijklmnopqrstuvwxyz"
+                             "0123456789+/";
+
+// Heavily based on code by Jouni Malinen <j@w1.fi>
+// https://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c
+static size_t b64encode(char *dst, const void *src, const size_t length) {
+       const uint8_t *end = src + length;
+       const uint8_t *in = src;
+       char *pos = dst;
+
+       while(end - in >= 3) {
+               *pos++ = b64enc[in[0] >> 2];
+               *pos++ = b64enc[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+               *pos++ = b64enc[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+               *pos++ = b64enc[in[2] & 0x3f];
+               in += 3;
+       }
+
+       if(end - in) {
+               *pos++ = b64enc[in[0] >> 2];
+
+               if(end - in == 1) {
+                       *pos++ = b64enc[(in[0] & 0x03) << 4];
+                       *pos++ = '=';
+               } else {
+                       *pos++ = b64enc[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+                       *pos++ = b64enc[(in[1] & 0x0f) << 2];
+               }
+
+               *pos++ = '=';
+       }
+
+       *pos = '\0';
+
+       return pos - dst;
+}
+
+
+bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size) {
+       if(fprintf(fp, "-----BEGIN %s-----\n", header) <= 0) {
+               return false;
+       }
+
+       char b64[B64_SIZE(size)];
+       const size_t b64len = b64encode(b64, buf, size);
+
+       for(char *p = b64; p < b64 + b64len; p += 64) {
+               if(fprintf(fp, "%.64s\n", p) <= 0) {
+                       return false;
+               }
+       }
+
+       return fprintf(fp, "-----END %s-----\n", header) > 0;
+}
+
+bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) {
+       bool decode = false;
+       char line[1024];
+       uint16_t word = 0;
+       int shift = 10;
+       size_t i, j = 0;
+
+       while(!feof(fp)) {
+               if(!fgets(line, sizeof(line), fp)) {
+                       return false;
+               }
+
+               if(!decode && !strncmp(line, "-----BEGIN ", 11)) {
+                       if(!strncmp(line + 11, header, strlen(header))) {
+                               decode = true;
+                       }
+
+                       continue;
+               }
+
+               if(decode && !strncmp(line, "-----END", 8)) {
+                       break;
+               }
+
+               if(!decode) {
+                       continue;
+               }
+
+               for(i = 0; line[i] >= ' '; i++) {
+                       if((signed char)line[i] < 0 || b64dec[(int)line[i]] == 0xff) {
+                               break;
+                       }
+
+                       word |= b64dec[(int)line[i]] << shift;
+                       shift -= 6;
+
+                       if(shift <= 2) {
+                               if(j > size) {
+                                       errno = ENOMEM;
+                                       return false;
+                               }
+
+                               buf[j++] = word >> 8;
+                               word = (uint16_t)(word << 8);
+                               shift += 8;
+                       }
+               }
+       }
+
+       if(outsize) {
+               *outsize = j;
+       }
+
+       return true;
+}
diff --git a/src/gcrypt/pem.h b/src/gcrypt/pem.h
new file mode 100644 (file)
index 0000000..119c114
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef TINC_GCRYPT_PEM_H
+#define TINC_GCRYPT_PEM_H
+
+#include "../system.h"
+
+bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize);
+bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size);
+
+#endif // TINC_GCRYPT_PEM_H
index c026968..5d12985 100644 (file)
 
 #include "logger.h"
 #include "rsa.h"
-
-// Base64 decoding table
-
-static const uint8_t b64d[128] = {
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
-       0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
-       0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
-       0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
-       0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
-       0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
-       0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-       0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
-       0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
-       0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
-       0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
-       0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
-       0x31, 0x32, 0x33, 0xff, 0xff, 0xff,
-       0xff, 0xff
-};
-
-// PEM encoding/decoding functions
-
-static bool pem_decode(FILE *fp, const char *header, uint8_t *buf, size_t size, size_t *outsize) {
-       bool decode = false;
-       char line[1024];
-       uint16_t word = 0;
-       int shift = 10;
-       size_t i, j = 0;
-
-       while(!feof(fp)) {
-               if(!fgets(line, sizeof(line), fp)) {
-                       return false;
-               }
-
-               if(!decode && !strncmp(line, "-----BEGIN ", 11)) {
-                       if(!strncmp(line + 11, header, strlen(header))) {
-                               decode = true;
-                       }
-
-                       continue;
-               }
-
-               if(decode && !strncmp(line, "-----END", 8)) {
-                       break;
-               }
-
-               if(!decode) {
-                       continue;
-               }
-
-               for(i = 0; line[i] >= ' '; i++) {
-                       if((signed char)line[i] < 0 || b64d[(int)line[i]] == 0xff) {
-                               break;
-                       }
-
-                       word |= b64d[(int)line[i]] << shift;
-                       shift -= 6;
-
-                       if(shift <= 2) {
-                               if(j > size) {
-                                       errno = ENOMEM;
-                                       return false;
-                               }
-
-                               buf[j++] = word >> 8;
-                               word <<= 8;
-                               shift += 8;
-                       }
-               }
-       }
-
-       if(outsize) {
-               *outsize = j;
-       }
-
-       return true;
-}
-
+#include "pem.h"
+#include "xalloc.h"
 
 // BER decoding functions
 
@@ -154,7 +70,7 @@ static size_t ber_read_len(unsigned char **p, size_t *buflen) {
                }
 
                while(len--) {
-                       result <<= 8;
+                       result = (size_t)(result << 8);
                        result |= *(*p)++;
                        (*buflen)--;
                }
@@ -201,38 +117,40 @@ static bool ber_read_mpi(unsigned char **p, size_t *buflen, gcry_mpi_t *mpi) {
        return mpi ? !err : true;
 }
 
-bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e) {
-       gcry_error_t err = 0;
+rsa_t *rsa_set_hex_public_key(char *n, char *e) {
+       rsa_t *rsa = xzalloc(sizeof(rsa_t));
 
-       err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
-             ? : gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL);
+       gcry_error_t err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
+                          ? : gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL);
 
        if(err) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
+               free(rsa);
                return false;
        }
 
-       return true;
+       return rsa;
 }
 
-bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d) {
-       gcry_error_t err = 0;
+rsa_t *rsa_set_hex_private_key(char *n, char *e, char *d) {
+       rsa_t *rsa = xzalloc(sizeof(rsa_t));
 
-       err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
-             ? : gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL)
-             ? : gcry_mpi_scan(&rsa->d, GCRYMPI_FMT_HEX, d, 0, NULL);
+       gcry_error_t err = gcry_mpi_scan(&rsa->n, GCRYMPI_FMT_HEX, n, 0, NULL)
+                          ? : gcry_mpi_scan(&rsa->e, GCRYMPI_FMT_HEX, e, 0, NULL)
+                          ? : gcry_mpi_scan(&rsa->d, GCRYMPI_FMT_HEX, d, 0, NULL);
 
        if(err) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading RSA public key: %s", gcry_strerror(errno));
+               free(rsa);
                return false;
        }
 
-       return true;
+       return rsa;
 }
 
 // Read PEM RSA keys
 
-bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
+rsa_t *rsa_read_pem_public_key(FILE *fp) {
        uint8_t derbuf[8096], *derp = derbuf;
        size_t derlen;
 
@@ -241,18 +159,21 @@ bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp) {
                return NULL;
        }
 
+       rsa_t *rsa = xzalloc(sizeof(rsa_t));
+
        if(!ber_read_sequence(&derp, &derlen, NULL)
                        || !ber_read_mpi(&derp, &derlen, &rsa->n)
                        || !ber_read_mpi(&derp, &derlen, &rsa->e)
                        || derlen) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while decoding RSA public key");
+               free(rsa);
                return NULL;
        }
 
-       return true;
+       return rsa;
 }
 
-bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
+rsa_t *rsa_read_pem_private_key(FILE *fp) {
        uint8_t derbuf[8096], *derp = derbuf;
        size_t derlen;
 
@@ -261,6 +182,8 @@ bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
                return NULL;
        }
 
+       rsa_t *rsa = xzalloc(sizeof(rsa_t));
+
        if(!ber_read_sequence(&derp, &derlen, NULL)
                        || !ber_read_mpi(&derp, &derlen, NULL)
                        || !ber_read_mpi(&derp, &derlen, &rsa->n)
@@ -273,10 +196,11 @@ bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp) {
                        || !ber_read_mpi(&derp, &derlen, NULL) // u
                        || derlen) {
                logger(DEBUG_ALWAYS, LOG_ERR, "Error while decoding RSA private key");
+               free(rsa);
                return NULL;
        }
 
-       return true;
+       return rsa;
 }
 
 size_t rsa_size(rsa_t *rsa) {
@@ -300,7 +224,7 @@ bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out) {
        size_t out_bytes = (gcry_mpi_get_nbits(outmpi) + 7) / 8;
        size_t pad = len - MIN(out_bytes, len);
 
-       while(pad--) {
+       for(; pad; --pad) {
                *(char *)out++ = 0;
        }
 
@@ -318,7 +242,7 @@ bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
 
        size_t pad = len - (gcry_mpi_get_nbits(outmpi) + 7) / 8;
 
-       while(pad--) {
+       for(; pad; --pad) {
                *(char *)out++ = 0;
        }
 
@@ -326,3 +250,21 @@ bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out) {
 
        return true;
 }
+
+void rsa_free(rsa_t *rsa) {
+       if(rsa) {
+               if(rsa->n) {
+                       gcry_mpi_release(rsa->n);
+               }
+
+               if(rsa->e) {
+                       gcry_mpi_release(rsa->e);
+               }
+
+               if(rsa->d) {
+                       gcry_mpi_release(rsa->d);
+               }
+
+               free(rsa);
+       }
+}
index 2ea5e65..c27f196 100644 (file)
@@ -28,12 +28,4 @@ typedef struct rsa {
        gcry_mpi_t d;
 } rsa_t;
 
-extern bool rsa_set_hex_public_key(rsa_t *rsa, char *n, char *e);
-extern bool rsa_set_hex_private_key(rsa_t *rsa, char *n, char *e, char *d);
-extern bool rsa_read_pem_public_key(rsa_t *rsa, FILE *fp);
-extern bool rsa_read_pem_private_key(rsa_t *rsa, FILE *fp);
-extern size_t rsa_size(rsa_t *rsa);
-extern bool rsa_public_encrypt(rsa_t *rsa, void *in, size_t len, void *out);
-extern bool rsa_private_decrypt(rsa_t *rsa, void *in, size_t len, void *out);
-
 #endif
index 030f2bc..acf96ac 100644 (file)
 #include "system.h"
 
 #include <gcrypt.h>
+#include <assert.h>
+
+#include "../rsagen.h"
+#include "xalloc.h"
+#include "rsa.h"
+#include "pem.h"
+
+// ASN.1 tags.
+typedef enum {
+       TAG_INTEGER = 2,
+       TAG_SEQUENCE = 16,
+} asn1_tag_t;
+
+static size_t der_tag_len(size_t n) {
+       if(n < 128) {
+               return 2;
+       }
 
-#include "rsagen.h"
+       if(n < 256) {
+               return 3;
+       }
 
-#if 0
-// Base64 encoding table
+       if(n < 65536) {
+               return 4;
+       }
 
-static const char b64e[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+       abort();
+}
 
-// PEM encoding
+static uint8_t *der_store_tag(uint8_t *p, asn1_tag_t tag, size_t n) {
+       if(tag == TAG_SEQUENCE) {
+               tag |= 0x20;
+       }
 
-static bool pem_encode(FILE *fp, const char *header, uint8_t *buf, size_t size) {
-       bool decode = false;
-       char line[1024];
-       uint32_t word = 0;
-       int shift = 0;
-       size_t i, j = 0;
+       *p++ = tag;
+
+       if(n < 128) {
+               *p++ = n;
+       } else if(n < 256) {
+               *p++ = 0x81;
+               *p++ = n;
+       } else if(n < 65536) {
+               *p++ = 0x82;
+               *p++ = n >> 8;
+               *p++ = n & 0xff;
+       } else {
+               abort();
+       }
 
-       fprintf(fp, "-----BEGIN %s-----\n", header);
+       return p;
+}
 
-       for(i = 0; i < size; i += 3) {
-               if(i <= size - 3) {
-                       word = buf[i] << 16 | buf[i + 1] << 8 | buf[i + 2];
-               } else {
-                       word = buf[i] << 16;
+static size_t der_fill(uint8_t *derbuf, bool is_private, const gcry_mpi_t mpi[], size_t num_mpi) {
+       size_t needed = 0;
+       size_t lengths[16] = {0};
 
-                       if(i == size - 2) {
-                               word |= buf[i + 1] << 8;
-                       }
-               }
+       assert(num_mpi > 0 && num_mpi < sizeof(lengths) / sizeof(*lengths));
 
-               line[j++] = b64e[(word >> 18)       ];
-               line[j++] = b64e[(word >> 12) & 0x3f];
-               line[j++] = b64e[(word >>  6) & 0x3f];
-               line[j++] = b64e[(word) & 0x3f];
+       if(is_private) {
+               // Add space for the version number.
+               needed += der_tag_len(1) + 1;
+       }
 
-               if(j >= 64) {
-                       line[j++] = '\n';
-                       line[j] = 0;
-                       fputs(line, fp);
-                       j = 0;
-               }
+       for(size_t i = 0; i < num_mpi; ++i) {
+               gcry_mpi_print(GCRYMPI_FMT_STD, NULL, 0, &lengths[i], mpi[i]);
+               needed += der_tag_len(lengths[i]) + lengths[i];
        }
 
-       if(size % 3 > 0) {
-               if(size % 3 > 1) {
-                       line[j++] = '=';
-               }
+       const size_t derlen = der_tag_len(needed) + needed;
+
+       uint8_t *der = derbuf;
+       der = der_store_tag(der, TAG_SEQUENCE, needed);
 
-               line[j++] = '=';
+       if(is_private) {
+               // Private key requires storing version number.
+               der = der_store_tag(der, TAG_INTEGER, 1);
+               *der++ = 0;
        }
 
-       if(j) {
-               line[j++] = '\n';
-               line[j] = 0;
-               fputs(line, fp);
+       for(size_t i = 0; i < num_mpi; ++i) {
+               const size_t len = lengths[i];
+               der = der_store_tag(der, TAG_INTEGER, len);
+               gcry_mpi_print(GCRYMPI_FMT_STD, der, len, NULL, mpi[i]);
+               der += len;
        }
 
-       fprintf(fp, "-----END %s-----\n", header);
+       assert(der - derbuf == derlen);
+       return derlen;
+}
+
+bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
+       uint8_t derbuf[8096];
+
+       gcry_mpi_t params[] = {
+               rsa->n,
+               rsa->e,
+       };
 
-       return true;
+       size_t derlen = der_fill(derbuf, false, params, sizeof(params) / sizeof(*params));
+
+       return pem_encode(fp, "RSA PUBLIC KEY", derbuf, derlen);
 }
 
+// Calculate p/q primes from n/e/d.
+static void get_p_q(gcry_mpi_t *p,
+                    gcry_mpi_t *q,
+                    const gcry_mpi_t n,
+                    const gcry_mpi_t e,
+                    const gcry_mpi_t d) {
+       const size_t nbits = gcry_mpi_get_nbits(n);
+
+       gcry_mpi_t k = gcry_mpi_new(nbits);
+       gcry_mpi_mul(k, e, d);
+       gcry_mpi_sub_ui(k, k, 1);
 
-// BER encoding functions
+       size_t t = 0;
 
-static bool ber_write_id(uint8_t **p, size_t *buflen, int id) {
-       if(*buflen <= 0) {
-               return false;
+       while(!gcry_mpi_test_bit(k, t)) {
+               ++t;
        }
 
-       if(id >= 0x1f) {
-               while(id) {
-                       if(*buflen <= 0) {
-                               return false;
-                       }
+       gcry_mpi_t g = gcry_mpi_new(nbits);
+       gcry_mpi_t gk = gcry_mpi_new(0);
+       gcry_mpi_t sq = gcry_mpi_new(0);
+       gcry_mpi_t rem = gcry_mpi_new(0);
+       gcry_mpi_t gcd = gcry_mpi_new(0);
 
-                       (*buflen)--;
-                       **p = id & 0x7f;
-                       id >>= 7;
+       while(true) {
+               gcry_mpi_t kt = gcry_mpi_copy(k);
+               gcry_mpi_randomize(g, nbits, GCRY_STRONG_RANDOM);
 
-                       if(id) {
-                               **p |= 0x80;
-                       }
+               size_t i;
 
-                       (*p)++;
-               }
-       } else {
-               (*buflen)--;
-               *(*p)++ = id;
-       }
+               for(i = 0; i < t; ++i) {
+                       gcry_mpi_rshift(kt, kt, 1);
+                       gcry_mpi_powm(gk, g, kt, n);
 
-       return true;
-}
+                       if(gcry_mpi_cmp_ui(gk, 1) != 0) {
+                               gcry_mpi_mul(sq, gk, gk);
+                               gcry_mpi_mod(rem, sq, n);
 
-static bool ber_write_len(uint8_t **p, size_t *buflen, size_t len) {
-       do {
-               if(*buflen <= 0) {
-                       return false;
+                               if(gcry_mpi_cmp_ui(rem, 1) == 0) {
+                                       break;
+                               }
+                       }
                }
 
-               (*buflen)--;
-               **p = len & 0x7f;
-               len >>= 7;
+               gcry_mpi_release(kt);
+
+               if(i < t) {
+                       gcry_mpi_sub_ui(gk, gk, 1);
+                       gcry_mpi_gcd(gcd, gk, n);
 
-               if(len) {
-                       **p |= 0x80;
+                       if(gcry_mpi_cmp_ui(gcd, 1) != 0) {
+                               break;
+                       }
                }
+       }
 
-               (*p)++;
-       } while(len);
+       gcry_mpi_release(k);
+       gcry_mpi_release(g);
+       gcry_mpi_release(gk);
+       gcry_mpi_release(sq);
+       gcry_mpi_release(rem);
 
-       return true;
+       *p = gcd;
+       *q = gcry_mpi_new(0);
+
+       gcry_mpi_div(*q, NULL, n, *p, 0);
 }
 
-static bool ber_write_sequence(uint8_t **p, size_t *buflen, uint8_t *seqbuf, size_t seqlen) {
-       if(!ber_write_id(p, buflen, 0x10) || !ber_write_len(p, buflen, seqlen) || *buflen < seqlen) {
-               return false;
+bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
+       gcry_mpi_t params[] = {
+               rsa->n,
+               rsa->e,
+               rsa->d,
+               NULL, // p
+               NULL, // q
+               gcry_mpi_new(0), // d mod (p-1)
+               gcry_mpi_new(0), // d mod (q-1)
+               gcry_mpi_new(0), // u = p^-1 mod q
+       };
+
+       // Indexes into params.
+       const size_t d = 2;
+       const size_t p = 3;
+       const size_t q = 4;
+       const size_t dp = 5;
+       const size_t dq = 6;
+       const size_t u = 7;
+
+       // Calculate p and q.
+       get_p_q(&params[p], &params[q], rsa->n, rsa->e, rsa->d);
+
+       // Swap p and q if q > p.
+       if(gcry_mpi_cmp(params[q], params[p]) > 0) {
+               gcry_mpi_swap(params[p], params[q]);
        }
 
-       memcpy(*p, seqbuf, seqlen);
-       *p += seqlen;
-       *buflen -= seqlen;
+       // Calculate u.
+       gcry_mpi_invm(params[u], params[p], params[q]);
 
-       return true;
-}
+       // Calculate d mod (p - 1).
+       gcry_mpi_sub_ui(params[dp], params[p], 1);
+       gcry_mpi_mod(params[dp], params[d], params[dp]);
 
-static bool ber_write_mpi(uint8_t **p, size_t *buflen, gcry_mpi_t mpi) {
-       uint8_t tmpbuf[1024];
-       size_t tmplen = sizeof(tmpbuf);
-       gcry_error_t err;
+       // Calculate d mod (q - 1).
+       gcry_mpi_sub_ui(params[dq], params[q], 1);
+       gcry_mpi_mod(params[dq], params[d], params[dq]);
 
-       err = gcry_mpi_aprint(GCRYMPI_FMT_USG, &tmpbuf, &tmplen, mpi);
+       uint8_t derbuf[8096];
+       const size_t nparams = sizeof(params) / sizeof(*params);
+       size_t derlen = der_fill(derbuf, true, params, nparams);
 
-       if(err) {
-               return false;
-       }
+       gcry_mpi_release(params[p]);
+       gcry_mpi_release(params[q]);
+       gcry_mpi_release(params[dp]);
+       gcry_mpi_release(params[dq]);
+       gcry_mpi_release(params[u]);
 
-       if(!ber_write_id(p, buflen, 0x02) || !ber_write_len(p, buflen, tmplen) || *buflen < tmplen) {
-               return false;
-       }
+       return pem_encode(fp, "RSA PRIVATE KEY", derbuf, derlen);
+}
 
-       memcpy(*p, tmpbuf, tmplen);
-       *p += tmplen;
-       *buflen -= tmplen;
+static gcry_mpi_t find_mpi(const gcry_sexp_t rsa, const char *token) {
+       gcry_sexp_t sexp = gcry_sexp_find_token(rsa, token, 1);
+
+       if(!sexp) {
+               fprintf(stderr, "Token %s not found in RSA S-expression.\n", token);
+               return NULL;
+       }
 
-       return true;
+       gcry_mpi_t mpi = gcry_sexp_nth_mpi(sexp, 1, GCRYMPI_FMT_USG);
+       gcry_sexp_release(sexp);
+       return mpi;
 }
 
-// Write PEM RSA keys
+rsa_t *rsa_generate(size_t bits, unsigned long exponent) {
+       gcry_sexp_t s_params;
+       gcry_error_t err = gcry_sexp_build(&s_params, NULL,
+                                          "(genkey"
+                                          "  (rsa"
+                                          "    (nbits %u)"
+                                          "    (rsa-use-e %u)))",
+                                          bits,
+                                          exponent);
 
-bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
-       uint8_t derbuf1[8096];
-       uint8_t derbuf2[8096];
-       uint8_t *derp1 = derbuf1;
-       uint8_t *derp2 = derbuf2;
-       size_t derlen1 = sizeof(derbuf1);
-       size_t derlen2 = sizeof(derbuf2);
+       if(err) {
+               fprintf(stderr, "Error building keygen S-expression: %s.\n", gcry_strerror(err));
+               return NULL;
+       }
 
-       if(!ber_write_mpi(&derp1, &derlen1, &rsa->n)
-                       || !ber_write_mpi(&derp1, &derlen1, &rsa->e)
-                       || !ber_write_sequence(&derp2, &derlen2, derbuf1, derlen1)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while encoding RSA public key");
-               return false;
+       gcry_sexp_t s_key;
+       err = gcry_pk_genkey(&s_key, s_params);
+       gcry_sexp_release(s_params);
+
+       if(err) {
+               fprintf(stderr, "Error generating RSA key pair: %s.\n", gcry_strerror(err));
+               return NULL;
        }
 
-       if(!pem_encode(fp, "RSA PUBLIC KEY", derbuf2, derlen2)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Unable to write RSA public key: %s", strerror(errno));
-               return false;
+       // `gcry_sexp_extract_param` can replace everything below
+       // with a single line, but it's not available on CentOS 7.
+       gcry_sexp_t s_priv = gcry_sexp_find_token(s_key, "private-key", 0);
+
+       if(!s_priv) {
+               fprintf(stderr, "Private key not found in gcrypt result.\n");
+               gcry_sexp_release(s_key);
+               return NULL;
        }
 
-       return true;
-}
+       gcry_sexp_t s_rsa = gcry_sexp_find_token(s_priv, "rsa", 0);
 
-bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
-       uint8_t derbuf1[8096];
-       uint8_t derbuf2[8096];
-       uint8_t *derp1 = derbuf1;
-       uint8_t *derp2 = derbuf2;
-       size_t derlen1 = sizeof(derbuf1);
-       size_t derlen2 = sizeof(derbuf2);
-
-       if(!ber_write_mpi(&derp1, &derlen1, &bits)
-                       || ber_write_mpi(&derp1, &derlen1, &rsa->n) // modulus
-                       || ber_write_mpi(&derp1, &derlen1, &rsa->e) // public exponent
-                       || ber_write_mpi(&derp1, &derlen1, &rsa->d) // private exponent
-                       || ber_write_mpi(&derp1, &derlen1, &p)
-                       || ber_write_mpi(&derp1, &derlen1, &q)
-                       || ber_write_mpi(&derp1, &derlen1, &exp1)
-                       || ber_write_mpi(&derp1, &derlen1, &exp2)
-                       || ber_write_mpi(&derp1, &derlen1, &coeff)) {
-               logger(DEBUG_ALWAYS, LOG_ERR, "Error while encoding RSA private key");
-       }
-
-       return false;
-}
+       if(!s_rsa) {
+               fprintf(stderr, "RSA not found in gcrypt result.\n");
+               gcry_sexp_release(s_priv);
+               gcry_sexp_release(s_key);
+               return NULL;
+       }
 
-if(!pem_encode(fp, "RSA PRIVATE KEY", derbuf2, derlen2)) {
-       logger(DEBUG_ALWAYS, LOG_ERR, "Unable to write RSA private key: %s", strerror(errno));
-       return false;
-}
+       rsa_t *rsa = xzalloc(sizeof(*rsa));
 
-return true;
-}
-#endif
+       rsa->n = find_mpi(s_rsa, "n");
+       rsa->e = find_mpi(s_rsa, "e");
+       rsa->d = find_mpi(s_rsa, "d");
 
-bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp) {
-       return false;
-}
+       gcry_sexp_release(s_rsa);
+       gcry_sexp_release(s_priv);
+       gcry_sexp_release(s_key);
 
-bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp) {
-       return false;
-}
+       if(rsa->n && rsa->e && rsa->d) {
+               return rsa;
+       }
 
-bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent) {
-       fprintf(stderr, "Generating RSA keys with libgcrypt not implemented yet\n");
-       return false;
+       rsa_free(rsa);
+       return NULL;
 }
diff --git a/src/gcrypt/rsagen.h b/src/gcrypt/rsagen.h
deleted file mode 100644 (file)
index 4b49117..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef TINC_GCRYPT_RSAGEN_H
-#define TINC_GCRYPT_RSAGEN_H
-
-/*
-    rsagen.h -- RSA key generation and export
-    Copyright (C) 2008 Guus Sliepen <guus@tinc-vpn.org>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program 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 General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "rsa.h"
-
-extern bool rsa_generate(rsa_t *rsa, size_t bits, unsigned long exponent);
-extern bool rsa_write_pem_public_key(rsa_t *rsa, FILE *fp);
-extern bool rsa_write_pem_private_key(rsa_t *rsa, FILE *fp);
-
-#endif
index dcf055a..d069010 100755 (executable)
@@ -33,7 +33,7 @@ echo [STEP] Test various ciphers and digests
 # The full suite results in a large test matrix and it takes a lot of time to run.
 # The list was reduced to the strongest available algorithms (and "none").
 digests="none sha256 sha512"
-ciphers="none aes-256-cbc camellia-256-cbc"
+ciphers="none aes-256-cbc"
 
 for digest in $digests; do
   for cipher in $ciphers; do