)
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])
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
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 \
{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) {
}
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);
}
}
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;
}
int cipher_get_nid(const cipher_t *cipher) {
+ if(!cipher || !cipher->nid) {
+ return 0;
+ }
+
return cipher->nid;
}
}
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;
}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
#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
}
while(len--) {
- result <<= 8;
+ result = (size_t)(result << 8);
result |= *(*p)++;
(*buflen)--;
}
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;
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;
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)
|| !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) {
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;
}
size_t pad = len - (gcry_mpi_get_nbits(outmpi) + 7) / 8;
- while(pad--) {
+ for(; pad; --pad) {
*(char *)out++ = 0;
}
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);
+ }
+}
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
#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(¶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;
}
+++ /dev/null
-#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
# 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