From: Pekka Riikonen Date: Tue, 23 Jan 2007 14:50:19 +0000 (+0000) Subject: Added SILC Public Key version 2 support (V= identifier support). X-Git-Tag: silc.client.1.1.beta1~51 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=cf7c57c5507866dab44cc92c9ee772cfc80eddbd Added SILC Public Key version 2 support (V= identifier support). Added PKCS #1 with hash OID support. --- diff --git a/lib/silccrypt/silchash.c b/lib/silccrypt/silchash.c index bf8cde8f..a9729c93 100644 --- a/lib/silccrypt/silchash.c +++ b/lib/silccrypt/silchash.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2006 Pekka Riikonen + Copyright (C) 1997 - 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 @@ -38,14 +38,17 @@ SilcDList silc_hash_list = NULL; /* Default hash functions for silc_hash_register_default(). */ const SilcHashObject silc_default_hash[] = { - { "sha256", 32, 64, silc_sha256_init, silc_sha256_update, silc_sha256_final, + { "sha256", "2.16.840.1.101.3.4.2.1", + 32, 64, silc_sha256_init, silc_sha256_update, silc_sha256_final, silc_sha256_transform, silc_sha256_context_len }, - { "sha1", 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final, + { "sha1", "1.3.14.3.2.26", + 20, 64, silc_sha1_init, silc_sha1_update, silc_sha1_final, silc_sha1_transform, silc_sha1_context_len }, - { "md5", 16, 64, silc_md5_init, silc_md5_update, silc_md5_final, + { "md5", "1.2.840.113549.2.5", + 16, 64, silc_md5_init, silc_md5_update, silc_md5_final, silc_md5_transform, silc_md5_context_len }, - { NULL, 0, 0, NULL, NULL, NULL, NULL, NULL } + { NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL } }; /* Registers a new hash function into the SILC. This function is used at @@ -76,6 +79,11 @@ SilcBool silc_hash_register(const SilcHashObject *hash) silc_free(new); return FALSE; } + new->oid = strdup(hash->oid); + if (!new->oid) { + silc_free(new); + return FALSE; + } new->hash_len = hash->hash_len; new->block_len = hash->block_len; new->init = hash->init; @@ -166,7 +174,7 @@ SilcBool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash) { SilcHashObject *entry = NULL; - SILC_LOG_DEBUG(("Allocating new hash object")); + SILC_LOG_DEBUG(("Allocating new hash %s", name)); #ifndef SILC_SYMBIAN if (silc_hash_list) { @@ -205,6 +213,51 @@ SilcBool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash) return FALSE; } +/* Allocate hash by OID string */ + +SilcBool silc_hash_alloc_by_oid(const char *oid, SilcHash *new_hash) +{ + SilcHashObject *entry = NULL; + + SILC_LOG_DEBUG(("Allocating new hash %s", oid)); + +#ifndef SILC_SYMBIAN + if (silc_hash_list) { + silc_dlist_start(silc_hash_list); + while ((entry = silc_dlist_get(silc_hash_list)) != SILC_LIST_END) { + if (!strcmp(entry->oid, oid)) + break; + } + } +#else + { + /* On EPOC which don't have globals we check our constant hash list. */ + int i; + for (i = 0; silc_default_hash[i].oid; i++) { + if (!strcmp(silc_default_hash[i].oid, oid)) { + entry = (SilcHashObject *)&(silc_default_hash[i]); + break; + } + } + } +#endif /* SILC_SYMBIAN */ + + if (entry) { + *new_hash = silc_calloc(1, sizeof(**new_hash)); + if (!(*new_hash)) + return FALSE; + (*new_hash)->hash = entry; + (*new_hash)->context = silc_calloc(1, entry->context_len()); + if (!(*new_hash)->context) { + silc_free(*new_hash); + return FALSE; + } + return TRUE; + } + + return FALSE; +} + /* Free's the SilcHash object */ void silc_hash_free(SilcHash hash) @@ -236,6 +289,13 @@ const char *silc_hash_get_name(SilcHash hash) return hash->hash->name; } +/* Returns hash OID string */ + +const char *silc_hash_get_oid(SilcHash hash) +{ + return hash->hash->oid; +} + /* Returns TRUE if hash algorithm `name' is supported. */ SilcBool silc_hash_is_supported(const unsigned char *name) diff --git a/lib/silccrypt/silchash.h b/lib/silccrypt/silchash.h index 698ce15a..dee139ef 100644 --- a/lib/silccrypt/silchash.h +++ b/lib/silccrypt/silchash.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005 Pekka Riikonen + Copyright (C) 1997 - 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 @@ -65,8 +65,9 @@ typedef struct SilcHashStruct *SilcHash; ***/ typedef struct { char *name; - SilcUInt32 hash_len; - SilcUInt32 block_len; + char *oid; + SilcUInt16 hash_len; + SilcUInt16 block_len; void (*init)(void *); void (*update)(void *, const unsigned char *, SilcUInt32); @@ -197,6 +198,21 @@ SilcBool silc_hash_unregister_all(void); ***/ SilcBool silc_hash_alloc(const unsigned char *name, SilcHash *new_hash); +/****f* silccrypt/SilcHashAPI/silc_hash_alloc_by_oid + * + * SYNOPSIS + * + * SilcBool silc_hash_alloc_by_oid(const char *oid, SilcHash *new_hash); + * + * DESCRIPTION + * + * Same as silc_hash_alloc but allocates the hash algorithm by the + * hash algorithm OID string indicated by `oid'. Returns FALSE if such + * hash function does not exist. + * + ***/ +SilcBool silc_hash_alloc_by_oid(const char *oid, SilcHash *new_hash); + /****f* silccrypt/SilcHashAPI/silc_hash_free * * SYNOPSIS @@ -278,6 +294,20 @@ SilcUInt32 silc_hash_block_len(SilcHash hash); ***/ const char *silc_hash_get_name(SilcHash hash); +/****f* silccrypt/SilcHashAPI/silc_hash_get_oid + * + * SYNOPSIS + * + * const char *silc_hash_get_name(SilcHash hash); + * + * DESCRIPTION + * + * Returns the hash OID string. Returns NULL if the hash doesn't have + * OID string. Use strlen() to get the OID string length. + * + ***/ +const char *silc_hash_get_oid(SilcHash hash); + /****f* silccrypt/SilcHashAPI/silc_hash_make * * SYNOPSIS diff --git a/lib/silccrypt/silcpk.c b/lib/silccrypt/silcpk.c index b934bdc8..e012e370 100644 --- a/lib/silccrypt/silcpk.c +++ b/lib/silccrypt/silcpk.c @@ -25,7 +25,6 @@ /* Generate new SILC key pair. */ SilcBool silc_pkcs_silc_generate_key(const char *algorithm, - const char *scheme, SilcUInt32 bits_key_len, const char *identifier, SilcRng rng, @@ -36,6 +35,7 @@ SilcBool silc_pkcs_silc_generate_key(const char *algorithm, SilcSILCPrivateKey privkey; const SilcPKCSAlgorithm *alg; const SilcPKCSObject *pkcs; + SilcUInt32 version; SILC_LOG_DEBUG(("Generating SILC %s key pair with key length %d bits", algorithm, bits_key_len)); @@ -47,21 +47,28 @@ SilcBool silc_pkcs_silc_generate_key(const char *algorithm, if (!pkcs) return FALSE; - alg = silc_pkcs_find_algorithm(algorithm, scheme); - if (!alg) - return FALSE; - /* Allocate SILC public key */ pubkey = silc_calloc(1, sizeof(*pubkey)); if (!pubkey) return FALSE; - pubkey->pkcs = alg; /* Decode identifier */ - if (!silc_pkcs_silc_decode_identifier(identifier, &pubkey->identifier)) { + if (!silc_pkcs_silc_decode_identifier(identifier, &pubkey->identifier)) + return FALSE; + + if (pubkey->identifier.version && atoi(pubkey->identifier.version) >= 2) + version = 2; + else + version = 1; + + /* Allocate algorithm */ + alg = silc_pkcs_find_algorithm(algorithm, (version == 1 ? "pkcs1-no-oid" : + "pkcs1")); + if (!alg) { silc_free(pubkey); return FALSE; } + pubkey->pkcs = alg; /* Allocate SILC private key */ privkey = silc_calloc(1, sizeof(*privkey)); @@ -169,6 +176,8 @@ SilcBool silc_pkcs_silc_decode_identifier(const char *identifier, ident->org = strdup(item + strcspn(cp, "=") + 1); else if (strstr(item, "C=")) ident->country = strdup(item + strcspn(cp, "=") + 1); + else if (strstr(item, "V=")) + ident->version = strdup(item + strcspn(cp, "=") + 1); cp += len; if (strlen(cp) < 1) @@ -189,100 +198,102 @@ SilcBool silc_pkcs_silc_decode_identifier(const char *identifier, char *silc_pkcs_silc_encode_identifier(char *username, char *host, char *realname, char *email, - char *org, char *country) + char *org, char *country, + char *version) { - SilcBuffer buf; + SilcBufferStruct buf; char *identifier; - SilcUInt32 len, tlen = 0; if (!username || !host) return NULL; - - len = (username ? strlen(username) : 0) + - (host ? strlen(host) : 0) + - (realname ? strlen(realname) : 0) + - (email ? strlen(email) : 0) + - (org ? strlen(org) : 0) + - (country ? strlen(country) : 0); - - if (len < 3) + if (strlen(username) < 3 || strlen(host) < 3) return NULL; - len += 3 + 5 + 5 + 4 + 4 + 4; - buf = silc_buffer_alloc(len); - if (!buf) - return NULL; - silc_buffer_pull_tail(buf, len); + memset(&buf, 0, sizeof(buf)); - if (username) { - silc_buffer_format(buf, + if (username) + silc_buffer_format(&buf, + SILC_STR_ADVANCE, SILC_STR_UI32_STRING("UN="), SILC_STR_UI32_STRING(username), SILC_STR_END); - silc_buffer_pull(buf, 3 + strlen(username)); - tlen = 3 + strlen(username); - } - if (host) { - silc_buffer_format(buf, + if (host) + silc_buffer_format(&buf, + SILC_STR_ADVANCE, SILC_STR_UI32_STRING(", "), SILC_STR_UI32_STRING("HN="), SILC_STR_UI32_STRING(host), SILC_STR_END); - silc_buffer_pull(buf, 5 + strlen(host)); - tlen += 5 + strlen(host); - } - if (realname) { - silc_buffer_format(buf, + if (realname) + silc_buffer_format(&buf, + SILC_STR_ADVANCE, SILC_STR_UI32_STRING(", "), SILC_STR_UI32_STRING("RN="), SILC_STR_UI32_STRING(realname), SILC_STR_END); - silc_buffer_pull(buf, 5 + strlen(realname)); - tlen += 5 + strlen(realname); - } - if (email) { - silc_buffer_format(buf, + if (email) + silc_buffer_format(&buf, + SILC_STR_ADVANCE, SILC_STR_UI32_STRING(", "), SILC_STR_UI32_STRING("E="), SILC_STR_UI32_STRING(email), SILC_STR_END); - silc_buffer_pull(buf, 4 + strlen(email)); - tlen += 4 + strlen(email); - } - if (org) { - silc_buffer_format(buf, + if (org) + silc_buffer_format(&buf, + SILC_STR_ADVANCE, SILC_STR_UI32_STRING(", "), SILC_STR_UI32_STRING("O="), SILC_STR_UI32_STRING(org), SILC_STR_END); - silc_buffer_pull(buf, 4 + strlen(org)); - tlen += 4 + strlen(org); - } - if (country) { - silc_buffer_format(buf, + if (country) + silc_buffer_format(&buf, + SILC_STR_ADVANCE, SILC_STR_UI32_STRING(", "), SILC_STR_UI32_STRING("C="), SILC_STR_UI32_STRING(country), SILC_STR_END); - silc_buffer_pull(buf, 4 + strlen(country)); - tlen += 4 + strlen(country); + + if (version) { + if (strlen(version) > 1 || !isdigit(version[0])) { + silc_buffer_purge(&buf); + return NULL; + } + silc_buffer_format(&buf, + SILC_STR_ADVANCE, + SILC_STR_UI32_STRING(", "), + SILC_STR_UI32_STRING("V="), + SILC_STR_UI32_STRING(version), + SILC_STR_END); } - silc_buffer_push(buf, buf->data - buf->head); - identifier = silc_calloc(tlen + 1, sizeof(*identifier)); - if (!identifier) - return NULL; - memcpy(identifier, buf->data, tlen); - silc_buffer_free(buf); + silc_buffer_format(&buf, SILC_STR_UI_CHAR(0), SILC_STR_END); + identifier = silc_buffer_steal(&buf, NULL); return identifier; } +/* Return SILC public key version */ + +int silc_pkcs_silc_public_key_version(SilcPublicKey public_key) +{ + SilcSILCPublicKey silc_pubkey; + + if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) + return -1; + + silc_pubkey = public_key->public_key; + + /* If version identifire is not present it is version 1. */ + if (!silc_pubkey->identifier.version) + return 1; + + return atoi(silc_pubkey->identifier.version); +} /*************************** Public key routines *****************************/ @@ -413,13 +424,25 @@ int silc_pkcs_silc_import_public_key(unsigned char *key, if (!asn1) goto err; + SILC_LOG_DEBUG(("Public key version %s", + (!silc_pubkey->identifier.version ? " 1" : + silc_pubkey->identifier.version))); + if (!strcmp(pkcs_name, "rsa")) { /* Parse the SILC RSA public key */ SilcUInt32 e_len, n_len; SilcMPInt n, e; - /* Get PKCS object */ - pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid"); + /* Get PKCS object. Different PKCS #1 scheme is used with different + versions. */ + if (!silc_pubkey->identifier.version || + atoi(silc_pubkey->identifier.version) <= 1) { + /* Version 1 */ + pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid"); + } else { + /* Version 2 and newer */ + pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1"); + } if (!pkcs) { SILC_LOG_DEBUG(("Unsupported PKCS algorithm")); goto err; @@ -579,7 +602,8 @@ unsigned char *silc_pkcs_silc_export_public_key(void *public_key, silc_pubkey->identifier.realname, silc_pubkey->identifier.email, silc_pubkey->identifier.org, - silc_pubkey->identifier.country); + silc_pubkey->identifier.country, + silc_pubkey->identifier.version); if (!identifier) goto err; @@ -742,6 +766,12 @@ SilcBool silc_pkcs_silc_public_key_compare(void *key1, void *key2) strcmp(k1->identifier.country, k2->identifier.country))) return FALSE; + if ((k1->identifier.version && !k2->identifier.version) || + (!k1->identifier.version && k2->identifier.version) || + (k1->identifier.version && k2->identifier.version && + strcmp(k1->identifier.version, k2->identifier.version))) + return FALSE; + return k1->pkcs->public_key_compare(k1->public_key, k2->public_key); } @@ -759,6 +789,7 @@ void silc_pkcs_silc_public_key_free(void *public_key) silc_free(silc_pubkey->identifier.email); silc_free(silc_pubkey->identifier.org); silc_free(silc_pubkey->identifier.country); + silc_free(silc_pubkey->identifier.version); silc_free(silc_pubkey); } @@ -912,6 +943,7 @@ SilcBool silc_pkcs_silc_import_private_key_file(unsigned char *filedata, /* Private key version */ #define SILC_PRIVATE_KEY_VERSION_1 0x82171273 +#define SILC_PRIVATE_KEY_VERSION_2 0xf911a3d1 /* Imports SILC implementation style private key */ @@ -976,17 +1008,8 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, unsigned char *tmp; SilcUInt32 len, ver; - /* Get PKCS object */ - pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid"); - if (!pkcs) { - SILC_LOG_DEBUG(("Unsupported PKCS algorithm")); - goto err; - } - silc_privkey->pkcs = pkcs; - if (keydata_len < 4) goto err; - silc_buffer_set(&k, key_data, keydata_len); /* Get version. Key without the version is old style private key @@ -997,8 +1020,10 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); - if (ver != SILC_PRIVATE_KEY_VERSION_1) { + if (ver != SILC_PRIVATE_KEY_VERSION_1 && + ver != SILC_PRIVATE_KEY_VERSION_2) { len = ver; + ver = 0; } else { if (silc_buffer_unformat(&k, SILC_STR_UI_INT(&len), @@ -1007,9 +1032,28 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, silc_buffer_pull(&k, 4); } + /* Get PKCS object. Different PKCS #1 scheme is used with different + versions. */ + if (ver == 0 || ver == SILC_PRIVATE_KEY_VERSION_1) { + /* Version 0 and 1 */ + pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1-no-oid"); + } else { + /* Version 2 and newer */ + pkcs = silc_pkcs_find_algorithm(pkcs_name, "pkcs1"); + } + if (!pkcs) { + SILC_LOG_DEBUG(("Unsupported PKCS algorithm")); + goto err; + } + silc_privkey->pkcs = pkcs; + + SILC_LOG_DEBUG(("Private key version %s", + (ver == SILC_PRIVATE_KEY_VERSION_1 ? "1" : + ver == SILC_PRIVATE_KEY_VERSION_2 ? "2" : "0"))); + /* Get e */ if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&e); @@ -1023,7 +1067,7 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&n); @@ -1037,7 +1081,7 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&d); @@ -1051,7 +1095,7 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&dp); @@ -1065,14 +1109,14 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&dq); silc_mp_bin2mp(tmp, len, &dq); silc_buffer_pull(&k, len); - if (ver != SILC_PRIVATE_KEY_VERSION_1) { + if (ver == 0) { /* Old version */ /* Get pQ len */ @@ -1104,7 +1148,7 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&qp); @@ -1119,7 +1163,7 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&p); @@ -1133,14 +1177,14 @@ int silc_pkcs_silc_import_private_key(unsigned char *key, goto err; silc_buffer_pull(&k, 4); if (silc_buffer_unformat(&k, - SILC_STR_UI_XNSTRING(&tmp, len), + SILC_STR_DATA(&tmp, len), SILC_STR_END) < 0) goto err; silc_mp_init(&q); silc_mp_bin2mp(tmp, len, &q); silc_buffer_pull(&k, len); - if (ver != SILC_PRIVATE_KEY_VERSION_1) { + if (ver == 0) { /* Old version. Compute to new version */ SILC_LOG_DEBUG(("Old version private key")); silc_mp_init(&qp); @@ -1566,6 +1610,7 @@ SilcBool silc_pkcs_silc_sign(void *private_key, unsigned char *signature, SilcUInt32 signature_size, SilcUInt32 *ret_signature_len, + SilcBool compute_hash, SilcHash hash) { SilcSILCPrivateKey silc_privkey = private_key; @@ -1576,7 +1621,7 @@ SilcBool silc_pkcs_silc_sign(void *private_key, return silc_privkey->pkcs->sign(silc_privkey->private_key, src, src_len, signature, signature_size, - ret_signature_len, hash); + ret_signature_len, compute_hash, hash); } /* Verifies as specified in SILC protocol specification */ diff --git a/lib/silccrypt/silcpk.h b/lib/silccrypt/silcpk.h index d704b7dd..3e1e464c 100644 --- a/lib/silccrypt/silcpk.h +++ b/lib/silccrypt/silcpk.h @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2006 Pekka Riikonen + Copyright (C) 1997 - 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 @@ -50,6 +50,7 @@ typedef struct { char *email; char *org; char *country; + char *version; } *SilcPublicKeyIdentifier, SilcPublicKeyIdentifierStruct; /***/ @@ -99,7 +100,6 @@ typedef struct { * SYNOPSIS * * SilcBool silc_pkcs_silc_generate_key(const char *algorithm, - * const char *scheme, * SilcUInt32 bits_key_len, * const char *identifier, * SilcRng rng, @@ -116,12 +116,10 @@ typedef struct { * * // Generate RSA key pair with 2048 bit key length, using PKCS #1 * // no OID scheme. - * silc_pkcs_silc_generate_key("rsa", "pkcs1-no-oid", 2048, - * rng, &public_key, &private_key); + * silc_pkcs_silc_generate_key("rsa", 2048, rng, &public_key, &private_key); * ***/ SilcBool silc_pkcs_silc_generate_key(const char *algorithm, - const char *scheme, SilcUInt32 bits_key_len, const char *identifier, SilcRng rng, @@ -134,7 +132,8 @@ SilcBool silc_pkcs_silc_generate_key(const char *algorithm, * * char *silc_pkcs_silc_encode_identifier(char *username, char *host, * char *realname, char *email, - * char *org, char *country) + * char *org, char *country, + * char *version); * * DESCRIPTION * @@ -146,7 +145,8 @@ SilcBool silc_pkcs_silc_generate_key(const char *algorithm, ***/ char *silc_pkcs_silc_encode_identifier(char *username, char *host, char *realname, char *email, - char *org, char *country); + char *org, char *country, + char *version); /****f* silccrypt/SilcPubkeyAPI/silc_pkcs_silc_decode_identifier * @@ -165,4 +165,19 @@ char *silc_pkcs_silc_encode_identifier(char *username, char *host, SilcBool silc_pkcs_silc_decode_identifier(const char *identifier, SilcPublicKeyIdentifier ident); +/****f* silccrypt/SilcPubkeyAPI/silc_pkcs_silc_public_key_version + * + * SYNOPSIS + * + * int silc_pkcs_silc_public_key_version(SilcPublicKey public_key); + * + * DESCRIPTION + * + * Returns the verison of the SILC Public Key indicated by `public_key'. + * Returns -1 if the `public_key' is not a SILC Public Key and the + * version number otherwise. + * + ***/ +int silc_pkcs_silc_public_key_version(SilcPublicKey public_key); + #endif /* SILCPK_H */ diff --git a/lib/silccrypt/silcpk_i.h b/lib/silccrypt/silcpk_i.h index 9f6aa76e..e0275610 100644 --- a/lib/silccrypt/silcpk_i.h +++ b/lib/silccrypt/silcpk_i.h @@ -83,6 +83,7 @@ SilcBool silc_pkcs_silc_sign(void *private_key, unsigned char *signature, SilcUInt32 signature_size, SilcUInt32 *ret_signature_len, + SilcBool compute_hash, SilcHash hash); SilcBool silc_pkcs_silc_verify(void *public_key, unsigned char *signature, diff --git a/lib/silccrypt/silcpkcs.c b/lib/silccrypt/silcpkcs.c index 457deb80..6bd2eaac 100644 --- a/lib/silccrypt/silcpkcs.c +++ b/lib/silccrypt/silcpkcs.c @@ -593,10 +593,11 @@ SilcBool silc_pkcs_decrypt(SilcPrivateKey private_key, SilcBool silc_pkcs_sign(SilcPrivateKey private_key, unsigned char *src, SilcUInt32 src_len, unsigned char *dst, SilcUInt32 dst_size, - SilcUInt32 *dst_len, SilcHash hash) + SilcUInt32 *dst_len, SilcBool compute_hash, + SilcHash hash) { return private_key->pkcs->sign(private_key->private_key, src, src_len, - dst, dst_size, dst_len, hash); + dst, dst_size, dst_len, compute_hash, hash); } /* Verifies signature */ diff --git a/lib/silccrypt/silcpkcs.h b/lib/silccrypt/silcpkcs.h index dab35b30..857824f4 100644 --- a/lib/silccrypt/silcpkcs.h +++ b/lib/silccrypt/silcpkcs.h @@ -170,6 +170,7 @@ typedef struct { unsigned char *signature, SilcUInt32 signature_size, SilcUInt32 *ret_signature_len, + SilcBool compute_hash, SilcHash hash); SilcBool (*verify)(void *public_key, unsigned char *signature, @@ -278,6 +279,7 @@ struct SilcPKCSObjectStruct { unsigned char *signature, SilcUInt32 signature_size, SilcUInt32 *ret_signature_len, + SilcBool compute_hash, SilcHash hash); SilcBool (*verify)(void *public_key, unsigned char *signature, @@ -687,18 +689,21 @@ SilcBool silc_pkcs_decrypt(SilcPrivateKey private_key, * SilcBool silc_pkcs_sign(SilcPrivateKey private_key, * unsigned char *src, SilcUInt32 src_len, * unsigned char *dst, SilcUInt32 dst_size, - * SilcUInt32 *dst_len, SilcHash hash); + * SilcUInt32 *dst_len, SilcBool compute_hash, + * SilcHash hash); * * DESCRIPTION * * Generates signature with the private key. Returns FALSE on error. - * If `hash' is non-NULL the `src' will be hashed before signing. + * If `compute_hash' is TRUE the `hash' will be used to compute a + * digest over the `src'. The `hash' must always be valid. * ***/ SilcBool silc_pkcs_sign(SilcPrivateKey private_key, unsigned char *src, SilcUInt32 src_len, unsigned char *dst, SilcUInt32 dst_size, - SilcUInt32 *dst_len, SilcHash hash); + SilcUInt32 *dst_len, SilcBool compute_hash, + SilcHash hash); /****f* silccrypt/SilcPKCSAPI/silc_pkcs_verify * diff --git a/lib/silccrypt/silcpkcs1.c b/lib/silccrypt/silcpkcs1.c index a438ef3c..283f1ab3 100644 --- a/lib/silccrypt/silcpkcs1.c +++ b/lib/silccrypt/silcpkcs1.c @@ -247,7 +247,7 @@ int 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); @@ -392,7 +392,7 @@ int 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); @@ -574,9 +574,84 @@ SilcBool silc_pkcs1_sign(void *private_key, 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. */ @@ -588,6 +663,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; } @@ -599,6 +779,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; @@ -607,13 +788,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); @@ -640,7 +823,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; @@ -656,12 +839,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); @@ -692,10 +877,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)); diff --git a/lib/silccrypt/silcpkcs1_i.h b/lib/silccrypt/silcpkcs1_i.h index d23b65b8..fd5ecb4f 100644 --- a/lib/silccrypt/silcpkcs1_i.h +++ b/lib/silccrypt/silcpkcs1_i.h @@ -59,6 +59,7 @@ SilcBool silc_pkcs1_sign(void *private_key, unsigned char *signature, SilcUInt32 signature_size, SilcUInt32 *ret_signature_len, + SilcBool compute_hash, SilcHash hash); SilcBool silc_pkcs1_verify(void *public_key, unsigned char *signature, @@ -72,6 +73,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); SilcBool silc_pkcs1_verify_no_oid(void *public_key, unsigned char *signature,