aboutsummaryrefslogtreecommitdiff
path: root/rsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'rsa.c')
-rw-r--r--rsa.c249
1 files changed, 245 insertions, 4 deletions
diff --git a/rsa.c b/rsa.c
index 6c1e12e..ad5cb16 100644
--- a/rsa.c
+++ b/rsa.c
@@ -343,10 +343,10 @@ static hal_error_t find_prime(unsigned prime_length, fp_int *e, fp_int *result)
return HAL_OK;
}
-hal_error_t hal_rsa_gen(hal_rsa_key_t *key_,
- void *keybuf, const size_t keybuf_len,
- const unsigned key_length,
- const unsigned long public_exponent)
+hal_error_t hal_rsa_key_gen(hal_rsa_key_t *key_,
+ void *keybuf, const size_t keybuf_len,
+ const unsigned key_length,
+ const unsigned long public_exponent)
{
struct rsa_key *key = keybuf;
hal_error_t err = HAL_OK;
@@ -410,6 +410,247 @@ hal_error_t hal_rsa_gen(hal_rsa_key_t *key_,
}
/*
+ * Minimal ASN.1 encoding and decoding for private keys. This is NOT
+ * a general-purpose ASN.1 implementation, just enough to read and
+ * write PKCS #1.5 RSAPrivateKey syntax (RFC 2313 section 7.2).
+ *
+ * If at some later date we need a full ASN.1 implementation we'll add
+ * it as (a) separate library module(s), but for now the goal is just
+ * to let us serialize private keys for internal use and debugging.
+ */
+
+#define ASN1_INTEGER 0x02
+#define ASN1_SEQUENCE 0x30
+
+static size_t count_length(size_t length)
+{
+ size_t result = 1;
+
+ if (length >= 128)
+ for (; length > 0; length >>= 8)
+ result++;
+
+ return result;
+}
+
+static void encode_length(size_t length, size_t length_len, uint8_t *der)
+{
+ assert(der != NULL && length_len > 0 && length_len < 128);
+
+ if (length < 128) {
+ assert(length_len == 1);
+ *der = (uint8_t) length;
+ }
+
+ else {
+ *der = 0x80 | (uint8_t) --length_len;
+ while (length > 0 && length_len > 0) {
+ der[length_len--] = (uint8_t) (length & 0xFF);
+ length >>= 8;
+ }
+ assert(length == 0 && length_len == 0);
+ }
+}
+
+static hal_error_t decode_header(const uint8_t tag,
+ const uint8_t * const der, size_t der_max,
+ size_t *hlen, size_t *vlen)
+{
+ assert(der != NULL && hlen != NULL && vlen != NULL);
+
+ if (der_max < 2 || der[0] != tag)
+ return HAL_ERROR_ASN1_PARSE_FAILED;
+
+ if ((der[1] & 0x80) == 0) {
+ *hlen = 2;
+ *vlen = der[1];
+ }
+
+ else {
+ *hlen = 2 + (der[1] & 0x7F);
+ *vlen = 0;
+
+ if (*hlen > der_max)
+ return HAL_ERROR_ASN1_PARSE_FAILED;
+
+ for (size_t i = 2; i < *hlen; i++)
+ *vlen = (*vlen << 8) + der[i];
+ }
+
+ if (*hlen + *vlen > der_max)
+ return HAL_ERROR_ASN1_PARSE_FAILED;
+
+ return HAL_OK;
+}
+
+static hal_error_t encode_integer(fp_int *bn,
+ uint8_t *der, size_t *der_len, const size_t der_max)
+{
+ if (bn == NULL || der_len == NULL)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ /*
+ * Calculate length. Need to pad data with a leading zero if most
+ * significant bit is set, to avoid flipping ASN.1 sign bit. If
+ * caller didn't supply a buffer, just return the total length.
+ */
+
+ const int cmp = fp_cmp_d(bn, 0);
+
+ if (cmp != FP_EQ && cmp != FP_GT)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ const int leading_zero = (cmp == FP_EQ || (fp_count_bits(bn) & 7) == 0);
+ const size_t data_len = fp_unsigned_bin_size(bn) + leading_zero;
+ const size_t tag_len = 1;
+ const size_t length_len = count_length(data_len);
+ const size_t total_len = tag_len + length_len + data_len;
+
+ *der_len = total_len;
+
+ if (der == NULL)
+ return HAL_OK;
+
+ if (total_len > der_max)
+ return HAL_ERROR_RESULT_TOO_LONG;
+
+ /*
+ * Now encode.
+ */
+
+ *der++ = ASN1_INTEGER;
+ encode_length(data_len, length_len, der);
+ der += length_len;
+ if (leading_zero)
+ *der++ = 0x00;
+ fp_to_unsigned_bin(bn, der);
+
+ return HAL_OK;
+}
+
+static hal_error_t decode_integer(fp_int *bn,
+ const uint8_t * const der, size_t *der_len, const size_t der_max)
+{
+ if (bn == NULL || der == NULL)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ hal_error_t err;
+ size_t hlen, vlen;
+
+ if ((err = decode_header(ASN1_INTEGER, der, der_max, &hlen, &vlen)) != HAL_OK)
+ return err;
+
+ if (der_len != NULL)
+ *der_len = hlen + vlen;
+
+ if (vlen < 1)
+ return HAL_ERROR_ASN1_PARSE_FAILED;
+
+ fp_init(bn);
+ fp_read_unsigned_bin(bn, (uint8_t *) der + hlen, vlen);
+ return HAL_OK;
+}
+
+/*
+ * RSAPrivateKey fields in the required order.
+ */
+
+#define RSAPrivateKey_fields \
+ _(&version); \
+ _(&key->n); \
+ _(&key->e); \
+ _(&key->d); \
+ _(&key->p); \
+ _(&key->q); \
+ _(&key->dP); \
+ _(&key->dQ); \
+ _(&key->u);
+
+
+hal_error_t hal_rsa_key_to_der(hal_rsa_key_t key_,
+ uint8_t *der, size_t *der_len, const size_t der_max)
+{
+ struct rsa_key *key = key_.key;
+ hal_error_t err = HAL_OK;
+
+ if (key == NULL || der_len == NULL || key->type != HAL_RSA_PRIVATE)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ fp_int version;
+ fp_zero(&version);
+
+ /*
+ * Calculate length.
+ */
+
+ size_t data_len = 0;
+
+#define _(x) { size_t i; if ((err = encode_integer(x, NULL, &i, der_max - data_len)) != HAL_OK) return err; data_len += i; }
+ RSAPrivateKey_fields;
+#undef _
+
+ const size_t tag_len = 1;
+ const size_t length_len = count_length(data_len);
+ const size_t total_len = tag_len + length_len + data_len;
+
+ *der_len = total_len;
+
+ if (der == NULL)
+ return HAL_OK;
+
+ if (total_len > der_max)
+ return HAL_ERROR_RESULT_TOO_LONG;
+
+ /*
+ * Now encode.
+ */
+
+ *der++ = ASN1_SEQUENCE;
+ encode_length(data_len, length_len, der);
+ der += length_len;
+
+#define _(x) { size_t i; if ((err = encode_integer(x, der, &i, data_len)) != HAL_OK) return err; der += i; data_len -= i; }
+ RSAPrivateKey_fields;
+#undef _
+
+ return HAL_OK;
+}
+
+hal_error_t hal_rsa_key_from_der(hal_rsa_key_t *key_,
+ void *keybuf, const size_t keybuf_len,
+ const uint8_t *der, const size_t der_len)
+{
+ if (key_ == NULL || keybuf == NULL || keybuf_len < sizeof(struct rsa_key) || der == NULL)
+ return HAL_ERROR_BAD_ARGUMENTS;
+
+ memset(keybuf, 0, keybuf_len);
+
+ struct rsa_key *key = keybuf;
+
+ key->type = HAL_RSA_PRIVATE;
+
+ hal_error_t err = HAL_OK;
+ size_t hlen, vlen;
+
+ if ((err = decode_header(ASN1_SEQUENCE, der, der_len, &hlen, &vlen)) != HAL_OK)
+ return err;
+
+ der += hlen;
+
+ fp_int version;
+ fp_init(&version);
+
+#define _(x) { size_t i; if ((err = decode_integer(x, der, &i, vlen)) != HAL_OK) return err; der += i; vlen -= i; }
+ RSAPrivateKey_fields;
+#undef _
+
+ if (fp_cmp_d(&version, 0) != FP_EQ)
+ return HAL_ERROR_ASN1_PARSE_FAILED;
+
+ return HAL_OK;
+}
+
+/*
* Local variables:
* indent-tabs-mode: nil
* End: