From 77cd819058de43a5fcea54300dde50e03088c318 Mon Sep 17 00:00:00 2001 From: Kirill Isakov Date: Wed, 18 Aug 2021 14:51:10 +0600 Subject: [PATCH] Restore libgcrypt support. --- m4/libgcrypt.m4 | 5 +- src/Makefile.am | 10 +- src/gcrypt/cipher.c | 72 ++++++-- src/gcrypt/digest.c | 10 +- src/gcrypt/pem.c | 141 ++++++++++++++++ src/gcrypt/pem.h | 9 + src/gcrypt/rsa.c | 150 +++++------------ src/gcrypt/rsa.h | 8 - src/gcrypt/rsagen.c | 391 ++++++++++++++++++++++++++----------------- src/gcrypt/rsagen.h | 29 ---- test/algorithms.test | 2 +- 11 files changed, 505 insertions(+), 322 deletions(-) create mode 100644 src/gcrypt/pem.c create mode 100644 src/gcrypt/pem.h delete mode 100644 src/gcrypt/rsagen.h diff --git a/m4/libgcrypt.m4 b/m4/libgcrypt.m4 index 42efd60f..c38752aa 100644 --- a/m4/libgcrypt.m4 +++ b/m4/libgcrypt.m4 @@ -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]) diff --git a/src/Makefile.am b/src/Makefile.am index a1d34239..9ee5f677 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/gcrypt/cipher.c b/src/gcrypt/cipher.c index d8e4cc51..0f7b008d 100644 --- a/src/gcrypt/cipher.c +++ b/src/gcrypt/cipher.c @@ -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; } diff --git a/src/gcrypt/digest.c b/src/gcrypt/digest.c index ad1f31d8..c8d4b314 100644 --- a/src/gcrypt/digest.c +++ b/src/gcrypt/digest.c @@ -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 index 00000000..b22d0e40 --- /dev/null +++ b/src/gcrypt/pem.c @@ -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 +// 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 index 00000000..119c114b --- /dev/null +++ b/src/gcrypt/pem.h @@ -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 diff --git a/src/gcrypt/rsa.c b/src/gcrypt/rsa.c index c026968a..5d12985d 100644 --- a/src/gcrypt/rsa.c +++ b/src/gcrypt/rsa.c @@ -23,92 +23,8 @@ #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); + } +} diff --git a/src/gcrypt/rsa.h b/src/gcrypt/rsa.h index 2ea5e650..c27f1967 100644 --- a/src/gcrypt/rsa.h +++ b/src/gcrypt/rsa.h @@ -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 diff --git a/src/gcrypt/rsagen.c b/src/gcrypt/rsagen.c index 030f2bcb..acf96acf 100644 --- a/src/gcrypt/rsagen.c +++ b/src/gcrypt/rsagen.c @@ -20,220 +20,299 @@ #include "system.h" #include +#include + +#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(¶ms[p], ¶ms[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 index 4b491173..00000000 --- a/src/gcrypt/rsagen.h +++ /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 - - 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 diff --git a/test/algorithms.test b/test/algorithms.test index dcf055a0..d0690101 100755 --- a/test/algorithms.test +++ b/test/algorithms.test @@ -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 -- 2.20.1