summaryrefslogtreecommitdiff
path: root/src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c')
-rw-r--r--src/libstrongswan/plugins/gmp/gmp_rsa_public_key.c350
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;
}