diff options
Diffstat (limited to 'src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c')
-rw-r--r-- | src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c | 350 |
1 files changed, 313 insertions, 37 deletions
diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c index 8a89849cd..1f3e3072f 100644 --- a/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c +++ b/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c @@ -12,8 +12,6 @@ * 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. - * - * $Id: gmp_rsa_public_key.c 4345 2008-09-17 08:10:48Z martin $ */ #include <gmp.h> @@ -30,11 +28,7 @@ #include <asn1/asn1_parser.h> #include <asn1/pem.h> #include <crypto/hashers/hasher.h> - -/** - * defined in gmp_rsa_private_key.c - */ -extern chunk_t gmp_mpz_to_asn1(const mpz_t value); +#include <pgp/pgp.h> typedef struct private_gmp_rsa_public_key_t private_gmp_rsa_public_key_t; @@ -79,6 +73,12 @@ struct private_gmp_rsa_public_key_t { }; /** + * Shared functions defined in gmp_rsa_private_key.c + */ +extern chunk_t gmp_mpz_to_chunk(const mpz_t value); +extern chunk_t gmp_mpz_to_asn1(const mpz_t value); + +/** * RSAEP algorithm specified in PKCS#1. */ static chunk_t rsaep(private_gmp_rsa_public_key_t *this, chunk_t data) @@ -140,11 +140,10 @@ static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, /* remove any preceding 0-bytes from signature */ while (signature.len && *(signature.ptr) == 0x00) { - signature.len -= 1; - signature.ptr++; + signature = chunk_skip(signature, 1); } - if (signature.len > this->k) + if (signature.len == 0 || signature.len > this->k) { return INVALID_ARG; } @@ -163,8 +162,7 @@ static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, { goto end; } - em.ptr += 2; - em.len -= 2; + em = chunk_skip(em, 2); /* find magic 0x00 */ while (em.len > 0) @@ -172,8 +170,7 @@ static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, if (*em.ptr == 0x00) { /* found magic byte, stop */ - em.ptr++; - em.len--; + em = chunk_skip(em, 1); break; } else if (*em.ptr != 0xFF) @@ -181,8 +178,7 @@ static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, /* bad padding, decryption failed ?!*/ goto end; } - em.ptr++; - em.len--; + em = chunk_skip(em, 1); } if (em.len == 0) @@ -191,13 +187,24 @@ static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, goto end; } - /* parse ASN.1-based digestInfo */ - { + if (algorithm == HASH_UNKNOWN) + { /* IKEv1 signatures without digestInfo */ + if (em.len != data.len) + { + DBG1("hash size in signature is %u bytes instead of %u bytes", + em.len, data.len); + goto end; + } + success = memeq(em.ptr, data.ptr, data.len); + } + else + { /* IKEv2 and X.509 certificate signatures */ asn1_parser_t *parser; chunk_t object; int objectID; hash_algorithm_t hash_algorithm = HASH_UNKNOWN; + DBG2("signature verification:"); parser = asn1_parser_create(digestInfoObjects, em); while (parser->iterate(parser, &objectID, &object)) @@ -220,8 +227,7 @@ static bool verify_emsa_pkcs1_signature(private_gmp_rsa_public_key_t *this, parser->get_level(parser)+1, NULL); hash_algorithm = hasher_algorithm_from_oid(hash_oid); - if (hash_algorithm == HASH_UNKNOWN || - (algorithm != HASH_UNKNOWN && hash_algorithm != algorithm)) + if (hash_algorithm == HASH_UNKNOWN || hash_algorithm != algorithm) { DBG1("expected hash algorithm %N, but found %N (OID: %#B)", hash_algorithm_names, algorithm, @@ -289,7 +295,7 @@ static bool verify(private_gmp_rsa_public_key_t *this, signature_scheme_t scheme { switch (scheme) { - case SIGN_DEFAULT: /* default is EMSA-PKCS1 using included OID */ + case SIGN_RSA_EMSA_PKCS1_NULL: return verify_emsa_pkcs1_signature(this, HASH_UNKNOWN, data, signature); case SIGN_RSA_EMSA_PKCS1_MD5: return verify_emsa_pkcs1_signature(this, HASH_MD5, data, signature); @@ -308,12 +314,96 @@ static bool verify(private_gmp_rsa_public_key_t *this, signature_scheme_t scheme } } +#define MIN_PS_PADDING 8 + /** - * Implementation of public_key_t.get_keysize. + * Implementation of public_key_t.encrypt. + */ +static bool encrypt_(private_gmp_rsa_public_key_t *this, chunk_t plain, + chunk_t *crypto) +{ + chunk_t em; + u_char *pos; + int padding, i; + rng_t *rng; + + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (rng == NULL) + { + DBG1("no random generator available"); + return FALSE; + } + + /* number of pseudo-random padding octets */ + padding = this->k - plain.len - 3; + if (padding < MIN_PS_PADDING) + { + DBG1("pseudo-random padding must be at least %d octets", MIN_PS_PADDING); + return FALSE; + } + + /* padding according to PKCS#1 7.2.1 (RSAES-PKCS1-v1.5-ENCRYPT) */ + DBG2("padding %u bytes of data to the rsa modulus size of %u bytes", + plain.len, this->k); + em.len = this->k; + em.ptr = malloc(em.len); + pos = em.ptr; + *pos++ = 0x00; + *pos++ = 0x02; + + /* fill with pseudo random octets */ + rng->get_bytes(rng, padding, pos); + + /* replace zero-valued random octets */ + for (i = 0; i < padding; i++) + { + while (*pos == 0) + { + rng->get_bytes(rng, 1, pos); + } + pos++; + } + rng->destroy(rng); + + /* append the padding terminator */ + *pos++ = 0x00; + + /* now add the data */ + memcpy(pos, plain.ptr, plain.len); + DBG3("padded data before rsa encryption: %B", &em); + + /* rsa encryption using PKCS#1 RSAEP */ + *crypto = rsaep(this, em); + DBG3("rsa encrypted data: %B", crypto); + chunk_clear(&em); + return TRUE; +} + +/** + * Implementation of gmp_rsa_public_key.equals. */ -static bool encrypt(private_gmp_rsa_public_key_t *this, chunk_t crypto, chunk_t *plain) +static bool equals(private_gmp_rsa_public_key_t *this, public_key_t *other) { - DBG1("RSA public key encryption not implemented"); + identification_t *keyid; + + if (&this->public.interface == other) + { + return TRUE; + } + if (other->get_type(other) != KEY_RSA) + { + return FALSE; + } + keyid = other->get_id(other, ID_PUBKEY_SHA1); + if (keyid && keyid->equals(keyid, this->keyid)) + { + return TRUE; + } + keyid = other->get_id(other, ID_PUBKEY_INFO_SHA1); + if (keyid && keyid->equals(keyid, this->keyid_info)) + { + return TRUE; + } return FALSE; } @@ -326,6 +416,46 @@ static size_t get_keysize(private_gmp_rsa_public_key_t *this) } /** + * Build the PGP version 3 RSA key identifier from n and e using + * MD5 hashed modulus and exponent. Also used in rsa_private_key.c. + */ +static identification_t* gmp_rsa_build_pgp_v3_keyid(mpz_t n, mpz_t e) +{ + identification_t *keyid; + chunk_t modulus, mod, exponent, exp, hash; + hasher_t *hasher; + + hasher= lib->crypto->create_hasher(lib->crypto, HASH_MD5); + if (hasher == NULL) + { + DBG1("computation of PGP V3 keyid failed, no MD5 hasher is available"); + return NULL; + } + mod = modulus = gmp_mpz_to_chunk(n); + exp = exponent = gmp_mpz_to_chunk(e); + + /* remove leading zero bytes before hashing modulus and exponent */ + while (mod.len > 0 && *mod.ptr == 0x00) + { + mod.ptr++; + mod.len--; + } + while (exp.len > 0 && *exp.ptr == 0x00) + { + exp.ptr++; + exp.len--; + } + hasher->allocate_hash(hasher, mod, NULL); + hasher->allocate_hash(hasher, exp, &hash); + hasher->destroy(hasher); + keyid = identification_create_from_encoding(ID_KEY_ID, hash); + free(hash.ptr); + free(modulus.ptr); + free(exponent.ptr); + return keyid; +} + +/** * Implementation of public_key_t.get_id. */ static identification_t *get_id(private_gmp_rsa_public_key_t *this, @@ -337,6 +467,8 @@ static identification_t *get_id(private_gmp_rsa_public_key_t *this, return this->keyid_info; case ID_PUBKEY_SHA1: return this->keyid; + case ID_KEY_ID: + return gmp_rsa_build_pgp_v3_keyid(this->n, this->e); default: return NULL; } @@ -383,14 +515,15 @@ static private_gmp_rsa_public_key_t *gmp_rsa_public_key_create_empty() { private_gmp_rsa_public_key_t *this = malloc_thing(private_gmp_rsa_public_key_t); - this->public.interface.get_type = (key_type_t (*)(public_key_t *this))get_type; - this->public.interface.verify = (bool (*)(public_key_t *this, signature_scheme_t scheme, chunk_t data, chunk_t signature))verify; - this->public.interface.encrypt = (bool (*)(public_key_t *this, chunk_t crypto, chunk_t *plain))encrypt; - this->public.interface.get_keysize = (size_t (*) (public_key_t *this))get_keysize; - this->public.interface.get_id = (identification_t* (*) (public_key_t *this,id_type_t))get_id; - this->public.interface.get_encoding = (chunk_t(*)(public_key_t*))get_encoding; - this->public.interface.get_ref = (public_key_t* (*)(public_key_t *this))get_ref; - this->public.interface.destroy = (void (*)(public_key_t *this))destroy; + this->public.interface.get_type = (key_type_t (*) (public_key_t*))get_type; + this->public.interface.verify = (bool (*) (public_key_t*, signature_scheme_t, chunk_t, chunk_t))verify; + this->public.interface.encrypt = (bool (*) (public_key_t*, chunk_t, chunk_t*))encrypt_; + this->public.interface.equals = (bool (*) (public_key_t*, public_key_t*))equals; + this->public.interface.get_keysize = (size_t (*) (public_key_t*))get_keysize; + this->public.interface.get_id = (identification_t* (*) (public_key_t*, id_type_t))get_id; + this->public.interface.get_encoding = (chunk_t(*) (public_key_t*))get_encoding; + this->public.interface.get_ref = (public_key_t* (*) (public_key_t *this))get_ref; + this->public.interface.destroy = (void (*) (public_key_t *this))destroy; this->keyid = NULL; this->keyid_info = NULL; @@ -445,7 +578,7 @@ gmp_rsa_public_key_t *gmp_rsa_public_key_create_from_n_e(mpz_t n, mpz_t e) mpz_init_set(this->n, n); mpz_init_set(this->e, e); - this->k = (mpz_sizeinbase(this->n, 2) + 7) / 8; + this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; if (!gmp_rsa_public_key_build_id(this->n, this->e, &this->keyid, &this->keyid_info)) { @@ -469,9 +602,9 @@ static const asn1Object_t pubkeyObjects[] = { #define PUB_KEY_EXPONENT 2 /** - * Load a public key from an ASN1 encoded blob + * Load a public key from an ASN.1 encoded blob */ -static gmp_rsa_public_key_t *load(chunk_t blob) +static gmp_rsa_public_key_t *load_asn1_der(chunk_t blob) { asn1_parser_t *parser; chunk_t object; @@ -507,7 +640,7 @@ static gmp_rsa_public_key_t *load(chunk_t blob) return NULL; } - this->k = (mpz_sizeinbase(this->n, 2) + 7) / 8; + this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; if (!gmp_rsa_public_key_build_id(this->n, this->e, &this->keyid, &this->keyid_info)) @@ -518,6 +651,133 @@ static gmp_rsa_public_key_t *load(chunk_t blob) return &this->public; } +/** + * Load a public key from an OpenPGP blob + */ +static gmp_rsa_public_key_t* load_pgp(chunk_t blob) +{ + int objectID; + chunk_t packet = blob; + private_gmp_rsa_public_key_t *this = gmp_rsa_public_key_create_empty(); + + mpz_init(this->n); + mpz_init(this->e); + + for (objectID = PUB_KEY_MODULUS; objectID <= PUB_KEY_EXPONENT; objectID++) + { + chunk_t object; + + DBG2("L3 - %s:", pubkeyObjects[objectID].name); + object.len = pgp_length(&packet, 2); + + if (object.len == PGP_INVALID_LENGTH) + { + DBG1("OpenPGP length is invalid"); + goto end; + } + object.len = (object.len + 7) / BITS_PER_BYTE; + if (object.len > packet.len) + { + DBG1("OpenPGP field is too short"); + goto end; + } + object.ptr = packet.ptr; + packet.ptr += object.len; + packet.len -= object.len; + DBG4("%B", &object); + + switch (objectID) + { + case PUB_KEY_MODULUS: + mpz_import(this->n, object.len, 1, 1, 1, 0, object.ptr); + break; + case PUB_KEY_EXPONENT: + mpz_import(this->e, object.len, 1, 1, 1, 0, object.ptr); + break; + } + } + + this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; + free(blob.ptr); + + if (!gmp_rsa_public_key_build_id(this->n, this->e, + &this->keyid, &this->keyid_info)) + { + destroy(this); + return NULL; + } + return &this->public; + +end: + free(blob.ptr); + destroy(this); + return NULL; +} + +/** + * Load a public key from an RFC 3110 encoded blob + */ +static gmp_rsa_public_key_t *load_rfc_3110(chunk_t blob) +{ + chunk_t exponent, modulus; + u_char *pos = blob.ptr; + size_t len = blob.len; + private_gmp_rsa_public_key_t *this = gmp_rsa_public_key_create_empty(); + + mpz_init(this->n); + mpz_init(this->e); + + if (blob.len < 3) + { + DBG1("RFC 3110 public key blob too short for exponent length"); + goto end; + } + if (pos[0] != 0x00) + { + exponent = chunk_create(pos + 1, pos[0]); + pos++; + len--; + } + else + { + exponent = chunk_create(pos + 3, 256*pos[1] + pos[2]); + pos += 3; + len -= 3; + } + if (exponent.len > len) + { + DBG1("RFC 3110 public key blob too short for exponent"); + goto end; + } + pos += exponent.len; + len -= exponent.len; + + if (len == 0) + { + DBG1("RFC 3110 public key blob has zero length modulus"); + goto end; + } + modulus = chunk_create(pos, len); + + mpz_import(this->n, modulus.len, 1, 1, 1, 0, modulus.ptr); + mpz_import(this->e, exponent.len, 1, 1, 1, 0, exponent.ptr); + this->k = (mpz_sizeinbase(this->n, 2) + 7) / BITS_PER_BYTE; + free(blob.ptr); + + if (!gmp_rsa_public_key_build_id(this->n, this->e, + &this->keyid, &this->keyid_info)) + { + destroy(this); + return NULL; + } + return &this->public; + +end: + free(blob.ptr); + destroy(this); + return NULL; +} + typedef struct private_builder_t private_builder_t; /** * Builder implementation for key loading @@ -556,7 +816,23 @@ static void add(private_builder_t *this, builder_part_t part, ...) { va_start(args, part); chunk = va_arg(args, chunk_t); - this->key = load(chunk_clone(chunk)); + this->key = load_asn1_der(chunk_clone(chunk)); + va_end(args); + return; + } + case BUILD_BLOB_PGP: + { + va_start(args, part); + chunk = va_arg(args, chunk_t); + this->key = load_pgp(chunk_clone(chunk)); + va_end(args); + return; + } + case BUILD_BLOB_RFC_3110: + { + va_start(args, part); + chunk = va_arg(args, chunk_t); + this->key = load_rfc_3110(chunk_clone(chunk)); va_end(args); return; } |