X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilcpkcs1.c;h=57f25e675fa2b597926db3bd163c85cb2de2ed60;hb=410642a14d4185abd75715cee3f5177cd55b1ceb;hp=bfdded05218f814f7ab93838cbe1d20bf3caae70;hpb=4a2208546ba22e3da99dc1a2adcf2102246d6c6f;p=crypto.git diff --git a/lib/silccrypt/silcpkcs1.c b/lib/silccrypt/silcpkcs1.c index bfdded05..57f25e67 100644 --- a/lib/silccrypt/silcpkcs1.c +++ b/lib/silccrypt/silcpkcs1.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2003 Pekka Riikonen + Copyright (C) 2003 - 2006 Pekka Riikonen 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 @@ -19,7 +19,9 @@ /* $Id$ */ #include "silc.h" -#include "silcpkcs1.h" +#include "rsa.h" + +/************************** PKCS #1 message format ***************************/ /* Minimum padding in block */ #define SILC_PKCS1_MIN_PADDING 8 @@ -34,11 +36,11 @@ success. */ SilcBool silc_pkcs1_encode(SilcPkcs1BlockType bt, - const unsigned char *data, - SilcUInt32 data_len, - unsigned char *dest_data, - SilcUInt32 dest_data_size, - SilcRng rng) + const unsigned char *data, + SilcUInt32 data_len, + unsigned char *dest_data, + SilcUInt32 dest_data_size, + SilcRng rng) { SilcInt32 padlen; int i; @@ -46,7 +48,8 @@ SilcBool silc_pkcs1_encode(SilcPkcs1BlockType bt, SILC_LOG_DEBUG(("PKCS#1 encoding, bt %d", bt)); if (!data || !dest_data || - dest_data_size < 3 || dest_data_size < data_len) { + dest_data_size < SILC_PKCS1_MIN_PADDING + 3 || + dest_data_size < data_len) { SILC_LOG_DEBUG(("Data to be encoded is too long")); return FALSE; } @@ -97,11 +100,11 @@ SilcBool silc_pkcs1_encode(SilcPkcs1BlockType bt, Returns TRUE on success. */ SilcBool silc_pkcs1_decode(SilcPkcs1BlockType bt, - const unsigned char *data, - SilcUInt32 data_len, - unsigned char *dest_data, - SilcUInt32 dest_data_size, - SilcUInt32 *dest_len) + const unsigned char *data, + SilcUInt32 data_len, + unsigned char *dest_data, + SilcUInt32 dest_data_size, + SilcUInt32 *dest_len) { int i = 0; @@ -158,3 +161,537 @@ SilcBool silc_pkcs1_decode(SilcPkcs1BlockType bt, return TRUE; } + + +/***************************** PKCS #1 PKCS API ******************************/ + +/* Generates RSA key pair. */ + +SilcBool silc_pkcs1_generate_key(SilcUInt32 keylen, + SilcRng rng, + void **ret_public_key, + void **ret_private_key) +{ + SilcUInt32 prime_bits = keylen / 2; + SilcMPInt p, q; + SilcBool found = FALSE; + + if (keylen < 768 || keylen > 16384) + return FALSE; + + silc_mp_init(&p); + silc_mp_init(&q); + + /* Find p and q */ + while (!found) { + silc_math_gen_prime(&p, prime_bits, FALSE, rng); + silc_math_gen_prime(&q, prime_bits, FALSE, rng); + if ((silc_mp_cmp(&p, &q)) != 0) + found = TRUE; + } + + /* If p is smaller than q, switch them */ + if ((silc_mp_cmp(&p, &q)) > 0) { + SilcMPInt hlp; + silc_mp_init(&hlp); + + silc_mp_set(&hlp, &p); + silc_mp_set(&p, &q); + silc_mp_set(&q, &hlp); + + silc_mp_uninit(&hlp); + } + + /* Generate the actual keys */ + if (!rsa_generate_keys(keylen, &p, &q, ret_public_key, ret_private_key)) + return FALSE; + + silc_mp_uninit(&p); + silc_mp_uninit(&q); + + return TRUE; +} + +/* Import PKCS #1 compliant public key */ + +SilcBool silc_pkcs1_import_public_key(unsigned char *key, + SilcUInt32 key_len, + void **ret_public_key) +{ + SilcAsn1 asn1 = NULL; + SilcBufferStruct alg_key; + RsaPublicKey *pubkey; + + if (!ret_public_key) + return FALSE; + + asn1 = silc_asn1_alloc(); + if (!asn1) + return FALSE; + + /* Allocate RSA public key */ + *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey)); + if (!pubkey) + goto err; + + /* Parse the PKCS #1 public key */ + silc_buffer_set(&alg_key, key, key_len); + if (!silc_asn1_decode(asn1, &alg_key, + SILC_ASN1_OPTS(SILC_ASN1_ALLOC), + SILC_ASN1_SEQUENCE, + SILC_ASN1_INT(&pubkey->n), + SILC_ASN1_INT(&pubkey->e), + SILC_ASN1_END, SILC_ASN1_END)) + goto err; + + /* Set key length */ + pubkey->bits = silc_mp_sizeinbase(&pubkey->n, 2); + + silc_asn1_free(asn1); + + return TRUE; + + err: + silc_asn1_free(asn1); + return FALSE; +} + +/* Export PKCS #1 compliant public key */ + +unsigned char *silc_pkcs1_export_public_key(void *public_key, + SilcUInt32 *ret_len) +{ + RsaPublicKey *key = public_key; + SilcAsn1 asn1 = NULL; + SilcBufferStruct alg_key; + unsigned char *ret; + + asn1 = silc_asn1_alloc(); + if (!asn1) + goto err; + + /* Encode to PKCS #1 public key */ + memset(&alg_key, 0, sizeof(alg_key)); + if (!silc_asn1_encode(asn1, &alg_key, + SILC_ASN1_OPTS(SILC_ASN1_ALLOC), + SILC_ASN1_SEQUENCE, + SILC_ASN1_INT(&key->n), + SILC_ASN1_INT(&key->e), + SILC_ASN1_END, SILC_ASN1_END)) + goto err; + + ret = silc_buffer_steal(&alg_key, ret_len); + silc_asn1_free(asn1); + + return ret; + + err: + if (asn1) + silc_asn1_free(asn1); + return NULL; +} + +/* Returns key length */ + +SilcUInt32 silc_pkcs1_public_key_bitlen(void *public_key) +{ + RsaPublicKey *key = public_key; + return key->bits; +} + +/* Copy public key */ + +void *silc_pkcs1_public_key_copy(void *public_key) +{ + RsaPublicKey *key = public_key, *new_key; + + new_key = silc_calloc(1, sizeof(*new_key)); + if (!new_key) + return NULL; + + silc_mp_init(&new_key->n); + silc_mp_init(&new_key->e); + silc_mp_set(&new_key->n, &key->n); + silc_mp_set(&new_key->e, &key->e); + new_key->bits = key->bits; + + return new_key; +} + +/* Compare public keys */ + +SilcBool silc_pkcs1_public_key_compare(void *key1, void *key2) +{ + RsaPublicKey *k1 = key1, *k2 = key2; + + if (k1->bits != k2->bits) + return FALSE; + if (silc_mp_cmp(&k1->e, &k2->e) != 0) + return FALSE; + if (silc_mp_cmp(&k1->n, &k2->n) != 0) + return FALSE; + + return TRUE; +} + +/* Frees public key */ + +void silc_pkcs1_public_key_free(void *public_key) +{ + RsaPublicKey *key = public_key; + + silc_mp_uninit(&key->n); + silc_mp_uninit(&key->e); + silc_free(key); +} + +/* Import PKCS #1 compliant private key */ + +SilcBool silc_pkcs1_import_private_key(unsigned char *key, + SilcUInt32 key_len, + void **ret_private_key) +{ + SilcAsn1 asn1; + SilcBufferStruct alg_key; + RsaPrivateKey *privkey; + + if (!ret_private_key) + return FALSE; + + asn1 = silc_asn1_alloc(); + if (!asn1) + return FALSE; + + /* Allocate RSA private key */ + *ret_private_key = privkey = silc_calloc(1, sizeof(*privkey)); + if (!privkey) + goto err; + + /* Parse the PKCS #1 private key */ + silc_buffer_set(&alg_key, key, key_len); + if (!silc_asn1_decode(asn1, &alg_key, + SILC_ASN1_OPTS(SILC_ASN1_ALLOC), + SILC_ASN1_SEQUENCE, + SILC_ASN1_INT(NULL), + SILC_ASN1_INT(&privkey->n), + SILC_ASN1_INT(&privkey->e), + SILC_ASN1_INT(&privkey->d), + SILC_ASN1_INT(&privkey->p), + SILC_ASN1_INT(&privkey->q), + SILC_ASN1_INT(&privkey->dP), + SILC_ASN1_INT(&privkey->dQ), + SILC_ASN1_INT(&privkey->qP), + SILC_ASN1_END, SILC_ASN1_END)) + goto err; + + /* Set key length */ + privkey->bits = silc_mp_sizeinbase(&privkey->n, 2); + + silc_asn1_free(asn1); + + return TRUE; + + err: + silc_asn1_free(asn1); + return FALSE; +} + +/* Export PKCS #1 compliant private key */ + +unsigned char *silc_pkcs1_export_private_key(void *private_key, + SilcUInt32 *ret_len) +{ + RsaPrivateKey *key = private_key; + SilcAsn1 asn1; + SilcBufferStruct alg_key; + SilcMPInt version; + unsigned char *ret; + + asn1 = silc_asn1_alloc(); + if (!asn1) + return FALSE; + + /* Encode to PKCS #1 private key */ + silc_mp_init(&version); + silc_mp_set_ui(&version, 0); + memset(&alg_key, 0, sizeof(alg_key)); + if (!silc_asn1_encode(asn1, &alg_key, + SILC_ASN1_OPTS(SILC_ASN1_ALLOC), + SILC_ASN1_SEQUENCE, + SILC_ASN1_INT(&version), + SILC_ASN1_INT(&key->n), + SILC_ASN1_INT(&key->e), + SILC_ASN1_INT(&key->d), + SILC_ASN1_INT(&key->p), + SILC_ASN1_INT(&key->q), + SILC_ASN1_INT(&key->dP), + SILC_ASN1_INT(&key->dQ), + SILC_ASN1_INT(&key->qP), + SILC_ASN1_END, SILC_ASN1_END)) + goto err; + silc_mp_uninit(&version); + + ret = silc_buffer_steal(&alg_key, ret_len); + silc_asn1_free(asn1); + + return ret; + + err: + silc_asn1_free(asn1); + return NULL; +} + +/* Returns key length */ + +SilcUInt32 silc_pkcs1_private_key_bitlen(void *private_key) +{ + RsaPrivateKey *key = private_key; + return key->bits; +} + +/* Frees private key */ + +void silc_pkcs1_private_key_free(void *private_key) +{ + RsaPrivateKey *key = private_key; + + silc_mp_uninit(&key->n); + silc_mp_uninit(&key->e); + silc_mp_uninit(&key->d); + silc_mp_uninit(&key->dP); + silc_mp_uninit(&key->dQ); + silc_mp_uninit(&key->qP); + silc_mp_uninit(&key->p); + silc_mp_uninit(&key->q); + silc_free(key); +} + +/* PKCS #1 RSA routines */ + +SilcBool silc_pkcs1_encrypt(void *public_key, + unsigned char *src, + SilcUInt32 src_len, + unsigned char *dst, + SilcUInt32 dst_size, + SilcUInt32 *ret_dst_len) +{ + RsaPublicKey *key = public_key; + SilcMPInt mp_tmp; + SilcMPInt mp_dst; + unsigned char padded[2048 + 1]; + SilcUInt32 len = (key->bits + 7) / 8; + + if (sizeof(padded) < len) + return FALSE; + if (dst_size < len) + return FALSE; + + /* Pad data */ + if (!silc_pkcs1_encode(SILC_PKCS1_BT_PUB, src, src_len, + padded, len, NULL)) + return FALSE; + + silc_mp_init(&mp_tmp); + silc_mp_init(&mp_dst); + + /* Data to MP */ + silc_mp_bin2mp(padded, len, &mp_tmp); + + /* Encrypt */ + rsa_public_operation(key, &mp_tmp, &mp_dst); + + /* MP to data */ + silc_mp_mp2bin_noalloc(&mp_dst, dst, len); + *ret_dst_len = len; + + memset(padded, 0, sizeof(padded)); + silc_mp_uninit(&mp_tmp); + silc_mp_uninit(&mp_dst); + + return TRUE; +} + +SilcBool silc_pkcs1_decrypt(void *private_key, + unsigned char *src, + SilcUInt32 src_len, + unsigned char *dst, + SilcUInt32 dst_size, + SilcUInt32 *ret_dst_len) +{ + RsaPrivateKey *key = private_key; + SilcMPInt mp_tmp; + SilcMPInt mp_dst; + unsigned char *padded, unpadded[2048 + 1]; + SilcUInt32 padded_len; + + if (dst_size < (key->bits + 7) / 8) + return FALSE; + + silc_mp_init(&mp_tmp); + silc_mp_init(&mp_dst); + + /* Data to MP */ + silc_mp_bin2mp(src, src_len, &mp_tmp); + + /* Decrypt */ + rsa_private_operation(key, &mp_tmp, &mp_dst); + + /* MP to data */ + padded = silc_mp_mp2bin(&mp_dst, (key->bits + 7) / 8, &padded_len); + + /* Unpad data */ + if (!silc_pkcs1_decode(SILC_PKCS1_BT_PUB, padded, padded_len, + unpadded, sizeof(unpadded), ret_dst_len)) { + memset(padded, 0, padded_len); + silc_free(padded); + silc_mp_uninit(&mp_tmp); + silc_mp_uninit(&mp_dst); + return FALSE; + } + + /* Copy to destination */ + memcpy(dst, unpadded, *ret_dst_len); + + memset(padded, 0, padded_len); + memset(unpadded, 0, sizeof(unpadded)); + silc_free(padded); + silc_mp_uninit(&mp_tmp); + silc_mp_uninit(&mp_dst); + + return TRUE; +} + +SilcBool silc_pkcs1_sign(void *private_key, + unsigned char *src, + SilcUInt32 src_len, + unsigned char *signature, + SilcUInt32 signature_size, + SilcUInt32 *ret_signature_len, + SilcHash hash) +{ + return FALSE; +} + +SilcBool silc_pkcs1_verify(void *public_key, + unsigned char *signature, + SilcUInt32 signature_len, + unsigned char *data, + SilcUInt32 data_len, + SilcHash hash) +{ + return FALSE; +} + +/* PKCS #1 sign without hash oid */ + +SilcBool silc_pkcs1_sign_no_oid(void *private_key, + unsigned char *src, + SilcUInt32 src_len, + unsigned char *signature, + SilcUInt32 signature_size, + SilcUInt32 *ret_signature_len, + SilcHash hash) +{ + RsaPrivateKey *key = private_key; + SilcMPInt mp_tmp; + SilcMPInt mp_dst; + unsigned char padded[2048 + 1], hashr[SILC_HASH_MAXLEN]; + SilcUInt32 len = (key->bits + 7) / 8; + + if (sizeof(padded) < len) + return FALSE; + if (signature_size < len) + return FALSE; + + /* Compute hash if requested */ + if (hash) { + silc_hash_make(hash, src, src_len, hashr); + src = hashr; + src_len = silc_hash_len(hash); + } + + /* Pad data */ + if (!silc_pkcs1_encode(SILC_PKCS1_BT_PRV1, src, src_len, + padded, len, NULL)) + return FALSE; + + silc_mp_init(&mp_tmp); + silc_mp_init(&mp_dst); + + /* Data to MP */ + silc_mp_bin2mp(padded, len, &mp_tmp); + + /* Sign */ + rsa_private_operation(key, &mp_tmp, &mp_dst); + + /* MP to data */ + silc_mp_mp2bin_noalloc(&mp_dst, signature, len); + *ret_signature_len = len; + + memset(padded, 0, sizeof(padded)); + silc_mp_uninit(&mp_tmp); + silc_mp_uninit(&mp_dst); + if (hash) + memset(hashr, 0, sizeof(hashr)); + + return TRUE; +} + +/* PKCS #1 verify without hash oid */ + +SilcBool silc_pkcs1_verify_no_oid(void *public_key, + unsigned char *signature, + SilcUInt32 signature_len, + unsigned char *data, + SilcUInt32 data_len, + SilcHash hash) +{ + RsaPublicKey *key = public_key; + int ret = TRUE; + SilcMPInt mp_tmp2; + SilcMPInt mp_dst; + unsigned char *verify, unpadded[2048 + 1], hashr[SILC_HASH_MAXLEN]; + SilcUInt32 verify_len, len = (key->bits + 7) / 8; + + silc_mp_init(&mp_tmp2); + silc_mp_init(&mp_dst); + + /* Format the signature into MP int */ + silc_mp_bin2mp(signature, signature_len, &mp_tmp2); + + /* Verify */ + rsa_public_operation(key, &mp_tmp2, &mp_dst); + + /* MP to data */ + verify = silc_mp_mp2bin(&mp_dst, len, &verify_len); + + /* Unpad data */ + if (!silc_pkcs1_decode(SILC_PKCS1_BT_PRV1, verify, verify_len, + unpadded, sizeof(unpadded), &len)) { + memset(verify, 0, verify_len); + silc_free(verify); + silc_mp_uninit(&mp_tmp2); + silc_mp_uninit(&mp_dst); + return FALSE; + } + + /* Hash data if requested */ + if (hash) { + silc_hash_make(hash, data, data_len, hashr); + data = hashr; + } + + /* Compare */ + if (memcmp(data, unpadded, len)) + ret = FALSE; + + memset(verify, 0, verify_len); + memset(unpadded, 0, sizeof(unpadded)); + silc_free(verify); + silc_mp_uninit(&mp_tmp2); + silc_mp_uninit(&mp_dst); + if (hash) + memset(hashr, 0, sizeof(hashr)); + + return ret; +}