diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2007-04-12 20:30:08 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2007-04-12 20:30:08 +0000 |
commit | b0d8ed94fe9e74afb49fdf5f11e4add29879c65c (patch) | |
tree | b20167235628771046e940a82a906a6d0991ee4a /src/pluto/pkcs1.c | |
parent | ea939d07c84d2a8e51215458063fc05e9c399290 (diff) | |
download | vyos-strongswan-b0d8ed94fe9e74afb49fdf5f11e4add29879c65c.tar.gz vyos-strongswan-b0d8ed94fe9e74afb49fdf5f11e4add29879c65c.zip |
[svn-upgrade] Integrating new upstream version, strongswan (4.1.1)
Diffstat (limited to 'src/pluto/pkcs1.c')
-rw-r--r-- | src/pluto/pkcs1.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/src/pluto/pkcs1.c b/src/pluto/pkcs1.c new file mode 100644 index 000000000..ade5fdd94 --- /dev/null +++ b/src/pluto/pkcs1.c @@ -0,0 +1,674 @@ +/* Support of PKCS#1 private key data structures + * Copyright (C) 2005 Jan Hutter, Martin Willi + * Copyright (C) 2002-2005 Andreas Steffen + * Hochschule fuer Technik Rapperswil, Switzerland + * + * 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. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * RCSID $Id: pkcs1.c,v 1.17 2006/01/04 21:00:43 as Exp $ + */ + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <freeswan.h> +#include <libsha2/sha2.h> + +#include "constants.h" +#include "defs.h" +#include "mp_defs.h" +#include "asn1.h" +#include "oid.h" +#include "log.h" +#include "pkcs1.h" +#include "md2.h" +#include "md5.h" +#include "sha1.h" +#include "rnd.h" + +const struct fld RSA_private_field[] = +{ + { "Modulus", offsetof(RSA_private_key_t, pub.n) }, + { "PublicExponent", offsetof(RSA_private_key_t, pub.e) }, + + { "PrivateExponent", offsetof(RSA_private_key_t, d) }, + { "Prime1", offsetof(RSA_private_key_t, p) }, + { "Prime2", offsetof(RSA_private_key_t, q) }, + { "Exponent1", offsetof(RSA_private_key_t, dP) }, + { "Exponent2", offsetof(RSA_private_key_t, dQ) }, + { "Coefficient", offsetof(RSA_private_key_t, qInv) }, +}; + +/* ASN.1 definition of a PKCS#1 RSA private key */ + +static const asn1Object_t privkeyObjects[] = { + { 0, "RSAPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */ + { 1, "version", ASN1_INTEGER, ASN1_BODY }, /* 1 */ + { 1, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 2 */ + { 1, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 3 */ + { 1, "privateExponent", ASN1_INTEGER, ASN1_BODY }, /* 4 */ + { 1, "prime1", ASN1_INTEGER, ASN1_BODY }, /* 5 */ + { 1, "prime2", ASN1_INTEGER, ASN1_BODY }, /* 6 */ + { 1, "exponent1", ASN1_INTEGER, ASN1_BODY }, /* 7 */ + { 1, "exponent2", ASN1_INTEGER, ASN1_BODY }, /* 8 */ + { 1, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 9 */ + { 1, "otherPrimeInfos", ASN1_SEQUENCE, ASN1_OPT | + ASN1_LOOP }, /* 10 */ + { 2, "otherPrimeInfo", ASN1_SEQUENCE, ASN1_NONE }, /* 11 */ + { 3, "prime", ASN1_INTEGER, ASN1_BODY }, /* 12 */ + { 3, "exponent", ASN1_INTEGER, ASN1_BODY }, /* 13 */ + { 3, "coefficient", ASN1_INTEGER, ASN1_BODY }, /* 14 */ + { 1, "end opt or loop", ASN1_EOC, ASN1_END } /* 15 */ +}; + +#define PKCS1_PRIV_KEY_VERSION 1 +#define PKCS1_PRIV_KEY_MODULUS 2 +#define PKCS1_PRIV_KEY_PUB_EXP 3 +#define PKCS1_PRIV_KEY_COEFF 9 +#define PKCS1_PRIV_KEY_ROOF 16 + + +/* + * forms the FreeS/WAN keyid from the public exponent e and modulus n + */ +void +form_keyid(chunk_t e, chunk_t n, char* keyid, unsigned *keysize) +{ + /* eliminate leading zero bytes in modulus from ASN.1 coding */ + while (n.len > 1 && *n.ptr == 0x00) + { + n.ptr++; n.len--; + } + + /* form the FreeS/WAN keyid */ + keyid[0] = '\0'; /* in case of splitkeytoid failure */ + splitkeytoid(e.ptr, e.len, n.ptr, n.len, keyid, KEYID_BUF); + + /* return the RSA modulus size in octets */ + *keysize = n.len; +} + +/* + * initialize an RSA_public_key_t object + */ +void +init_RSA_public_key(RSA_public_key_t *rsa, chunk_t e, chunk_t n) +{ + n_to_mpz(&rsa->e, e.ptr, e.len); + n_to_mpz(&rsa->n, n.ptr, n.len); + + form_keyid(e, n, rsa->keyid, &rsa->k); +} + +#ifdef DEBUG +static void +RSA_show_key_fields(RSA_private_key_t *k, int fieldcnt) +{ + const struct fld *p; + + DBG_log(" keyid: *%s", k->pub.keyid); + + for (p = RSA_private_field; p < &RSA_private_field[fieldcnt]; p++) + { + MP_INT *n = (MP_INT *) ((char *)k + p->offset); + size_t sz = mpz_sizeinbase(n, 16); + char buf[RSA_MAX_OCTETS * 2 + 2]; /* ought to be big enough */ + + passert(sz <= sizeof(buf)); + mpz_get_str(buf, 16, n); + + DBG_log(" %s: 0x%s", p->name, buf); + } +} + +/* debugging info that compromises security! */ +void +RSA_show_private_key(RSA_private_key_t *k) +{ + RSA_show_key_fields(k, elemsof(RSA_private_field)); +} + +void +RSA_show_public_key(RSA_public_key_t *k) +{ + /* Kludge: pretend that it is a private key, but only display the + * first two fields (which are the public key). + */ + passert(offsetof(RSA_private_key_t, pub) == 0); + RSA_show_key_fields((RSA_private_key_t *)k, 2); +} +#endif + +err_t +RSA_private_key_sanity(RSA_private_key_t *k) +{ + /* note that the *last* error found is reported */ + err_t ugh = NULL; + mpz_t t, u, q1; + +#ifdef DEBUG /* debugging info that compromises security */ + DBG(DBG_PRIVATE, RSA_show_private_key(k)); +#endif + + /* PKCS#1 1.5 section 6 requires modulus to have at least 12 octets. + * We actually require more (for security). + */ + if (k->pub.k < RSA_MIN_OCTETS) + return RSA_MIN_OCTETS_UGH; + + /* we picked a max modulus size to simplify buffer allocation */ + if (k->pub.k > RSA_MAX_OCTETS) + return RSA_MAX_OCTETS_UGH; + + mpz_init(t); + mpz_init(u); + mpz_init(q1); + + /* check that n == p * q */ + mpz_mul(u, &k->p, &k->q); + if (mpz_cmp(u, &k->pub.n) != 0) + ugh = "n != p * q"; + + /* check that e divides neither p-1 nor q-1 */ + mpz_sub_ui(t, &k->p, 1); + mpz_mod(t, t, &k->pub.e); + if (mpz_cmp_ui(t, 0) == 0) + ugh = "e divides p-1"; + + mpz_sub_ui(t, &k->q, 1); + mpz_mod(t, t, &k->pub.e); + if (mpz_cmp_ui(t, 0) == 0) + ugh = "e divides q-1"; + + /* check that d is e^-1 (mod lcm(p-1, q-1)) */ + /* see PKCS#1v2, aka RFC 2437, for the "lcm" */ + mpz_sub_ui(q1, &k->q, 1); + mpz_sub_ui(u, &k->p, 1); + mpz_gcd(t, u, q1); /* t := gcd(p-1, q-1) */ + mpz_mul(u, u, q1); /* u := (p-1) * (q-1) */ + mpz_divexact(u, u, t); /* u := lcm(p-1, q-1) */ + + mpz_mul(t, &k->d, &k->pub.e); + mpz_mod(t, t, u); + if (mpz_cmp_ui(t, 1) != 0) + ugh = "(d * e) mod (lcm(p-1, q-1)) != 1"; + + /* check that dP is d mod (p-1) */ + mpz_sub_ui(u, &k->p, 1); + mpz_mod(t, &k->d, u); + if (mpz_cmp(t, &k->dP) != 0) + ugh = "dP is not congruent to d mod (p-1)"; + + /* check that dQ is d mod (q-1) */ + mpz_sub_ui(u, &k->q, 1); + mpz_mod(t, &k->d, u); + if (mpz_cmp(t, &k->dQ) != 0) + ugh = "dQ is not congruent to d mod (q-1)"; + + /* check that qInv is (q^-1) mod p */ + mpz_mul(t, &k->qInv, &k->q); + mpz_mod(t, t, &k->p); + if (mpz_cmp_ui(t, 1) != 0) + ugh = "qInv is not conguent ot (q^-1) mod p"; + + mpz_clear(t); + mpz_clear(u); + mpz_clear(q1); + return ugh; +} + +/* + * Check the equality of two RSA public keys + */ +bool +same_RSA_public_key(const RSA_public_key_t *a, const RSA_public_key_t *b) +{ + return a == b + || (a->k == b->k && mpz_cmp(&a->n, &b->n) == 0 && mpz_cmp(&a->e, &b->e) == 0); +} + +/* + * Parses a PKCS#1 private key + */ +bool +pkcs1_parse_private_key(chunk_t blob, RSA_private_key_t *key) +{ + err_t ugh = NULL; + asn1_ctx_t ctx; + chunk_t object, modulus, exp; + u_int level; + int objectID = 0; + + asn1_init(&ctx, blob, 0, FALSE, DBG_PRIVATE); + + while (objectID < PKCS1_PRIV_KEY_ROOF) { + + if (!extract_object(privkeyObjects, &objectID, &object, &level, &ctx)) + return FALSE; + + if (objectID == PKCS1_PRIV_KEY_VERSION) + { + if (object.len > 0 && *object.ptr != 0) + { + plog(" wrong PKCS#1 private key version"); + return FALSE; + } + } + else if (objectID >= PKCS1_PRIV_KEY_MODULUS && + objectID <= PKCS1_PRIV_KEY_COEFF) + { + MP_INT *u = (MP_INT *) ((char *)key + + RSA_private_field[objectID - PKCS1_PRIV_KEY_MODULUS].offset); + + n_to_mpz(u, object.ptr, object.len); + + if (objectID == PKCS1_PRIV_KEY_MODULUS) + modulus = object; + else if (objectID == PKCS1_PRIV_KEY_PUB_EXP) + exp = object; + } + objectID++; + } + form_keyid(exp, modulus, key->pub.keyid, &key->pub.k); + ugh = RSA_private_key_sanity(key); + return (ugh == NULL); +} + +/* + * compute a digest over a binary blob + */ +bool +compute_digest(chunk_t tbs, int alg, chunk_t *digest) +{ + switch (alg) + { + case OID_MD2: + case OID_MD2_WITH_RSA: + { + MD2_CTX context; + + MD2Init(&context); + MD2Update(&context, tbs.ptr, tbs.len); + MD2Final(digest->ptr, &context); + digest->len = MD2_DIGEST_SIZE; + return TRUE; + } + case OID_MD5: + case OID_MD5_WITH_RSA: + { + MD5_CTX context; + + MD5Init(&context); + MD5Update(&context, tbs.ptr, tbs.len); + MD5Final(digest->ptr, &context); + digest->len = MD5_DIGEST_SIZE; + return TRUE; + } + case OID_SHA1: + case OID_SHA1_WITH_RSA: + case OID_SHA1_WITH_RSA_OIW: + { + SHA1_CTX context; + + SHA1Init(&context); + SHA1Update(&context, tbs.ptr, tbs.len); + SHA1Final(digest->ptr, &context); + digest->len = SHA1_DIGEST_SIZE; + return TRUE; + } + case OID_SHA256: + case OID_SHA256_WITH_RSA: + { + sha256_context context; + + sha256_init(&context); + sha256_write(&context, tbs.ptr, tbs.len); + sha256_final(&context); + memcpy(digest->ptr, context.sha_out, SHA2_256_DIGEST_SIZE); + digest->len = SHA2_256_DIGEST_SIZE; + return TRUE; + } + case OID_SHA384: + case OID_SHA384_WITH_RSA: + { + sha512_context context; + + sha384_init(&context); + sha512_write(&context, tbs.ptr, tbs.len); + sha512_final(&context); + memcpy(digest->ptr, context.sha_out, SHA2_384_DIGEST_SIZE); + digest->len = SHA2_384_DIGEST_SIZE; + return TRUE; + } + case OID_SHA512: + case OID_SHA512_WITH_RSA: + { + sha512_context context; + + sha512_init(&context); + sha512_write(&context, tbs.ptr, tbs.len); + sha512_final(&context); + memcpy(digest->ptr, context.sha_out, SHA2_512_DIGEST_SIZE); + digest->len = SHA2_512_DIGEST_SIZE; + return TRUE; + } + default: + digest->len = 0; + return FALSE; + } +} + +/* + * compute an RSA signature with PKCS#1 padding + */ +void +sign_hash(const RSA_private_key_t *k, const u_char *hash_val, size_t hash_len + , u_char *sig_val, size_t sig_len) +{ + chunk_t ch; + mpz_t t1, t2; + size_t padlen; + u_char *p = sig_val; + + DBG(DBG_CONTROL | DBG_CRYPT, + DBG_log("signing hash with RSA Key *%s", k->pub.keyid) + ) + /* PKCS#1 v1.5 8.1 encryption-block formatting */ + *p++ = 0x00; + *p++ = 0x01; /* BT (block type) 01 */ + padlen = sig_len - 3 - hash_len; + memset(p, 0xFF, padlen); + p += padlen; + *p++ = 0x00; + memcpy(p, hash_val, hash_len); + passert(p + hash_len - sig_val == (ptrdiff_t)sig_len); + + /* PKCS#1 v1.5 8.2 octet-string-to-integer conversion */ + n_to_mpz(t1, sig_val, sig_len); /* (could skip leading 0x00) */ + + /* PKCS#1 v1.5 8.3 RSA computation y = x^c mod n + * Better described in PKCS#1 v2.0 5.1 RSADP. + * There are two methods, depending on the form of the private key. + * We use the one based on the Chinese Remainder Theorem. + */ + mpz_init(t2); + + mpz_powm(t2, t1, &k->dP, &k->p); /* m1 = c^dP mod p */ + + mpz_powm(t1, t1, &k->dQ, &k->q); /* m2 = c^dQ mod Q */ + + mpz_sub(t2, t2, t1); /* h = qInv (m1 - m2) mod p */ + mpz_mod(t2, t2, &k->p); + mpz_mul(t2, t2, &k->qInv); + mpz_mod(t2, t2, &k->p); + + mpz_mul(t2, t2, &k->q); /* m = m2 + h q */ + mpz_add(t1, t1, t2); + + /* PKCS#1 v1.5 8.4 integer-to-octet-string conversion */ + ch = mpz_to_n(t1, sig_len); + memcpy(sig_val, ch.ptr, sig_len); + pfree(ch.ptr); + + mpz_clear(t1); + mpz_clear(t2); +} + +/* + * encrypt data with an RSA public key after padding + */ +chunk_t +RSA_encrypt(const RSA_public_key_t *key, chunk_t in) +{ + u_char padded[RSA_MAX_OCTETS]; + u_char *pos = padded; + int padding = key->k - in.len - 3; + int i; + + if (padding < 8 || key->k > RSA_MAX_OCTETS) + return empty_chunk; + + /* add padding according to PKCS#1 7.2.1 1.+2. */ + *pos++ = 0x00; + *pos++ = 0x02; + + /* pad with pseudo random bytes unequal to zero */ + get_rnd_bytes(pos, padding); + for (i = 0; i < padding; i++) + { + while (!*pos) + get_rnd_bytes(pos, 1); + pos++; + } + + /* append the padding terminator */ + *pos++ = 0x00; + + /* now add the data */ + memcpy(pos, in.ptr, in.len); + DBG(DBG_RAW, + DBG_dump_chunk("data for rsa encryption:\n", in); + DBG_dump("padded data for rsa encryption:\n", padded, key->k) + ) + + /* convert chunk to integer (PKCS#1 7.2.1 3.a) */ + { + chunk_t out; + mpz_t m, c; + + mpz_init(c); + n_to_mpz(m, padded, key->k); + + /* encrypt(PKCS#1 7.2.1 3.b) */ + mpz_powm(c, m, &key->e, &key->n); + + /* convert integer back to a chunk (PKCS#1 7.2.1 3.c) */ + out = mpz_to_n(c, key->k); + mpz_clear(c); + mpz_clear(m); + + DBG(DBG_RAW, + DBG_dump_chunk("rsa encrypted data:\n", out) + ) + return out; + } +} + +/* + * decrypt data with an RSA private key and remove padding + */ +bool +RSA_decrypt(const RSA_private_key_t *key, chunk_t in, chunk_t *out) +{ + chunk_t padded; + u_char *pos; + mpz_t t1, t2; + + n_to_mpz(t1, in.ptr,in.len); + + /* PKCS#1 v1.5 8.3 RSA computation y = x^c mod n + * Better described in PKCS#1 v2.0 5.1 RSADP. + * There are two methods, depending on the form of the private key. + * We use the one based on the Chinese Remainder Theorem. + */ + mpz_init(t2); + + mpz_powm(t2, t1, &key->dP, &key->p); /* m1 = c^dP mod p */ + mpz_powm(t1, t1, &key->dQ, &key->q); /* m2 = c^dQ mod Q */ + + mpz_sub(t2, t2, t1); /* h = qInv (m1 - m2) mod p */ + mpz_mod(t2, t2, &key->p); + mpz_mul(t2, t2, &key->qInv); + mpz_mod(t2, t2, &key->p); + + mpz_mul(t2, t2, &key->q); /* m = m2 + h q */ + mpz_add(t1, t1, t2); + + padded = mpz_to_n(t1, key->pub.k); + mpz_clear(t1); + mpz_clear(t2); + + DBG(DBG_PRIVATE, + DBG_dump_chunk("rsa decrypted data with padding:\n", padded) + ) + pos = padded.ptr; + + /* PKCS#1 v1.5 8.1 encryption-block formatting (EB = 00 || 02 || PS || 00 || D) */ + + /* check for hex pattern 00 02 in decrypted message */ + if ((*pos++ != 0x00) || (*(pos++) != 0x02)) + { + plog("incorrect padding - probably wrong RSA key"); + freeanychunk(padded); + return FALSE; + } + padded.len -= 2; + + /* the plaintext data starts after first 0x00 byte */ + while (padded.len-- > 0 && *pos++ != 0x00) + + if (padded.len == 0) + { + plog("no plaintext data"); + freeanychunk(padded); + return FALSE; + } + + clonetochunk(*out, pos, padded.len, "decrypted data"); + freeanychunk(padded); + return TRUE; +} + +/* + * build signatureValue + */ +chunk_t +pkcs1_build_signature(chunk_t tbs, int hash_alg, const RSA_private_key_t *key +, bool bit_string) +{ + + size_t siglen = key->pub.k; + + u_char digest_buf[MAX_DIGEST_LEN]; + chunk_t digest = { digest_buf, MAX_DIGEST_LEN }; + chunk_t digestInfo, alg_id, signatureValue; + u_char *pos; + + switch (hash_alg) + { + case OID_MD5: + case OID_MD5_WITH_RSA: + alg_id = ASN1_md5_id; + break; + case OID_SHA1: + case OID_SHA1_WITH_RSA: + alg_id = ASN1_sha1_id; + break; + default: + return empty_chunk; + } + compute_digest(tbs, hash_alg, &digest); + + /* according to PKCS#1 v2.1 digest must be packaged into + * an ASN.1 structure for encryption + */ + digestInfo = asn1_wrap(ASN1_SEQUENCE, "cm" + , alg_id + , asn1_simple_object(ASN1_OCTET_STRING, digest)); + + /* generate the RSA signature */ + if (bit_string) + { + pos = build_asn1_object(&signatureValue, ASN1_BIT_STRING, 1 + siglen); + *pos++ = 0x00; + } + else + { + pos = build_asn1_object(&signatureValue, ASN1_OCTET_STRING, siglen); + } + sign_hash(key, digestInfo.ptr, digestInfo.len, pos, siglen); + pfree(digestInfo.ptr); + + return signatureValue; +} + +/* + * build a DER-encoded PKCS#1 private key object + */ +chunk_t +pkcs1_build_private_key(const RSA_private_key_t *key) +{ + chunk_t pkcs1 = asn1_wrap(ASN1_SEQUENCE, "cmmmmmmmm" + , ASN1_INTEGER_0 + , asn1_integer_from_mpz(&key->pub.n) + , asn1_integer_from_mpz(&key->pub.e) + , asn1_integer_from_mpz(&key->d) + , asn1_integer_from_mpz(&key->p) + , asn1_integer_from_mpz(&key->q) + , asn1_integer_from_mpz(&key->dP) + , asn1_integer_from_mpz(&key->dQ) + , asn1_integer_from_mpz(&key->qInv)); + + DBG(DBG_PRIVATE, + DBG_dump_chunk("PKCS#1 encoded private key:", pkcs1) + ) + return pkcs1; +} + +/* + * build a DER-encoded PKCS#1 public key object + */ +chunk_t +pkcs1_build_public_key(const RSA_public_key_t *rsa) +{ + return asn1_wrap(ASN1_SEQUENCE, "mm" + , asn1_integer_from_mpz(&rsa->n) + , asn1_integer_from_mpz(&rsa->e)); +} + +/* + * build a DER-encoded publicKeyInfo object + */ +chunk_t +pkcs1_build_publicKeyInfo(const RSA_public_key_t *rsa) +{ + chunk_t publicKey; + chunk_t rawKey = pkcs1_build_public_key(rsa); + + u_char *pos = build_asn1_object(&publicKey, ASN1_BIT_STRING + , 1 + rawKey.len); + *pos++ = 0x00; + mv_chunk(&pos, rawKey); + + return asn1_wrap(ASN1_SEQUENCE, "cm" + , ASN1_rsaEncryption_id + , publicKey); +} +void +free_RSA_public_content(RSA_public_key_t *rsa) +{ + mpz_clear(&rsa->n); + mpz_clear(&rsa->e); +} + +void +free_RSA_private_content(RSA_private_key_t *rsak) +{ + free_RSA_public_content(&rsak->pub); + mpz_clear(&rsak->d); + mpz_clear(&rsak->p); + mpz_clear(&rsak->q); + mpz_clear(&rsak->dP); + mpz_clear(&rsak->dQ); + mpz_clear(&rsak->qInv); +} + |