X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilccrypt%2Fsilcpkcs1.c;h=0a75f800cc6ac0e17618ff01f84de40be75f61ec;hb=b36495161037e52ad993202da5d3df1837235d24;hp=0094e13089bf348da224efe520ac4d600be0ad84;hpb=53c44ff07775925292f656f524b62d6553d0c0d2;p=silc.git diff --git a/lib/silccrypt/silcpkcs1.c b/lib/silccrypt/silcpkcs1.c index 0094e130..0a75f800 100644 --- a/lib/silccrypt/silcpkcs1.c +++ b/lib/silccrypt/silcpkcs1.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 2003 - 2006 Pekka Riikonen + Copyright (C) 2003 - 2007 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 @@ -20,6 +20,7 @@ #include "silc.h" #include "rsa.h" +#include "silcpkcs1_i.h" /************************** PKCS #1 message format ***************************/ @@ -74,14 +75,15 @@ SilcBool silc_pkcs1_encode(SilcPkcs1BlockType bt, case SILC_PKCS1_BT_PUB: /* Encryption */ + if (!rng) { + SILC_LOG_ERROR(("Cannot encrypt: random number generator not provided")); + return FALSE; + } /* It is guaranteed this routine does not return zero byte. */ - if (rng) - for (i = 2; i < padlen; i++) - dest_data[i] = silc_rng_get_byte_fast(rng); - else - for (i = 2; i < padlen; i++) - dest_data[i] = silc_rng_global_get_byte_fast(); + for (i = 2; i < padlen; i++) + dest_data[i] = silc_rng_get_byte_fast(rng); + break; } @@ -106,7 +108,7 @@ SilcBool silc_pkcs1_decode(SilcPkcs1BlockType bt, SilcUInt32 dest_data_size, SilcUInt32 *dest_len) { - int i = 0; + SilcUInt32 i = 0; SILC_LOG_DEBUG(("PKCS#1 decoding, bt %d", bt)); @@ -139,11 +141,19 @@ SilcBool silc_pkcs1_decode(SilcPkcs1BlockType bt, } /* Sanity checks */ + if (i >= data_len) { + SILC_LOG_DEBUG(("Malformed block")); + return FALSE; + } + if (i < SILC_PKCS1_MIN_PADDING) { + SILC_LOG_DEBUG(("Malformed block")); + return FALSE; + } if (data[i++] != 0x00) { SILC_LOG_DEBUG(("Malformed block")); return FALSE; } - if (i - 1 < SILC_PKCS1_MIN_PADDING) { + if (i >= data_len) { SILC_LOG_DEBUG(("Malformed block")); return FALSE; } @@ -203,7 +213,7 @@ SilcBool silc_pkcs1_generate_key(SilcUInt32 keylen, } /* Generate the actual keys */ - if (!rsa_generate_keys(keylen, &p, &q, ret_public_key, ret_private_key)) + if (!silc_rsa_generate_keys(keylen, &p, &q, ret_public_key, ret_private_key)) return FALSE; silc_mp_uninit(&p); @@ -214,20 +224,20 @@ SilcBool silc_pkcs1_generate_key(SilcUInt32 keylen, /* Import PKCS #1 compliant public key */ -SilcBool silc_pkcs1_import_public_key(unsigned char *key, - SilcUInt32 key_len, - void **ret_public_key) +int 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; + return 0; asn1 = silc_asn1_alloc(); if (!asn1) - return FALSE; + return 0; /* Allocate RSA public key */ *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey)); @@ -245,15 +255,16 @@ SilcBool silc_pkcs1_import_public_key(unsigned char *key, goto err; /* Set key length */ - pubkey->bits = silc_mp_sizeinbase(&pubkey->n, 2); + pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8; silc_asn1_free(asn1); - return TRUE; + return key_len; err: + silc_free(pubkey); silc_asn1_free(asn1); - return FALSE; + return 0; } /* Export PKCS #1 compliant public key */ @@ -347,9 +358,9 @@ void silc_pkcs1_public_key_free(void *public_key) /* Import PKCS #1 compliant private key */ -SilcBool silc_pkcs1_import_private_key(unsigned char *key, - SilcUInt32 key_len, - void **ret_private_key) +int silc_pkcs1_import_private_key(unsigned char *key, + SilcUInt32 key_len, + void **ret_private_key) { SilcAsn1 asn1; SilcBufferStruct alg_key; @@ -357,11 +368,11 @@ SilcBool silc_pkcs1_import_private_key(unsigned char *key, SilcUInt32 ver; if (!ret_private_key) - return FALSE; + return 0; asn1 = silc_asn1_alloc(); if (!asn1) - return FALSE; + return 0; /* Allocate RSA private key */ *ret_private_key = privkey = silc_calloc(1, sizeof(*privkey)); @@ -389,15 +400,16 @@ SilcBool silc_pkcs1_import_private_key(unsigned char *key, goto err; /* Set key length */ - privkey->bits = silc_mp_sizeinbase(&privkey->n, 2); + privkey->bits = ((silc_mp_sizeinbase(&privkey->n, 2) + 7) / 8) * 8; silc_asn1_free(asn1); - return TRUE; + return key_len; err: + silc_free(privkey); silc_asn1_free(asn1); - return FALSE; + return 0; } /* Export PKCS #1 compliant private key */ @@ -473,7 +485,8 @@ SilcBool silc_pkcs1_encrypt(void *public_key, SilcUInt32 src_len, unsigned char *dst, SilcUInt32 dst_size, - SilcUInt32 *ret_dst_len) + SilcUInt32 *ret_dst_len, + SilcRng rng) { RsaPublicKey *key = public_key; SilcMPInt mp_tmp; @@ -488,7 +501,7 @@ SilcBool silc_pkcs1_encrypt(void *public_key, /* Pad data */ if (!silc_pkcs1_encode(SILC_PKCS1_BT_PUB, src, src_len, - padded, len, NULL)) + padded, len, rng)) return FALSE; silc_mp_init(&mp_tmp); @@ -498,7 +511,7 @@ SilcBool silc_pkcs1_encrypt(void *public_key, silc_mp_bin2mp(padded, len, &mp_tmp); /* Encrypt */ - rsa_public_operation(key, &mp_tmp, &mp_dst); + silc_rsa_public_operation(key, &mp_tmp, &mp_dst); /* MP to data */ silc_mp_mp2bin_noalloc(&mp_dst, dst, len); @@ -534,7 +547,7 @@ SilcBool silc_pkcs1_decrypt(void *private_key, silc_mp_bin2mp(src, src_len, &mp_tmp); /* Decrypt */ - rsa_private_operation(key, &mp_tmp, &mp_dst); + silc_rsa_private_operation(key, &mp_tmp, &mp_dst); /* MP to data */ padded = silc_mp_mp2bin(&mp_dst, (key->bits + 7) / 8, &padded_len); @@ -561,17 +574,96 @@ SilcBool silc_pkcs1_decrypt(void *private_key, return TRUE; } +/* PKCS #1 sign with appendix, hash OID included in the signature */ + SilcBool silc_pkcs1_sign(void *private_key, unsigned char *src, SilcUInt32 src_len, unsigned char *signature, SilcUInt32 signature_size, SilcUInt32 *ret_signature_len, + SilcBool compute_hash, SilcHash hash) { - return FALSE; + RsaPrivateKey *key = private_key; + unsigned char padded[2048 + 1], hashr[SILC_HASH_MAXLEN]; + SilcMPInt mp_tmp; + SilcMPInt mp_dst; + SilcBufferStruct di; + SilcUInt32 len = (key->bits + 7) / 8; + const char *oid; + SilcAsn1 asn1; + + SILC_LOG_DEBUG(("Sign")); + + if (sizeof(padded) < len) + return FALSE; + if (signature_size < len) + return FALSE; + + oid = silc_hash_get_oid(hash); + if (!oid) + return FALSE; + + asn1 = silc_asn1_alloc(); + if (!asn1) + return FALSE; + + /* Compute hash */ + if (compute_hash) { + silc_hash_make(hash, src, src_len, hashr); + src = hashr; + src_len = silc_hash_len(hash); + } + + /* Encode digest info */ + memset(&di, 0, sizeof(di)); + if (!silc_asn1_encode(asn1, &di, + SILC_ASN1_SEQUENCE, + SILC_ASN1_SEQUENCE, + SILC_ASN1_OID(oid), + SILC_ASN1_NULL, + SILC_ASN1_END, + SILC_ASN1_OCTET_STRING(src, src_len), + SILC_ASN1_END, SILC_ASN1_END)) { + silc_asn1_free(asn1); + return FALSE; + } + SILC_LOG_HEXDUMP(("DigestInfo"), silc_buffer_data(&di), + silc_buffer_len(&di)); + + /* Pad data */ + if (!silc_pkcs1_encode(SILC_PKCS1_BT_PRV1, silc_buffer_data(&di), + silc_buffer_len(&di), padded, len, NULL)) { + silc_asn1_free(asn1); + return FALSE; + } + + silc_mp_init(&mp_tmp); + silc_mp_init(&mp_dst); + + /* Data to MP */ + silc_mp_bin2mp(padded, len, &mp_tmp); + + /* Sign */ + silc_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 (compute_hash) + memset(hashr, 0, sizeof(hashr)); + silc_asn1_free(asn1); + + return TRUE; } +/* PKCS #1 verification with appendix. */ + SilcBool silc_pkcs1_verify(void *public_key, unsigned char *signature, SilcUInt32 signature_len, @@ -579,6 +671,111 @@ SilcBool silc_pkcs1_verify(void *public_key, SilcUInt32 data_len, SilcHash hash) { + RsaPublicKey *key = public_key; + SilcBool ret = FALSE; + SilcMPInt mp_tmp2; + SilcMPInt mp_dst; + unsigned char *verify, unpadded[2048 + 1], hashr[SILC_HASH_MAXLEN]; + SilcUInt32 verify_len, len = (key->bits + 7) / 8; + SilcBufferStruct di, ldi; + SilcHash ihash = NULL; + SilcAsn1 asn1 = NULL; + char *oid; + + SILC_LOG_DEBUG(("Verify signature")); + + asn1 = silc_asn1_alloc(); + if (!asn1) + return FALSE; + + 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 */ + silc_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)) + goto err; + silc_buffer_set(&di, unpadded, len); + + /* If hash isn't given, allocate the one given in digest info */ + if (!hash) { + /* Decode digest info */ + if (!silc_asn1_decode(asn1, &di, + SILC_ASN1_OPTS(SILC_ASN1_ACCUMUL), + SILC_ASN1_SEQUENCE, + SILC_ASN1_SEQUENCE, + SILC_ASN1_OID(&oid), + SILC_ASN1_END, + SILC_ASN1_END, SILC_ASN1_END)) + goto err; + + if (!silc_hash_alloc_by_oid(oid, &ihash)) { + SILC_LOG_DEBUG(("Unknown OID %s", oid)); + goto err; + } + hash = ihash; + } + + /* Hash the data */ + silc_hash_make(hash, data, data_len, hashr); + data = hashr; + data_len = silc_hash_len(hash); + oid = (char *)silc_hash_get_oid(hash); + + /* Encode digest info for comparison */ + memset(&ldi, 0, sizeof(ldi)); + if (!silc_asn1_encode(asn1, &ldi, + SILC_ASN1_OPTS(SILC_ASN1_ACCUMUL), + SILC_ASN1_SEQUENCE, + SILC_ASN1_SEQUENCE, + SILC_ASN1_OID(oid), + SILC_ASN1_NULL, + SILC_ASN1_END, + SILC_ASN1_OCTET_STRING(data, data_len), + SILC_ASN1_END, SILC_ASN1_END)) + goto err; + + SILC_LOG_HEXDUMP(("DigestInfo remote"), silc_buffer_data(&di), + silc_buffer_len(&di)); + SILC_LOG_HEXDUMP(("DigestInfo local"), silc_buffer_data(&ldi), + silc_buffer_len(&ldi)); + + /* Compare */ + if (silc_buffer_len(&di) == silc_buffer_len(&ldi) && + !memcmp(silc_buffer_data(&di), silc_buffer_data(&ldi), + silc_buffer_len(&ldi))) + ret = TRUE; + + 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)); + if (ihash) + silc_hash_free(ihash); + silc_asn1_free(asn1); + + return ret; + + err: + memset(verify, 0, verify_len); + silc_free(verify); + silc_mp_uninit(&mp_tmp2); + silc_mp_uninit(&mp_dst); + if (ihash) + silc_hash_free(ihash); + silc_asn1_free(asn1); return FALSE; } @@ -590,6 +787,7 @@ SilcBool silc_pkcs1_sign_no_oid(void *private_key, unsigned char *signature, SilcUInt32 signature_size, SilcUInt32 *ret_signature_len, + SilcBool compute_hash, SilcHash hash) { RsaPrivateKey *key = private_key; @@ -598,13 +796,15 @@ SilcBool silc_pkcs1_sign_no_oid(void *private_key, unsigned char padded[2048 + 1], hashr[SILC_HASH_MAXLEN]; SilcUInt32 len = (key->bits + 7) / 8; + SILC_LOG_DEBUG(("Sign")); + if (sizeof(padded) < len) return FALSE; if (signature_size < len) return FALSE; /* Compute hash if requested */ - if (hash) { + if (compute_hash) { silc_hash_make(hash, src, src_len, hashr); src = hashr; src_len = silc_hash_len(hash); @@ -622,7 +822,7 @@ SilcBool silc_pkcs1_sign_no_oid(void *private_key, silc_mp_bin2mp(padded, len, &mp_tmp); /* Sign */ - rsa_private_operation(key, &mp_tmp, &mp_dst); + silc_rsa_private_operation(key, &mp_tmp, &mp_dst); /* MP to data */ silc_mp_mp2bin_noalloc(&mp_dst, signature, len); @@ -631,7 +831,7 @@ SilcBool silc_pkcs1_sign_no_oid(void *private_key, memset(padded, 0, sizeof(padded)); silc_mp_uninit(&mp_tmp); silc_mp_uninit(&mp_dst); - if (hash) + if (compute_hash) memset(hashr, 0, sizeof(hashr)); return TRUE; @@ -647,12 +847,14 @@ SilcBool silc_pkcs1_verify_no_oid(void *public_key, SilcHash hash) { RsaPublicKey *key = public_key; - int ret = TRUE; + SilcBool ret = FALSE; 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_LOG_DEBUG(("Verify signature")); + silc_mp_init(&mp_tmp2); silc_mp_init(&mp_dst); @@ -660,7 +862,7 @@ SilcBool silc_pkcs1_verify_no_oid(void *public_key, silc_mp_bin2mp(signature, signature_len, &mp_tmp2); /* Verify */ - rsa_public_operation(key, &mp_tmp2, &mp_dst); + silc_rsa_public_operation(key, &mp_tmp2, &mp_dst); /* MP to data */ verify = silc_mp_mp2bin(&mp_dst, len, &verify_len); @@ -683,10 +885,8 @@ SilcBool silc_pkcs1_verify_no_oid(void *public_key, } /* Compare */ - if (len != data_len) - ret = FALSE; - else if (memcmp(data, unpadded, len)) - ret = FALSE; + if (len == data_len && !memcmp(data, unpadded, len)) + ret = TRUE; memset(verify, 0, verify_len); memset(unpadded, 0, sizeof(unpadded));