2006-04-28 07:14:48 +00:00
|
|
|
/* Support of PKCS#1 private key data structures
|
|
|
|
* Copyright (C) 2005 Jan Hutter, Martin Willi
|
2009-05-22 15:25:32 +00:00
|
|
|
* Copyright (C) 2002-2009 Andreas Steffen
|
|
|
|
*
|
|
|
|
* HSR Hochschule fuer Technik Rapperswil, Switzerland
|
2006-04-28 07:14:48 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <freeswan.h>
|
|
|
|
|
2009-04-30 18:31:48 +00:00
|
|
|
#include <library.h>
|
2009-04-20 20:53:38 +00:00
|
|
|
#include <asn1/asn1.h>
|
|
|
|
#include <asn1/asn1_parser.h>
|
|
|
|
#include <asn1/oid.h>
|
2009-04-30 18:31:48 +00:00
|
|
|
#include <crypto/rngs/rng.h>
|
2009-05-05 12:28:02 +00:00
|
|
|
#include <crypto/hashers/hasher.h>
|
2009-04-20 20:53:38 +00:00
|
|
|
|
2006-04-28 07:14:48 +00:00
|
|
|
#include "constants.h"
|
|
|
|
#include "defs.h"
|
|
|
|
#include "mp_defs.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "pkcs1.h"
|
|
|
|
|
|
|
|
const struct fld RSA_private_field[] =
|
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
{ "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) },
|
2006-04-28 07:14:48 +00:00
|
|
|
};
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* ASN.1 definition of a PKCS#1 RSA private key
|
|
|
|
*/
|
2006-04-28 07:14:48 +00:00
|
|
|
static const asn1Object_t privkeyObjects[] = {
|
2009-04-20 20:53:38 +00:00
|
|
|
{ 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 */
|
|
|
|
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
|
2006-04-28 07:14:48 +00:00
|
|
|
};
|
2009-04-20 20:53:38 +00:00
|
|
|
#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
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Forms the FreeS/WAN keyid from the public exponent e and modulus n
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
void form_keyid(chunk_t e, chunk_t n, char* keyid, unsigned *keysize)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
/* 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;
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Initialize an RSA_public_key_t object
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
void init_RSA_public_key(RSA_public_key_t *rsa, chunk_t e, chunk_t n)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
n_to_mpz(&rsa->e, e.ptr, e.len);
|
|
|
|
n_to_mpz(&rsa->n, n.ptr, n.len);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
form_keyid(e, n, rsa->keyid, &rsa->k);
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2009-04-20 20:53:38 +00:00
|
|
|
static void RSA_show_key_fields(RSA_private_key_t *k, int fieldcnt)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
const struct fld *p;
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
DBG_log(" keyid: *%s", k->pub.keyid);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
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 */
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
passert(sz <= sizeof(buf));
|
|
|
|
mpz_get_str(buf, 16, n);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
DBG_log(" %s: 0x%s", p->name, buf);
|
|
|
|
}
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* debugging info that compromises security!
|
|
|
|
*/
|
|
|
|
void RSA_show_private_key(RSA_private_key_t *k)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
RSA_show_key_fields(k, countof(RSA_private_field));
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
void RSA_show_public_key(RSA_public_key_t *k)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
/* 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);
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
err_t RSA_private_key_sanity(RSA_private_key_t *k)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
/* note that the *last* error found is reported */
|
|
|
|
err_t ugh = NULL;
|
|
|
|
mpz_t t, u, q1;
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
#ifdef DEBUG /* debugging info that compromises security */
|
|
|
|
DBG(DBG_PRIVATE, RSA_show_private_key(k));
|
2006-04-28 07:14:48 +00:00
|
|
|
#endif
|
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* 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;
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
2006-04-28 07:14:48 +00:00
|
|
|
* Check the equality of two RSA public keys
|
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
bool same_RSA_public_key(const RSA_public_key_t *a, const RSA_public_key_t *b)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
return a == b
|
|
|
|
|| (a->k == b->k && mpz_cmp(&a->n, &b->n) == 0 && mpz_cmp(&a->e, &b->e) == 0);
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
2006-04-28 07:14:48 +00:00
|
|
|
* Parses a PKCS#1 private key
|
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
bool pkcs1_parse_private_key(chunk_t blob, RSA_private_key_t *key)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-20 20:53:38 +00:00
|
|
|
asn1_parser_t *parser;
|
2009-04-19 19:16:09 +00:00
|
|
|
chunk_t object, modulus, exp;
|
2009-04-20 20:53:38 +00:00
|
|
|
int objectID;
|
|
|
|
bool success = FALSE;
|
2009-04-19 19:16:09 +00:00
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
parser = asn1_parser_create(privkeyObjects, blob);
|
|
|
|
parser->set_flags(parser, FALSE, TRUE);
|
|
|
|
|
|
|
|
while (parser->iterate(parser, &objectID, &object))
|
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
if (objectID == PKCS1_PRIV_KEY_VERSION)
|
|
|
|
{
|
|
|
|
if (object.len > 0 && *object.ptr != 0)
|
|
|
|
{
|
|
|
|
plog(" wrong PKCS#1 private key version");
|
2009-04-20 20:53:38 +00:00
|
|
|
goto end;
|
2009-04-19 19:16:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
2009-04-20 20:53:38 +00:00
|
|
|
success = parser->success(parser);
|
|
|
|
|
|
|
|
end:
|
|
|
|
parser->destroy(parser);
|
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
err_t ugh;
|
|
|
|
|
|
|
|
form_keyid(exp, modulus, key->pub.keyid, &key->pub.k);
|
|
|
|
ugh = RSA_private_key_sanity(key);
|
|
|
|
success = (ugh == NULL);
|
|
|
|
}
|
|
|
|
return success;
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Compute a digest over a binary blob
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-05-05 12:28:02 +00:00
|
|
|
bool compute_digest(chunk_t tbs, int oid, chunk_t *digest)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-05-05 12:28:02 +00:00
|
|
|
hasher_t *hasher;
|
|
|
|
hash_algorithm_t alg = hasher_algorithm_from_oid(oid);
|
2009-04-19 19:16:09 +00:00
|
|
|
|
2009-05-05 12:28:02 +00:00
|
|
|
if (alg == HASH_UNKNOWN)
|
|
|
|
{
|
|
|
|
digest->len = 0;
|
|
|
|
return FALSE;
|
|
|
|
}
|
2009-04-19 19:16:09 +00:00
|
|
|
|
2009-05-05 12:28:02 +00:00
|
|
|
hasher = lib->crypto->create_hasher(lib->crypto, alg);
|
|
|
|
if (hasher == NULL)
|
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
digest->len = 0;
|
|
|
|
return FALSE;
|
2007-02-21 13:08:45 +00:00
|
|
|
}
|
2009-05-05 12:28:02 +00:00
|
|
|
digest->len = hasher->get_hash_size(hasher);
|
|
|
|
hasher->get_hash(hasher, tbs, digest->ptr);
|
|
|
|
hasher->destroy(hasher);
|
|
|
|
return TRUE;
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Compute an RSA signature with PKCS#1 padding
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
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)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
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);
|
|
|
|
free(ch.ptr);
|
|
|
|
|
|
|
|
mpz_clear(t1);
|
|
|
|
mpz_clear(t2);
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Encrypt data with an RSA public key after padding
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
chunk_t RSA_encrypt(const RSA_public_key_t *key, chunk_t in)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
u_char padded[RSA_MAX_OCTETS];
|
|
|
|
u_char *pos = padded;
|
|
|
|
int padding = key->k - in.len - 3;
|
|
|
|
int i;
|
2009-04-30 18:31:48 +00:00
|
|
|
rng_t *rng;
|
2009-04-19 19:16:09 +00:00
|
|
|
|
|
|
|
if (padding < 8 || key->k > RSA_MAX_OCTETS)
|
|
|
|
return chunk_empty;
|
|
|
|
|
|
|
|
/* add padding according to PKCS#1 7.2.1 1.+2. */
|
|
|
|
*pos++ = 0x00;
|
|
|
|
*pos++ = 0x02;
|
|
|
|
|
|
|
|
/* pad with pseudo random bytes unequal to zero */
|
2009-04-30 18:31:48 +00:00
|
|
|
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
2009-04-19 19:16:09 +00:00
|
|
|
for (i = 0; i < padding; i++)
|
2008-01-27 20:17:15 +00:00
|
|
|
{
|
2009-04-30 18:31:48 +00:00
|
|
|
rng->get_bytes(rng, padding, pos);
|
2009-04-19 19:16:09 +00:00
|
|
|
while (!*pos)
|
|
|
|
{
|
2009-04-30 18:31:48 +00:00
|
|
|
rng->get_bytes(rng, 1, pos);
|
2009-04-19 19:16:09 +00:00
|
|
|
}
|
|
|
|
pos++;
|
2008-01-27 20:17:15 +00:00
|
|
|
}
|
2009-04-30 18:31:48 +00:00
|
|
|
rng->destroy(rng);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* append the padding terminator */
|
|
|
|
*pos++ = 0x00;
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* 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)
|
|
|
|
)
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* convert chunk to integer (PKCS#1 7.2.1 3.a) */
|
|
|
|
{
|
|
|
|
chunk_t out;
|
|
|
|
mpz_t m, c;
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
mpz_init(c);
|
|
|
|
n_to_mpz(m, padded, key->k);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* encrypt(PKCS#1 7.2.1 3.b) */
|
|
|
|
mpz_powm(c, m, &key->e, &key->n);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* 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);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
DBG(DBG_RAW,
|
|
|
|
DBG_dump_chunk("rsa encrypted data:\n", out)
|
|
|
|
)
|
|
|
|
return out;
|
|
|
|
}
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Decrypt data with an RSA private key and remove padding
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
bool RSA_decrypt(const RSA_private_key_t *key, chunk_t in, chunk_t *out)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
chunk_t padded, plaintext;
|
|
|
|
u_char *pos;
|
|
|
|
mpz_t t1, t2;
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
n_to_mpz(t1, in.ptr,in.len);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* 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);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
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 */
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
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);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
mpz_mul(t2, t2, &key->q); /* m = m2 + h q */
|
|
|
|
mpz_add(t1, t1, t2);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
padded = mpz_to_n(t1, key->pub.k);
|
|
|
|
mpz_clear(t1);
|
|
|
|
mpz_clear(t2);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
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");
|
|
|
|
chunk_clear(&padded);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
padded.len -= 2;
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
/* the plaintext data starts after first 0x00 byte */
|
|
|
|
while (padded.len-- > 0 && *pos++ != 0x00)
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
if (padded.len == 0)
|
|
|
|
{
|
|
|
|
plog("no plaintext data");
|
|
|
|
free(padded.ptr);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
plaintext = chunk_create(pos, padded.len);
|
|
|
|
*out = chunk_clone(plaintext);
|
2009-04-17 16:11:33 +00:00
|
|
|
chunk_clear(&padded);
|
2009-04-19 19:16:09 +00:00
|
|
|
return TRUE;
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Build signatureValue
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
chunk_t pkcs1_build_signature(chunk_t tbs, int hash_alg,
|
|
|
|
const RSA_private_key_t *key, bool bit_string)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
size_t siglen = key->pub.k;
|
|
|
|
|
|
|
|
u_char digest_buf[MAX_DIGEST_LEN];
|
|
|
|
chunk_t digest = { digest_buf, MAX_DIGEST_LEN };
|
2009-04-21 15:36:58 +00:00
|
|
|
chunk_t digestInfo, signatureValue;
|
2009-04-19 19:16:09 +00:00
|
|
|
u_char *pos;
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
if (!compute_digest(tbs, hash_alg, &digest))
|
2009-04-19 19:16:09 +00:00
|
|
|
{
|
|
|
|
return chunk_empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* according to PKCS#1 v2.1 digest must be packaged into
|
|
|
|
* an ASN.1 structure for encryption
|
|
|
|
*/
|
|
|
|
digestInfo = asn1_wrap(ASN1_SEQUENCE, "cm"
|
2009-04-20 20:53:38 +00:00
|
|
|
, asn1_algorithmIdentifier(hash_alg)
|
2009-04-19 19:16:09 +00:00
|
|
|
, asn1_simple_object(ASN1_OCTET_STRING, digest));
|
|
|
|
|
|
|
|
/* generate the RSA signature */
|
|
|
|
if (bit_string)
|
|
|
|
{
|
2009-04-20 20:53:38 +00:00
|
|
|
pos = asn1_build_object(&signatureValue, ASN1_BIT_STRING, 1 + siglen);
|
2009-04-19 19:16:09 +00:00
|
|
|
*pos++ = 0x00;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-04-20 20:53:38 +00:00
|
|
|
pos = asn1_build_object(&signatureValue, ASN1_OCTET_STRING, siglen);
|
2009-04-19 19:16:09 +00:00
|
|
|
}
|
|
|
|
sign_hash(key, digestInfo.ptr, digestInfo.len, pos, siglen);
|
|
|
|
free(digestInfo.ptr);
|
|
|
|
|
|
|
|
return signatureValue;
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Build a DER-encoded PKCS#1 private key object
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
chunk_t pkcs1_build_private_key(const RSA_private_key_t *key)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
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;
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Build a DER-encoded PKCS#1 public key object
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
chunk_t pkcs1_build_public_key(const RSA_public_key_t *rsa)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
return asn1_wrap(ASN1_SEQUENCE, "mm"
|
|
|
|
, asn1_integer_from_mpz(&rsa->n)
|
|
|
|
, asn1_integer_from_mpz(&rsa->e));
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
/**
|
|
|
|
* Build a DER-encoded publicKeyInfo object
|
2006-04-28 07:14:48 +00:00
|
|
|
*/
|
2009-04-20 20:53:38 +00:00
|
|
|
chunk_t pkcs1_build_publicKeyInfo(const RSA_public_key_t *rsa)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
chunk_t publicKey;
|
|
|
|
chunk_t rawKey = pkcs1_build_public_key(rsa);
|
2009-04-20 20:53:38 +00:00
|
|
|
u_char *pos;
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
pos = asn1_build_object(&publicKey, ASN1_BIT_STRING, 1 + rawKey.len);
|
2009-04-19 19:16:09 +00:00
|
|
|
*pos++ = 0x00;
|
|
|
|
mv_chunk(&pos, rawKey);
|
2006-04-28 07:14:48 +00:00
|
|
|
|
2009-04-19 19:16:09 +00:00
|
|
|
return asn1_wrap(ASN1_SEQUENCE, "cm"
|
2009-04-20 20:53:38 +00:00
|
|
|
, asn1_algorithmIdentifier(OID_RSA_ENCRYPTION)
|
2009-04-19 19:16:09 +00:00
|
|
|
, publicKey);
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
2009-04-20 20:53:38 +00:00
|
|
|
|
|
|
|
void free_RSA_public_content(RSA_public_key_t *rsa)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
mpz_clear(&rsa->n);
|
|
|
|
mpz_clear(&rsa->e);
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|
|
|
|
|
2009-04-20 20:53:38 +00:00
|
|
|
void free_RSA_private_content(RSA_private_key_t *rsak)
|
2006-04-28 07:14:48 +00:00
|
|
|
{
|
2009-04-19 19:16:09 +00:00
|
|
|
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);
|
2006-04-28 07:14:48 +00:00
|
|
|
}
|