summaryrefslogtreecommitdiffstats
path: root/src/crypto
diff options
context:
space:
mode:
authorSimon Rettberg2026-01-28 12:53:53 +0100
committerSimon Rettberg2026-01-28 12:53:53 +0100
commit8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch)
treea8b359e59196be5b2e3862bed189107f4bc9975f /src/crypto
parentMerge branch 'master' into openslx (diff)
parent[prefix] Make unlzma.S compatible with 386 class CPUs (diff)
downloadipxe-openslx.tar.gz
ipxe-openslx.tar.xz
ipxe-openslx.zip
Merge branch 'master' into openslxopenslx
Diffstat (limited to 'src/crypto')
-rw-r--r--src/crypto/aes.c1
-rw-r--r--src/crypto/asn1.c372
-rw-r--r--src/crypto/bigint.c839
-rw-r--r--src/crypto/cbc.c1
-rw-r--r--src/crypto/certstore.c74
-rw-r--r--src/crypto/chap.c1
-rw-r--r--src/crypto/cms.c1057
-rw-r--r--src/crypto/crc32.c1
-rw-r--r--src/crypto/crypto_null.c40
-rw-r--r--src/crypto/deflate.c123
-rw-r--r--src/crypto/des.c1
-rw-r--r--src/crypto/dhe.c4
-rw-r--r--src/crypto/drbg.c1
-rw-r--r--src/crypto/ecb.c1
-rw-r--r--src/crypto/ecdhe.c75
-rw-r--r--src/crypto/ecdsa.c944
-rw-r--r--src/crypto/entropy.c1
-rw-r--r--src/crypto/gcm.c1
-rw-r--r--src/crypto/hash_df.c1
-rw-r--r--src/crypto/hmac.c1
-rw-r--r--src/crypto/hmac_drbg.c1
-rw-r--r--src/crypto/md4.c1
-rw-r--r--src/crypto/md5.c1
-rw-r--r--src/crypto/mishmash/cmd_md4.c34
-rw-r--r--src/crypto/mishmash/cmd_sha224.c34
-rw-r--r--src/crypto/mishmash/cmd_sha256.c34
-rw-r--r--src/crypto/mishmash/cmd_sha384.c34
-rw-r--r--src/crypto/mishmash/cmd_sha512.c34
-rw-r--r--src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c1
-rw-r--r--src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c1
-rw-r--r--src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c1
-rw-r--r--src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c1
-rw-r--r--src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha1.c62
-rw-r--r--src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha256.c46
-rw-r--r--src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha384.c46
-rw-r--r--src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha256.c46
-rw-r--r--src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha384.c46
-rw-r--r--src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c1
-rw-r--r--src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c1
-rw-r--r--src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c1
-rw-r--r--src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c1
-rw-r--r--src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c1
-rw-r--r--src/crypto/mishmash/ecdsa_sha224.c52
-rw-r--r--src/crypto/mishmash/ecdsa_sha256.c52
-rw-r--r--src/crypto/mishmash/ecdsa_sha384.c52
-rw-r--r--src/crypto/mishmash/ecdsa_sha512.c52
-rw-r--r--src/crypto/mishmash/oid_aes_cbc.c61
-rw-r--r--src/crypto/mishmash/oid_aes_gcm.c61
-rw-r--r--src/crypto/mishmash/oid_md4.c1
-rw-r--r--src/crypto/mishmash/oid_md5.c1
-rw-r--r--src/crypto/mishmash/oid_p256.c48
-rw-r--r--src/crypto/mishmash/oid_p384.c48
-rw-r--r--src/crypto/mishmash/oid_rsa.c1
-rw-r--r--src/crypto/mishmash/oid_sha1.c1
-rw-r--r--src/crypto/mishmash/oid_sha224.c1
-rw-r--r--src/crypto/mishmash/oid_sha256.c1
-rw-r--r--src/crypto/mishmash/oid_sha384.c1
-rw-r--r--src/crypto/mishmash/oid_sha512.c1
-rw-r--r--src/crypto/mishmash/oid_sha512_224.c1
-rw-r--r--src/crypto/mishmash/oid_sha512_256.c1
-rw-r--r--src/crypto/mishmash/oid_x25519.c2
-rw-r--r--src/crypto/mishmash/rsa_aes_cbc_sha1.c1
-rw-r--r--src/crypto/mishmash/rsa_aes_cbc_sha256.c1
-rw-r--r--src/crypto/mishmash/rsa_aes_gcm_sha256.c1
-rw-r--r--src/crypto/mishmash/rsa_aes_gcm_sha384.c1
-rw-r--r--src/crypto/mishmash/rsa_md5.c1
-rw-r--r--src/crypto/mishmash/rsa_sha1.c1
-rw-r--r--src/crypto/mishmash/rsa_sha224.c1
-rw-r--r--src/crypto/mishmash/rsa_sha256.c1
-rw-r--r--src/crypto/mishmash/rsa_sha384.c1
-rw-r--r--src/crypto/mishmash/rsa_sha512.c1
-rw-r--r--src/crypto/ntlm.c1
-rw-r--r--src/crypto/ocsp.c38
-rw-r--r--src/crypto/p256.c75
-rw-r--r--src/crypto/p384.c86
-rw-r--r--src/crypto/privkey.c5
-rw-r--r--src/crypto/random_nz.c1
-rw-r--r--src/crypto/rbg.c44
-rw-r--r--src/crypto/rootcert.c14
-rw-r--r--src/crypto/rsa.c413
-rw-r--r--src/crypto/sha1.c1
-rw-r--r--src/crypto/sha224.c1
-rw-r--r--src/crypto/sha256.c1
-rw-r--r--src/crypto/sha384.c1
-rw-r--r--src/crypto/sha512.c1
-rw-r--r--src/crypto/sha512_224.c1
-rw-r--r--src/crypto/sha512_256.c1
-rw-r--r--src/crypto/weierstrass.c1027
-rw-r--r--src/crypto/x25519.c68
-rw-r--r--src/crypto/x509.c202
90 files changed, 5458 insertions, 938 deletions
diff --git a/src/crypto/aes.c b/src/crypto/aes.c
index 5200e7760..fe6ccb222 100644
--- a/src/crypto/aes.c
+++ b/src/crypto/aes.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/asn1.c b/src/crypto/asn1.c
index dc9d1c54d..98d5b638f 100644
--- a/src/crypto/asn1.c
+++ b/src/crypto/asn1.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stddef.h>
@@ -32,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <time.h>
#include <ipxe/tables.h>
#include <ipxe/image.h>
+#include <ipxe/crypto.h>
#include <ipxe/asn1.h>
/** @file
@@ -87,15 +89,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
* @v cursor ASN.1 object cursor
* @v type Expected type, or ASN1_ANY
- * @v extra Additional length not present within partial cursor
* @ret len Length of object body, or negative error
*
* The object cursor will be updated to point to the start of the
* object body (i.e. the first byte following the length byte(s)), and
* the length of the object body (i.e. the number of bytes until the
* following object tag, if any) is returned.
+ *
+ * If the expected type is not found, the object cursor will not be
+ * modified. If any other error occurs, the object cursor will be
+ * invalidated.
*/
-int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
+static int asn1_start ( struct asn1_cursor *cursor, unsigned int type ) {
unsigned int len_len;
unsigned int len;
@@ -103,6 +108,7 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
if ( cursor->len < 2 /* Tag byte and first length byte */ ) {
if ( cursor->len )
DBGC ( cursor, "ASN1 %p too short\n", cursor );
+ asn1_invalidate_cursor ( cursor );
return -EINVAL_ASN1_EMPTY;
}
@@ -127,6 +133,7 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
if ( cursor->len < len_len ) {
DBGC ( cursor, "ASN1 %p bad length field length %d (max "
"%zd)\n", cursor, len_len, cursor->len );
+ asn1_invalidate_cursor ( cursor );
return -EINVAL_ASN1_LEN_LEN;
}
@@ -137,9 +144,10 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
cursor->data++;
cursor->len--;
}
- if ( ( cursor->len + extra ) < len ) {
+ if ( cursor->len < len ) {
DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n",
- cursor, len, ( cursor->len + extra ) );
+ cursor, len, cursor->len );
+ asn1_invalidate_cursor ( cursor );
return -EINVAL_ASN1_LEN;
}
@@ -154,22 +162,26 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
* @ret rc Return status code
*
* The object cursor will be updated to point to the body of the
- * current ASN.1 object. If any error occurs, the object cursor will
- * be invalidated.
+ * current ASN.1 object.
+ *
+ * If any error occurs, the object cursor will be invalidated.
*/
int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) {
int len;
- len = asn1_start ( cursor, type, 0 );
+ /* Parse current object */
+ len = asn1_start ( cursor, type );
if ( len < 0 ) {
asn1_invalidate_cursor ( cursor );
return len;
}
- cursor->len = len;
+ /* Update cursor */
+ if ( ( ( size_t ) len ) <= cursor->len )
+ cursor->len = len;
+
DBGC ( cursor, "ASN1 %p entered object type %02x (len %x)\n",
cursor, type, len );
-
return 0;
}
@@ -181,26 +193,26 @@ int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) {
* @ret rc Return status code
*
* The object cursor will be updated to point to the next ASN.1
- * object. If any error occurs, the object cursor will not be
- * modified.
+ * object.
+ *
+ * If the expected type is not found, the object cursor will not be
+ * modified. If any other error occurs, the object cursor will be
+ * invalidated.
*/
int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) {
int len;
- len = asn1_start ( cursor, type, 0 );
+ /* Parse current object */
+ len = asn1_start ( cursor, type );
if ( len < 0 )
return len;
+ /* Update cursor */
cursor->data += len;
cursor->len -= len;
+
DBGC ( cursor, "ASN1 %p skipped object type %02x (len %x)\n",
cursor, type, len );
-
- if ( ! cursor->len ) {
- DBGC ( cursor, "ASN1 %p reached end of object\n", cursor );
- return -ENOENT;
- }
-
return 0;
}
@@ -212,8 +224,9 @@ int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) {
* @ret rc Return status code
*
* The object cursor will be updated to point to the next ASN.1
- * object. If any error occurs, the object cursor will be
- * invalidated.
+ * object.
+ *
+ * If any error occurs, the object cursor will be invalidated.
*/
int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ) {
int rc;
@@ -234,8 +247,9 @@ int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ) {
* @ret rc Return status code
*
* The object cursor will be shrunk to contain only the current ASN.1
- * object. If any error occurs, the object cursor will be
- * invalidated.
+ * object.
+ *
+ * If any error occurs, the object cursor will be invalidated.
*/
int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) {
struct asn1_cursor temp;
@@ -244,7 +258,7 @@ int asn1_shrink ( struct asn1_cursor *cursor, unsigned int type ) {
/* Find end of object */
memcpy ( &temp, cursor, sizeof ( temp ) );
- len = asn1_start ( &temp, type, 0 );
+ len = asn1_start ( &temp, type );
if ( len < 0 ) {
asn1_invalidate_cursor ( cursor );
return len;
@@ -288,6 +302,88 @@ int asn1_shrink_any ( struct asn1_cursor *cursor ) {
}
/**
+ * Enter ASN.1 bit string
+ *
+ * @v cursor ASN.1 cursor
+ * @v unused Unused bits to fill in (or NULL to require all used)
+ * @ret rc Return status code
+ */
+int asn1_enter_bits ( struct asn1_cursor *cursor, unsigned int *unused ) {
+ const struct {
+ uint8_t unused;
+ uint8_t data[0];
+ } __attribute__ (( packed )) *bit_string;
+ const uint8_t *last;
+ unsigned int unused_bits;
+ uint8_t unused_mask;
+ int rc;
+
+ /* Enter bit string */
+ if ( ( rc = asn1_enter ( cursor, ASN1_BIT_STRING ) ) != 0 )
+ return rc;
+
+ /* Check that bit string header exists */
+ if ( cursor->len < sizeof ( *bit_string ) ) {
+ DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor );
+ DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
+ asn1_invalidate_cursor ( cursor );
+ return -EINVAL_BIT_STRING;
+ }
+ bit_string = cursor->data;
+ cursor->data = &bit_string->data;
+ cursor->len -= offsetof ( typeof ( *bit_string ), data );
+ unused_bits = bit_string->unused;
+
+ /* Check validity of unused bits */
+ unused_mask = ( 0xff >> ( 8 - unused_bits ) );
+ last = ( cursor->data + cursor->len - 1 );
+ if ( ( unused_bits >= 8 ) ||
+ ( ( unused_bits > 0 ) && ( cursor->len == 0 ) ) ||
+ ( ( *last & unused_mask ) != 0 ) ) {
+ DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor );
+ DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
+ asn1_invalidate_cursor ( cursor );
+ return -EINVAL_BIT_STRING;
+ }
+
+ /* Record or check number of unused bits, as applicable */
+ if ( unused ) {
+ *unused = unused_bits;
+ } else if ( unused_bits ) {
+ DBGC ( cursor, "ASN1 %p invalid integral bit string:\n",
+ cursor );
+ DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
+ asn1_invalidate_cursor ( cursor );
+ return -EINVAL_BIT_STRING;
+ }
+
+ return 0;
+}
+
+/**
+ * Enter ASN.1 unsigned integer
+ *
+ * @v cursor ASN.1 object cursor
+ * @ret rc Return status code
+ */
+int asn1_enter_unsigned ( struct asn1_cursor *cursor ) {
+ int rc;
+
+ /* Enter integer */
+ if ( ( rc = asn1_enter ( cursor, ASN1_INTEGER ) ) != 0 )
+ return rc;
+
+ /* Skip initial positive sign byte if applicable */
+ if ( ( cursor->len > 1 ) &&
+ ( *( ( uint8_t * ) cursor->data ) == 0x00 ) ) {
+ cursor->data++;
+ cursor->len--;
+ }
+
+ return 0;
+}
+
+/**
* Parse value of ASN.1 boolean
*
* @v cursor ASN.1 object cursor
@@ -350,87 +446,6 @@ int asn1_integer ( const struct asn1_cursor *cursor, int *value ) {
}
/**
- * Parse ASN.1 bit string
- *
- * @v cursor ASN.1 cursor
- * @v bits Bit string to fill in
- * @ret rc Return status code
- */
-int asn1_bit_string ( const struct asn1_cursor *cursor,
- struct asn1_bit_string *bits ) {
- struct asn1_cursor contents;
- const struct {
- uint8_t unused;
- uint8_t data[0];
- } __attribute__ (( packed )) *bit_string;
- size_t len;
- unsigned int unused;
- uint8_t unused_mask;
- const uint8_t *last;
- int rc;
-
- /* Enter bit string */
- memcpy ( &contents, cursor, sizeof ( contents ) );
- if ( ( rc = asn1_enter ( &contents, ASN1_BIT_STRING ) ) != 0 ) {
- DBGC ( cursor, "ASN1 %p cannot locate bit string:\n", cursor );
- DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
- return rc;
- }
-
- /* Validity checks */
- if ( contents.len < sizeof ( *bit_string ) ) {
- DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor );
- DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
- return -EINVAL_BIT_STRING;
- }
- bit_string = contents.data;
- len = ( contents.len - offsetof ( typeof ( *bit_string ), data ) );
- unused = bit_string->unused;
- unused_mask = ( 0xff >> ( 8 - unused ) );
- last = ( bit_string->data + len - 1 );
- if ( ( unused >= 8 ) ||
- ( ( unused > 0 ) && ( len == 0 ) ) ||
- ( ( *last & unused_mask ) != 0 ) ) {
- DBGC ( cursor, "ASN1 %p invalid bit string:\n", cursor );
- DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
- return -EINVAL_BIT_STRING;
- }
-
- /* Populate bit string */
- bits->data = &bit_string->data;
- bits->len = len;
- bits->unused = unused;
-
- return 0;
-}
-
-/**
- * Parse ASN.1 bit string that must be an integral number of bytes
- *
- * @v cursor ASN.1 cursor
- * @v bits Bit string to fill in
- * @ret rc Return status code
- */
-int asn1_integral_bit_string ( const struct asn1_cursor *cursor,
- struct asn1_bit_string *bits ) {
- int rc;
-
- /* Parse bit string */
- if ( ( rc = asn1_bit_string ( cursor, bits ) ) != 0 )
- return rc;
-
- /* Check that there are no unused bits at end of string */
- if ( bits->unused ) {
- DBGC ( cursor, "ASN1 %p invalid integral bit string:\n",
- cursor );
- DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
- return -EINVAL_BIT_STRING;
- }
-
- return 0;
-}
-
-/**
* Compare two ASN.1 objects
*
* @v cursor1 ASN.1 object cursor
@@ -473,18 +488,26 @@ asn1_find_algorithm ( const struct asn1_cursor *cursor ) {
*
* @v cursor ASN.1 object cursor
* @ret algorithm Algorithm
+ * @ret params Algorithm parameters, or NULL
* @ret rc Return status code
*/
int asn1_algorithm ( const struct asn1_cursor *cursor,
- struct asn1_algorithm **algorithm ) {
+ struct asn1_algorithm **algorithm,
+ struct asn1_cursor *params ) {
struct asn1_cursor contents;
int rc;
- /* Enter signatureAlgorithm */
+ /* Enter algorithm */
memcpy ( &contents, cursor, sizeof ( contents ) );
asn1_enter ( &contents, ASN1_SEQUENCE );
- /* Enter algorithm */
+ /* Get raw parameters, if applicable */
+ if ( params ) {
+ memcpy ( params, &contents, sizeof ( *params ) );
+ asn1_skip_any ( params );
+ }
+
+ /* Enter algorithm identifier */
if ( ( rc = asn1_enter ( &contents, ASN1_OID ) ) != 0 ) {
DBGC ( cursor, "ASN1 %p cannot locate algorithm OID:\n",
cursor );
@@ -500,6 +523,14 @@ int asn1_algorithm ( const struct asn1_cursor *cursor,
return -ENOTSUP_ALGORITHM;
}
+ /* Parse parameters, if applicable */
+ if ( params && (*algorithm)->parse &&
+ ( ( rc = (*algorithm)->parse ( *algorithm, params ) ) != 0 ) ) {
+ DBGC ( cursor, "ASN1 %p cannot parse %s parameters: %s\n",
+ cursor, (*algorithm)->name, strerror ( rc ) );
+ return rc;
+ }
+
return 0;
}
@@ -515,7 +546,7 @@ int asn1_pubkey_algorithm ( const struct asn1_cursor *cursor,
int rc;
/* Parse algorithm */
- if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 )
+ if ( ( rc = asn1_algorithm ( cursor, algorithm, NULL ) ) != 0 )
return rc;
/* Check algorithm has a public key */
@@ -541,7 +572,7 @@ int asn1_digest_algorithm ( const struct asn1_cursor *cursor,
int rc;
/* Parse algorithm */
- if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 )
+ if ( ( rc = asn1_algorithm ( cursor, algorithm, NULL ) ) != 0 )
return rc;
/* Check algorithm has a digest */
@@ -556,6 +587,34 @@ int asn1_digest_algorithm ( const struct asn1_cursor *cursor,
}
/**
+ * Parse ASN.1 OID-identified cipher algorithm
+ *
+ * @v cursor ASN.1 object cursor
+ * @ret algorithm Algorithm
+ * @ret params Algorithm parameters, or NULL
+ * @ret rc Return status code
+ */
+int asn1_cipher_algorithm ( const struct asn1_cursor *cursor,
+ struct asn1_algorithm **algorithm,
+ struct asn1_cursor *params ) {
+ int rc;
+
+ /* Parse algorithm */
+ if ( ( rc = asn1_algorithm ( cursor, algorithm, params ) ) != 0 )
+ return rc;
+
+ /* Check algorithm has a cipher */
+ if ( ! (*algorithm)->cipher ) {
+ DBGC ( cursor, "ASN1 %p algorithm %s is not a cipher "
+ "algorithm:\n", cursor, (*algorithm)->name );
+ DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
+ return -ENOTTY_ALGORITHM;
+ }
+
+ return 0;
+}
+
+/**
* Parse ASN.1 OID-identified signature algorithm
*
* @v cursor ASN.1 object cursor
@@ -567,7 +626,7 @@ int asn1_signature_algorithm ( const struct asn1_cursor *cursor,
int rc;
/* Parse algorithm */
- if ( ( rc = asn1_algorithm ( cursor, algorithm ) ) != 0 )
+ if ( ( rc = asn1_algorithm ( cursor, algorithm, NULL ) ) != 0 )
return rc;
/* Check algorithm has a public key */
@@ -590,19 +649,67 @@ int asn1_signature_algorithm ( const struct asn1_cursor *cursor,
}
/**
+ * Parse ASN.1 OID-identified elliptic curve algorithm
+ *
+ * @v cursor ASN.1 object cursor
+ * @v wrapper Optional wrapper algorithm, or NULL
+ * @ret algorithm Algorithm
+ * @ret rc Return status code
+ */
+int asn1_curve_algorithm ( const struct asn1_cursor *cursor,
+ struct asn1_algorithm *wrapper,
+ struct asn1_algorithm **algorithm ) {
+ struct asn1_cursor curve;
+
+ /* Elliptic curves are identified as either:
+ *
+ * - a wrapper algorithm "id-ecPublicKey" with the actual
+ * curve specified in the algorithm parameters, or
+ *
+ * - a standalone object identifier for the curve
+ */
+ if ( ( wrapper == NULL ) ||
+ ( asn1_check_algorithm ( cursor, wrapper, &curve ) != 0 ) ) {
+ memcpy ( &curve, cursor, sizeof ( curve ) );
+ }
+
+ /* Identify curve */
+ asn1_enter ( &curve, ASN1_OID );
+ *algorithm = asn1_find_algorithm ( &curve );
+ if ( ! *algorithm ) {
+ DBGC ( cursor, "ASN1 %p unrecognised EC algorithm:\n",
+ cursor );
+ DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
+ return -ENOTSUP_ALGORITHM;
+ }
+
+ /* Check algorithm has an elliptic curve */
+ if ( ! (*algorithm)->curve ) {
+ DBGC ( cursor, "ASN1 %p algorithm %s is not an elliptic curve "
+ "algorithm:\n", cursor, (*algorithm)->name );
+ DBGC_HDA ( cursor, 0, cursor->data, cursor->len );
+ return -ENOTTY_ALGORITHM;
+ }
+
+ return 0;
+}
+
+/**
* Check ASN.1 OID-identified algorithm
*
* @v cursor ASN.1 object cursor
* @v expected Expected algorithm
+ * @ret params Algorithm parameters, or NULL
* @ret rc Return status code
*/
int asn1_check_algorithm ( const struct asn1_cursor *cursor,
- struct asn1_algorithm *expected ) {
+ struct asn1_algorithm *expected,
+ struct asn1_cursor *params ) {
struct asn1_algorithm *actual;
int rc;
/* Parse algorithm */
- if ( ( rc = asn1_algorithm ( cursor, &actual ) ) != 0 )
+ if ( ( rc = asn1_algorithm ( cursor, &actual, params ) ) != 0 )
return rc;
/* Check algorithm matches */
@@ -616,6 +723,47 @@ int asn1_check_algorithm ( const struct asn1_cursor *cursor,
}
/**
+ * Parse ASN.1 CBC cipher parameters
+ *
+ * @v algorithm Algorithm
+ * @v param Parameters to parse
+ * @ret rc Return status code
+ */
+int asn1_parse_cbc ( struct asn1_algorithm *algorithm,
+ struct asn1_cursor *params ) {
+ struct cipher_algorithm *cipher = algorithm->cipher;
+
+ /* Sanity check */
+ assert ( cipher != NULL );
+
+ /* Enter parameters */
+ asn1_enter ( params, ASN1_OCTET_STRING );
+
+ /* Check length */
+ if ( params->len != cipher->blocksize )
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * Parse ASN.1 GCM cipher parameters
+ *
+ * @v algorithm Algorithm
+ * @v param Parameters to parse
+ * @ret rc Return status code
+ */
+int asn1_parse_gcm ( struct asn1_algorithm *algorithm __unused,
+ struct asn1_cursor *params ) {
+
+ /* Enter parameters */
+ asn1_enter ( params, ASN1_SEQUENCE );
+
+ /* Enter nonce */
+ return asn1_enter ( params, ASN1_OCTET_STRING );
+}
+
+/**
* Parse ASN.1 GeneralizedTime
*
* @v cursor ASN.1 cursor
diff --git a/src/crypto/bigint.c b/src/crypto/bigint.c
index 656f979e5..5d2f7b560 100644
--- a/src/crypto/bigint.c
+++ b/src/crypto/bigint.c
@@ -22,11 +22,12 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
#include <assert.h>
-#include <ipxe/profile.h>
+#include <stdio.h>
#include <ipxe/bigint.h>
/** @file
@@ -34,21 +35,51 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Big integer support
*/
-/** Modular multiplication overall profiler */
-static struct profiler bigint_mod_multiply_profiler __profiler =
- { .name = "bigint_mod_multiply" };
+/** Minimum number of least significant bytes included in transcription */
+#define BIGINT_NTOA_LSB_MIN 16
-/** Modular multiplication multiply step profiler */
-static struct profiler bigint_mod_multiply_multiply_profiler __profiler =
- { .name = "bigint_mod_multiply.multiply" };
+/**
+ * Transcribe big integer (for debugging)
+ *
+ * @v value0 Element 0 of big integer to be transcribed
+ * @v size Number of elements
+ * @ret string Big integer in string form (may be abbreviated)
+ */
+const char * bigint_ntoa_raw ( const bigint_element_t *value0,
+ unsigned int size ) {
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *value = ( ( const void * ) value0 );
+ bigint_element_t element;
+ static char buf[256];
+ unsigned int count;
+ int threshold;
+ uint8_t byte;
+ char *tmp;
+ int i;
+
+ /* Calculate abbreviation threshold, if any */
+ count = ( size * sizeof ( element ) );
+ threshold = count;
+ if ( count >= ( ( sizeof ( buf ) - 1 /* NUL */ ) / 2 /* "xx" */ ) ) {
+ threshold -= ( ( sizeof ( buf ) - 3 /* "..." */
+ - ( BIGINT_NTOA_LSB_MIN * 2 /* "xx" */ )
+ - 1 /* NUL */ ) / 2 /* "xx" */ );
+ }
-/** Modular multiplication rescale step profiler */
-static struct profiler bigint_mod_multiply_rescale_profiler __profiler =
- { .name = "bigint_mod_multiply.rescale" };
+ /* Transcribe bytes, abbreviating with "..." if necessary */
+ for ( tmp = buf, i = ( count - 1 ) ; i >= 0 ; i-- ) {
+ element = value->element[ i / sizeof ( element ) ];
+ byte = ( element >> ( 8 * ( i % sizeof ( element ) ) ) );
+ tmp += sprintf ( tmp, "%02x", byte );
+ if ( i == threshold ) {
+ tmp += sprintf ( tmp, "..." );
+ i = BIGINT_NTOA_LSB_MIN;
+ }
+ }
+ assert ( tmp < ( buf + sizeof ( buf ) ) );
-/** Modular multiplication subtract step profiler */
-static struct profiler bigint_mod_multiply_subtract_profiler __profiler =
- { .name = "bigint_mod_multiply.subtract" };
+ return buf;
+}
/**
* Conditionally swap big integers (in constant time)
@@ -76,72 +107,635 @@ void bigint_swap_raw ( bigint_element_t *first0, bigint_element_t *second0,
}
/**
- * Perform modular multiplication of big integers
+ * Multiply big integers
*
* @v multiplicand0 Element 0 of big integer to be multiplied
+ * @v multiplicand_size Number of elements in multiplicand
* @v multiplier0 Element 0 of big integer to be multiplied
+ * @v multiplier_size Number of elements in multiplier
+ * @v result0 Element 0 of big integer to hold result
+ */
+void bigint_multiply_raw ( const bigint_element_t *multiplicand0,
+ unsigned int multiplicand_size,
+ const bigint_element_t *multiplier0,
+ unsigned int multiplier_size,
+ bigint_element_t *result0 ) {
+ unsigned int result_size = ( multiplicand_size + multiplier_size );
+ const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
+ *multiplicand = ( ( const void * ) multiplicand0 );
+ const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
+ *multiplier = ( ( const void * ) multiplier0 );
+ bigint_t ( result_size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+ bigint_element_t multiplicand_element;
+ const bigint_element_t *multiplier_element;
+ bigint_element_t *result_element;
+ bigint_element_t carry_element;
+ unsigned int i;
+ unsigned int j;
+
+ /* Zero required portion of result
+ *
+ * All elements beyond the length of the multiplier will be
+ * written before they are read, and so do not need to be
+ * zeroed in advance.
+ */
+ memset ( result, 0, sizeof ( *multiplier ) );
+
+ /* Multiply integers one element at a time, adding the low
+ * half of the double-element product directly into the
+ * result, and maintaining a running single-element carry.
+ *
+ * The running carry can never overflow beyond a single
+ * element. At each step, the calculation we perform is:
+ *
+ * carry:result[i+j] := ( ( multiplicand[i] * multiplier[j] )
+ * + result[i+j] + carry )
+ *
+ * The maximum value (for n-bit elements) is therefore:
+ *
+ * (2^n - 1)*(2^n - 1) + (2^n - 1) + (2^n - 1) = 2^(2n) - 1
+ *
+ * This is precisely the maximum value for a 2n-bit integer,
+ * and so the carry out remains within the range of an n-bit
+ * integer, i.e. a single element.
+ */
+ for ( i = 0 ; i < multiplicand_size ; i++ ) {
+ multiplicand_element = multiplicand->element[i];
+ multiplier_element = &multiplier->element[0];
+ result_element = &result->element[i];
+ carry_element = 0;
+ for ( j = 0 ; j < multiplier_size ; j++ ) {
+ bigint_multiply_one ( multiplicand_element,
+ *(multiplier_element++),
+ result_element++,
+ &carry_element );
+ }
+ *result_element = carry_element;
+ }
+}
+
+/**
+ * Reduce big integer R^2 modulo N
+ *
* @v modulus0 Element 0 of big integer modulus
* @v result0 Element 0 of big integer to hold result
- * @v size Number of elements in base, modulus, and result
- * @v tmp Temporary working space
+ * @v size Number of elements in modulus and result
+ *
+ * Reduce the value R^2 modulo N, where R=2^n and n is the number of
+ * bits in the representation of the modulus N, including any leading
+ * zero bits.
*/
-void bigint_mod_multiply_raw ( const bigint_element_t *multiplicand0,
- const bigint_element_t *multiplier0,
- const bigint_element_t *modulus0,
- bigint_element_t *result0,
- unsigned int size, void *tmp ) {
- const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
- ( ( const void * ) multiplicand0 );
- const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
- ( ( const void * ) multiplier0 );
- const bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
- ( ( const void * ) modulus0 );
- bigint_t ( size ) __attribute__ (( may_alias )) *result =
- ( ( void * ) result0 );
- struct {
- bigint_t ( size * 2 ) result;
- bigint_t ( size * 2 ) modulus;
- } *temp = tmp;
- int rotation;
- int i;
+void bigint_reduce_raw ( const bigint_element_t *modulus0,
+ bigint_element_t *result0, unsigned int size ) {
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *modulus = ( ( const void * ) modulus0 );
+ bigint_t ( size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+ const unsigned int width = ( 8 * sizeof ( bigint_element_t ) );
+ unsigned int shift;
+ int max;
+ int sign;
+ int msb;
+ int carry;
+
+ /* We have the constants:
+ *
+ * N = modulus
+ *
+ * n = number of bits in the modulus (including any leading zeros)
+ *
+ * R = 2^n
+ *
+ * Let r be the extension of the n-bit result register by a
+ * separate two's complement sign bit, such that -R <= r < R,
+ * and define:
+ *
+ * x = r * 2^k
+ *
+ * as the value being reduced modulo N, where k is a
+ * non-negative integer bit shift.
+ *
+ * We want to reduce the initial value R^2=2^(2n), which we
+ * may trivially represent using r=1 and k=2n.
+ *
+ * We then iterate over decrementing k, maintaining the loop
+ * invariant:
+ *
+ * -N <= r < N
+ *
+ * On each iteration we must first double r, to compensate for
+ * having decremented k:
+ *
+ * k' = k - 1
+ *
+ * r' = 2r
+ *
+ * x = r * 2^k = 2r * 2^(k-1) = r' * 2^k'
+ *
+ * Note that doubling the n-bit result register will create a
+ * value of n+1 bits: this extra bit needs to be handled
+ * separately during the calculation.
+ *
+ * We then subtract N (if r is currently non-negative) or add
+ * N (if r is currently negative) to restore the loop
+ * invariant:
+ *
+ * 0 <= r < N => r" = 2r - N => -N <= r" < N
+ * -N <= r < 0 => r" = 2r + N => -N <= r" < N
+ *
+ * Note that since N may use all n bits, the most significant
+ * bit of the n-bit result register is not a valid two's
+ * complement sign bit for r: the extra sign bit therefore
+ * also needs to be handled separately.
+ *
+ * Once we reach k=0, we have x=r and therefore:
+ *
+ * -N <= x < N
+ *
+ * After this last loop iteration (with k=0), we may need to
+ * add a single multiple of N to ensure that x is positive,
+ * i.e. lies within the range 0 <= x < N.
+ *
+ * Since neither the modulus nor the value R^2 are secret, we
+ * may elide approximately half of the total number of
+ * iterations by constructing the initial representation of
+ * R^2 as r=2^m and k=2n-m (for some m such that 2^m < N).
+ */
+
+ /* Initialise x=R^2 */
+ memset ( result, 0, sizeof ( *result ) );
+ max = ( bigint_max_set_bit ( modulus ) - 2 );
+ if ( max < 0 ) {
+ /* Degenerate case of N=0 or N=1: return a zero result */
+ return;
+ }
+ bigint_set_bit ( result, max );
+ shift = ( ( 2 * size * width ) - max );
+ sign = 0;
+
+ /* Iterate as described above */
+ while ( shift-- ) {
+
+ /* Calculate 2r, storing extra bit separately */
+ msb = bigint_shl ( result );
+
+ /* Add or subtract N according to current sign */
+ if ( sign ) {
+ carry = bigint_add ( modulus, result );
+ } else {
+ carry = bigint_subtract ( modulus, result );
+ }
+
+ /* Calculate new sign of result
+ *
+ * We know the result lies in the range -N <= r < N
+ * and so the tuple (old sign, msb, carry) cannot ever
+ * take the values (0, 1, 0) or (1, 0, 0). We can
+ * therefore treat these as don't-care inputs, which
+ * allows us to simplify the boolean expression by
+ * ignoring the old sign completely.
+ */
+ assert ( ( sign == msb ) || carry );
+ sign = ( msb ^ carry );
+ }
- /* Start profiling */
- profile_start ( &bigint_mod_multiply_profiler );
+ /* Add N to make result positive if necessary */
+ if ( sign )
+ bigint_add ( modulus, result );
/* Sanity check */
- assert ( sizeof ( *temp ) == bigint_mod_multiply_tmp_len ( modulus ) );
-
- /* Perform multiplication */
- profile_start ( &bigint_mod_multiply_multiply_profiler );
- bigint_multiply ( multiplicand, multiplier, &temp->result );
- profile_stop ( &bigint_mod_multiply_multiply_profiler );
-
- /* Rescale modulus to match result */
- profile_start ( &bigint_mod_multiply_rescale_profiler );
- bigint_grow ( modulus, &temp->modulus );
- rotation = ( bigint_max_set_bit ( &temp->result ) -
- bigint_max_set_bit ( &temp->modulus ) );
- for ( i = 0 ; i < rotation ; i++ )
- bigint_rol ( &temp->modulus );
- profile_stop ( &bigint_mod_multiply_rescale_profiler );
-
- /* Subtract multiples of modulus */
- profile_start ( &bigint_mod_multiply_subtract_profiler );
- for ( i = 0 ; i <= rotation ; i++ ) {
- if ( bigint_is_geq ( &temp->result, &temp->modulus ) )
- bigint_subtract ( &temp->modulus, &temp->result );
- bigint_ror ( &temp->modulus );
+ assert ( ! bigint_is_geq ( result, modulus ) );
+}
+
+/**
+ * Compute inverse of odd big integer modulo any power of two
+ *
+ * @v invertend0 Element 0 of odd big integer to be inverted
+ * @v inverse0 Element 0 of big integer to hold result
+ * @v size Number of elements in invertend and result
+ */
+void bigint_mod_invert_raw ( const bigint_element_t *invertend0,
+ bigint_element_t *inverse0, unsigned int size ) {
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *invertend = ( ( const void * ) invertend0 );
+ bigint_t ( size ) __attribute__ (( may_alias ))
+ *inverse = ( ( void * ) inverse0 );
+ bigint_element_t accum;
+ bigint_element_t bit;
+ unsigned int i;
+
+ /* Sanity check */
+ assert ( bigint_bit_is_set ( invertend, 0 ) );
+
+ /* Initialise output */
+ memset ( inverse, 0xff, sizeof ( *inverse ) );
+
+ /* Compute inverse modulo 2^(width)
+ *
+ * This method is a lightly modified version of the pseudocode
+ * presented in "A New Algorithm for Inversion mod p^k (Koç,
+ * 2017)".
+ *
+ * Each inner loop iteration calculates one bit of the
+ * inverse. The residue value is the two's complement
+ * negation of the value "b" as used by Koç, to allow for
+ * division by two using a logical right shift (since we have
+ * no arithmetic right shift operation for big integers).
+ *
+ * The residue is stored in the as-yet uncalculated portion of
+ * the inverse. The size of the residue therefore decreases
+ * by one element for each outer loop iteration. Trivial
+ * inspection of the algorithm shows that any higher bits
+ * could not contribute to the eventual output value, and so
+ * we may safely reuse storage this way.
+ *
+ * Due to the suffix property of inverses mod 2^k, the result
+ * represents the least significant bits of the inverse modulo
+ * an arbitrarily large 2^k.
+ */
+ for ( i = size ; i > 0 ; i-- ) {
+ const bigint_t ( i ) __attribute__ (( may_alias ))
+ *addend = ( ( const void * ) invertend );
+ bigint_t ( i ) __attribute__ (( may_alias ))
+ *residue = ( ( void * ) inverse );
+
+ /* Calculate one element's worth of inverse bits */
+ for ( accum = 0, bit = 1 ; bit ; bit <<= 1 ) {
+ if ( bigint_bit_is_set ( residue, 0 ) ) {
+ accum |= bit;
+ bigint_add ( addend, residue );
+ }
+ bigint_shr ( residue );
+ }
+
+ /* Store in the element no longer required to hold residue */
+ inverse->element[ i - 1 ] = accum;
+ }
+
+ /* Correct order of inverse elements */
+ for ( i = 0 ; i < ( size / 2 ) ; i++ ) {
+ accum = inverse->element[i];
+ inverse->element[i] = inverse->element[ size - 1 - i ];
+ inverse->element[ size - 1 - i ] = accum;
+ }
+}
+
+/**
+ * Perform relaxed Montgomery reduction (REDC) of a big integer
+ *
+ * @v modulus0 Element 0 of big integer odd modulus
+ * @v value0 Element 0 of big integer to be reduced
+ * @v result0 Element 0 of big integer to hold result
+ * @v size Number of elements in modulus and result
+ * @ret carry Carry out
+ *
+ * The value to be reduced will be made divisible by the size of the
+ * modulus while retaining its residue class (i.e. multiples of the
+ * modulus will be added until the low half of the value is zero).
+ *
+ * The result may be expressed as
+ *
+ * tR = x + mN
+ *
+ * where x is the input value, N is the modulus, R=2^n (where n is the
+ * number of bits in the representation of the modulus, including any
+ * leading zero bits), and m is the number of multiples of the modulus
+ * added to make the result tR divisible by R.
+ *
+ * The maximum addend is mN <= (R-1)*N (and such an m can be proven to
+ * exist since N is limited to being odd and therefore coprime to R).
+ *
+ * Since the result of this addition is one bit larger than the input
+ * value, a carry out bit is also returned. The caller may be able to
+ * prove that the carry out is always zero, in which case it may be
+ * safely ignored.
+ *
+ * The upper half of the output value (i.e. t) will also be copied to
+ * the result pointer. It is permissible for the result pointer to
+ * overlap the lower half of the input value.
+ *
+ * External knowledge of constraints on the modulus and the input
+ * value may be used to prove constraints on the result. The
+ * constraint on the modulus may be generally expressed as
+ *
+ * R > kN
+ *
+ * for some positive integer k. The value k=1 is allowed, and simply
+ * expresses that the modulus fits within the number of bits in its
+ * own representation.
+ *
+ * For classic Montgomery reduction, we have k=1, i.e. R > N and a
+ * separate constraint that the input value is in the range x < RN.
+ * This gives the result constraint
+ *
+ * tR < RN + (R-1)N
+ * < 2RN - N
+ * < 2RN
+ * t < 2N
+ *
+ * A single subtraction of the modulus may therefore be required to
+ * bring it into the range t < N.
+ *
+ * When the input value is known to be a product of two integers A and
+ * B, with A < aN and B < bN, we get the result constraint
+ *
+ * tR < abN^2 + (R-1)N
+ * < (ab/k)RN + RN - N
+ * < (1 + ab/k)RN
+ * t < (1 + ab/k)N
+ *
+ * If we have k=a=b=1, i.e. R > N with A < N and B < N, then the
+ * result is in the range t < 2N and may require a single subtraction
+ * of the modulus to bring it into the range t < N so that it may be
+ * used as an input on a subsequent iteration.
+ *
+ * If we have k=4 and a=b=2, i.e. R > 4N with A < 2N and B < 2N, then
+ * the result is in the range t < 2N and may immediately be used as an
+ * input on a subsequent iteration, without requiring a subtraction.
+ *
+ * Larger values of k may be used to allow for larger values of a and
+ * b, which can be useful to elide intermediate reductions in a
+ * calculation chain that involves additions and subtractions between
+ * multiplications (as used in elliptic curve point addition, for
+ * example). As a general rule: each intermediate addition or
+ * subtraction will require k to be doubled.
+ *
+ * When the input value is known to be a single integer A, with A < aN
+ * (as used when converting out of Montgomery form), we get the result
+ * constraint
+ *
+ * tR < aN + (R-1)N
+ * < RN + (a-1)N
+ *
+ * If we have a=1, i.e. A < N, then the constraint becomes
+ *
+ * tR < RN
+ * t < N
+ *
+ * and so the result is immediately in the range t < N with no
+ * subtraction of the modulus required.
+ *
+ * For any larger value of a, the result value t=N becomes possible.
+ * Additional external knowledge may potentially be used to prove that
+ * t=N cannot occur. For example: if the caller is performing modular
+ * exponentiation with a prime modulus (or, more generally, a modulus
+ * that is coprime to the base), then there is no way for a non-zero
+ * base value to end up producing an exact multiple of the modulus.
+ * If t=N cannot be disproved, then conversion out of Montgomery form
+ * may require an additional subtraction of the modulus.
+ */
+int bigint_montgomery_relaxed_raw ( const bigint_element_t *modulus0,
+ bigint_element_t *value0,
+ bigint_element_t *result0,
+ unsigned int size ) {
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *modulus = ( ( const void * ) modulus0 );
+ union {
+ bigint_t ( size * 2 ) full;
+ struct {
+ bigint_t ( size ) low;
+ bigint_t ( size ) high;
+ } __attribute__ (( packed ));
+ } __attribute__ (( may_alias )) *value = ( ( void * ) value0 );
+ bigint_t ( size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+ static bigint_t ( 1 ) cached;
+ static bigint_t ( 1 ) negmodinv;
+ bigint_element_t multiple;
+ bigint_element_t carry;
+ unsigned int i;
+ unsigned int j;
+ int overflow;
+
+ /* Sanity checks */
+ assert ( bigint_bit_is_set ( modulus, 0 ) );
+
+ /* Calculate inverse (or use cached version) */
+ if ( cached.element[0] != modulus->element[0] ) {
+ bigint_mod_invert ( modulus, &negmodinv );
+ negmodinv.element[0] = -negmodinv.element[0];
+ cached.element[0] = modulus->element[0];
+ }
+
+ /* Perform multiprecision Montgomery reduction */
+ for ( i = 0 ; i < size ; i++ ) {
+
+ /* Determine scalar multiple for this round */
+ multiple = ( value->low.element[i] * negmodinv.element[0] );
+
+ /* Multiply value to make it divisible by 2^(width*i) */
+ carry = 0;
+ for ( j = 0 ; j < size ; j++ ) {
+ bigint_multiply_one ( multiple, modulus->element[j],
+ &value->full.element[ i + j ],
+ &carry );
+ }
+
+ /* Since value is now divisible by 2^(width*i), we
+ * know that the current low element must have been
+ * zeroed.
+ */
+ assert ( value->low.element[i] == 0 );
+
+ /* Store the multiplication carry out in the result,
+ * avoiding the need to immediately propagate the
+ * carry through the remaining elements.
+ */
+ result->element[i] = carry;
}
- profile_stop ( &bigint_mod_multiply_subtract_profiler );
- /* Resize result */
- bigint_shrink ( &temp->result, result );
+ /* Add the accumulated carries */
+ overflow = bigint_add ( result, &value->high );
+
+ /* Copy to result buffer */
+ bigint_copy ( &value->high, result );
+
+ return overflow;
+}
+
+/**
+ * Perform classic Montgomery reduction (REDC) of a big integer
+ *
+ * @v modulus0 Element 0 of big integer odd modulus
+ * @v value0 Element 0 of big integer to be reduced
+ * @v result0 Element 0 of big integer to hold result
+ * @v size Number of elements in modulus and result
+ */
+void bigint_montgomery_raw ( const bigint_element_t *modulus0,
+ bigint_element_t *value0,
+ bigint_element_t *result0,
+ unsigned int size ) {
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *modulus = ( ( const void * ) modulus0 );
+ union {
+ bigint_t ( size * 2 ) full;
+ struct {
+ bigint_t ( size ) low;
+ bigint_t ( size ) high;
+ } __attribute__ (( packed ));
+ } __attribute__ (( may_alias )) *value = ( ( void * ) value0 );
+ bigint_t ( size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+ int overflow;
+ int underflow;
/* Sanity check */
- assert ( bigint_is_geq ( modulus, result ) );
+ assert ( ! bigint_is_geq ( &value->high, modulus ) );
+
+ /* Perform relaxed Montgomery reduction */
+ overflow = bigint_montgomery_relaxed ( modulus, &value->full, result );
- /* Stop profiling */
- profile_stop ( &bigint_mod_multiply_profiler );
+ /* Conditionally subtract the modulus once */
+ underflow = bigint_subtract ( modulus, result );
+ bigint_swap ( result, &value->high, ( underflow & ~overflow ) );
+
+ /* Sanity check */
+ assert ( ! bigint_is_geq ( result, modulus ) );
+}
+
+/**
+ * Perform generalised exponentiation via a Montgomery ladder
+ *
+ * @v result0 Element 0 of result (initialised to identity element)
+ * @v multiple0 Element 0 of multiple (initialised to generator)
+ * @v size Number of elements in result and multiple
+ * @v exponent0 Element 0 of exponent
+ * @v exponent_size Number of elements in exponent
+ * @v op Montgomery ladder commutative operation
+ * @v ctx Operation context (if needed)
+ * @v tmp Temporary working space (if needed)
+ *
+ * The Montgomery ladder may be used to perform any operation that is
+ * isomorphic to exponentiation, i.e. to compute the result
+ *
+ * r = g^e = g * g * g * g * .... * g
+ *
+ * for an arbitrary commutative operation "*", generator "g" and
+ * exponent "e".
+ *
+ * The result "r" is computed in constant time (assuming that the
+ * underlying operation is constant time) in k steps, where k is the
+ * number of bits in the big integer representation of the exponent.
+ *
+ * The result "r" must be initialised to the operation's identity
+ * element, and the multiple must be initialised to the generator "g".
+ * On exit, the multiple will contain
+ *
+ * m = r * g = g^(e+1)
+ *
+ * Note that the terminology used here refers to exponentiation
+ * defined as repeated multiplication, but that the ladder may equally
+ * well be used to perform any isomorphic operation (such as
+ * multiplication defined as repeated addition).
+ */
+void bigint_ladder_raw ( bigint_element_t *result0,
+ bigint_element_t *multiple0, unsigned int size,
+ const bigint_element_t *exponent0,
+ unsigned int exponent_size, bigint_ladder_op_t *op,
+ const void *ctx, void *tmp ) {
+ bigint_t ( size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+ bigint_t ( size ) __attribute__ (( may_alias ))
+ *multiple = ( ( void * ) multiple0 );
+ const bigint_t ( exponent_size ) __attribute__ (( may_alias ))
+ *exponent = ( ( const void * ) exponent0 );
+ const unsigned int width = ( 8 * sizeof ( bigint_element_t ) );
+ unsigned int bit = ( exponent_size * width );
+ int toggle = 0;
+ int previous;
+
+ /* We have two physical storage locations: Rres (the "result"
+ * register) and Rmul (the "multiple" register).
+ *
+ * On entry we have:
+ *
+ * Rres = g^0 = 1 (the identity element)
+ * Rmul = g (the generator)
+ *
+ * For calculation purposes, define two virtual registers R[0]
+ * and R[1], mapped to the underlying physical storage
+ * locations via a boolean toggle state "t":
+ *
+ * R[t] -> Rres
+ * R[~t] -> Rmul
+ *
+ * Define the initial toggle state to be t=0, so that we have:
+ *
+ * R[0] = g^0 = 1 (the identity element)
+ * R[1] = g (the generator)
+ *
+ * The Montgomery ladder then iterates over each bit "b" of
+ * the exponent "e" (from MSB down to LSB), computing:
+ *
+ * R[1] := R[0] * R[1], R[0] := R[0] * R[0] (if b=0)
+ * R[0] := R[1] * R[0], R[1] := R[1] * R[1] (if b=1)
+ *
+ * i.e.
+ *
+ * R[~b] := R[b] * R[~b], R[b] := R[b] * R[b]
+ *
+ * To avoid data-dependent memory access patterns, we
+ * implement this as:
+ *
+ * Rmul := Rres * Rmul
+ * Rres := Rres * Rres
+ *
+ * and update the toggle state "t" (via constant-time
+ * conditional swaps) as needed to ensure that R[b] always
+ * maps to Rres and R[~b] always maps to Rmul.
+ */
+ while ( bit-- ) {
+
+ /* Update toggle state t := b
+ *
+ * If the new toggle state is not equal to the old
+ * toggle state, then we must swap R[0] and R[1] (in
+ * constant time).
+ */
+ previous = toggle;
+ toggle = bigint_bit_is_set ( exponent, bit );
+ bigint_swap ( result, multiple, ( toggle ^ previous ) );
+
+ /* Calculate Rmul = R[~b] := R[b] * R[~b] = Rres * Rmul */
+ op ( result->element, multiple->element, size, ctx, tmp );
+
+ /* Calculate Rres = R[b] := R[b] * R[b] = Rres * Rres */
+ op ( result->element, result->element, size, ctx, tmp );
+ }
+
+ /* Reset toggle state to zero */
+ bigint_swap ( result, multiple, toggle );
+}
+
+/**
+ * Perform modular multiplication as part of a Montgomery ladder
+ *
+ * @v operand Element 0 of first input operand (may overlap result)
+ * @v result Element 0 of second input operand and result
+ * @v size Number of elements in operands and result
+ * @v ctx Operation context (odd modulus, or NULL)
+ * @v tmp Temporary working space
+ */
+void bigint_mod_exp_ladder ( const bigint_element_t *multiplier0,
+ bigint_element_t *result0, unsigned int size,
+ const void *ctx, void *tmp ) {
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *multiplier = ( ( const void * ) multiplier0 );
+ bigint_t ( size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *modulus = ( ( const void * ) ctx );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias ))
+ *product = ( ( void * ) tmp );
+
+ /* Multiply and reduce */
+ bigint_multiply ( result, multiplier, product );
+ if ( modulus ) {
+ bigint_montgomery ( modulus, product, result );
+ } else {
+ bigint_shrink ( product, result );
+ }
}
/**
@@ -169,25 +763,108 @@ void bigint_mod_exp_raw ( const bigint_element_t *base0,
*exponent = ( ( const void * ) exponent0 );
bigint_t ( size ) __attribute__ (( may_alias )) *result =
( ( void * ) result0 );
- size_t mod_multiply_len = bigint_mod_multiply_tmp_len ( modulus );
+ const unsigned int width = ( 8 * sizeof ( bigint_element_t ) );
struct {
- bigint_t ( size ) base;
- bigint_t ( exponent_size ) exponent;
- uint8_t mod_multiply[mod_multiply_len];
+ struct {
+ bigint_t ( size ) modulus;
+ bigint_t ( size ) stash;
+ };
+ union {
+ bigint_t ( 2 * size ) full;
+ bigint_t ( size ) low;
+ } product;
} *temp = tmp;
- static const uint8_t start[1] = { 0x01 };
+ const uint8_t one[1] = { 1 };
+ bigint_element_t submask;
+ unsigned int subsize;
+ unsigned int scale;
+ unsigned int i;
- memcpy ( &temp->base, base, sizeof ( temp->base ) );
- memcpy ( &temp->exponent, exponent, sizeof ( temp->exponent ) );
- bigint_init ( result, start, sizeof ( start ) );
+ /* Sanity check */
+ assert ( sizeof ( *temp ) == bigint_mod_exp_tmp_len ( modulus ) );
- while ( ! bigint_is_zero ( &temp->exponent ) ) {
- if ( bigint_bit_is_set ( &temp->exponent, 0 ) ) {
- bigint_mod_multiply ( result, &temp->base, modulus,
- result, temp->mod_multiply );
- }
- bigint_ror ( &temp->exponent );
- bigint_mod_multiply ( &temp->base, &temp->base, modulus,
- &temp->base, temp->mod_multiply );
+ /* Handle degenerate case of zero modulus */
+ if ( ! bigint_max_set_bit ( modulus ) ) {
+ memset ( result, 0, sizeof ( *result ) );
+ return;
+ }
+
+ /* Factor modulus as (N * 2^scale) where N is odd */
+ bigint_copy ( modulus, &temp->modulus );
+ for ( scale = 0 ; ( ! bigint_bit_is_set ( &temp->modulus, 0 ) ) ;
+ scale++ ) {
+ bigint_shr ( &temp->modulus );
+ }
+ subsize = ( ( scale + width - 1 ) / width );
+ submask = ( ( 1UL << ( scale % width ) ) - 1 );
+ if ( ! submask )
+ submask = ~submask;
+
+ /* Calculate (R^2 mod N) */
+ bigint_reduce ( &temp->modulus, &temp->stash );
+
+ /* Initialise result = Montgomery(1, R^2 mod N) */
+ bigint_grow ( &temp->stash, &temp->product.full );
+ bigint_montgomery ( &temp->modulus, &temp->product.full, result );
+
+ /* Convert base into Montgomery form */
+ bigint_multiply ( base, &temp->stash, &temp->product.full );
+ bigint_montgomery ( &temp->modulus, &temp->product.full,
+ &temp->stash );
+
+ /* Calculate x1 = base^exponent modulo N */
+ bigint_ladder ( result, &temp->stash, exponent, bigint_mod_exp_ladder,
+ &temp->modulus, &temp->product );
+
+ /* Convert back out of Montgomery form */
+ bigint_grow ( result, &temp->product.full );
+ bigint_montgomery_relaxed ( &temp->modulus, &temp->product.full,
+ result );
+
+ /* Handle even moduli via Garner's algorithm */
+ if ( subsize ) {
+ const bigint_t ( subsize ) __attribute__ (( may_alias ))
+ *subbase = ( ( const void * ) base );
+ bigint_t ( subsize ) __attribute__ (( may_alias ))
+ *submodulus = ( ( void * ) &temp->modulus );
+ bigint_t ( subsize ) __attribute__ (( may_alias ))
+ *substash = ( ( void * ) &temp->stash );
+ bigint_t ( subsize ) __attribute__ (( may_alias ))
+ *subresult = ( ( void * ) result );
+ union {
+ bigint_t ( 2 * subsize ) full;
+ bigint_t ( subsize ) low;
+ } __attribute__ (( may_alias ))
+ *subproduct = ( ( void * ) &temp->product.full );
+
+ /* Calculate x2 = base^exponent modulo 2^k */
+ bigint_init ( substash, one, sizeof ( one ) );
+ bigint_copy ( subbase, submodulus );
+ bigint_ladder ( substash, submodulus, exponent,
+ bigint_mod_exp_ladder, NULL, subproduct );
+
+ /* Reconstruct N */
+ bigint_copy ( modulus, &temp->modulus );
+ for ( i = 0 ; i < scale ; i++ )
+ bigint_shr ( &temp->modulus );
+
+ /* Calculate N^-1 modulo 2^k */
+ bigint_mod_invert ( submodulus, &subproduct->low );
+ bigint_copy ( &subproduct->low, submodulus );
+
+ /* Calculate y = (x2 - x1) * N^-1 modulo 2^k */
+ bigint_subtract ( subresult, substash );
+ bigint_multiply ( substash, submodulus, &subproduct->full );
+ subproduct->low.element[ subsize - 1 ] &= submask;
+ bigint_grow ( &subproduct->low, &temp->stash );
+
+ /* Reconstruct N */
+ bigint_mod_invert ( submodulus, &subproduct->low );
+ bigint_copy ( &subproduct->low, submodulus );
+
+ /* Calculate x = x1 + N * y */
+ bigint_multiply ( &temp->modulus, &temp->stash,
+ &temp->product.full );
+ bigint_add ( &temp->product.low, result );
}
}
diff --git a/src/crypto/cbc.c b/src/crypto/cbc.c
index 0ba17ee48..ddba7abd9 100644
--- a/src/crypto/cbc.c
+++ b/src/crypto/cbc.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <assert.h>
diff --git a/src/crypto/certstore.c b/src/crypto/certstore.c
index 2676c7e1e..8472a2eed 100644
--- a/src/crypto/certstore.c
+++ b/src/crypto/certstore.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <stdlib.h>
@@ -44,7 +45,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#undef CERT
#define CERT( _index, _path ) \
extern char stored_cert_ ## _index ## _data[]; \
- extern char stored_cert_ ## _index ## _len[]; \
+ extern size_t ABS_SYMBOL ( stored_cert_ ## _index ## _len ); \
__asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t" \
"\nstored_cert_" #_index "_data:\n\t" \
".incbin \"" _path "\"\n\t" \
@@ -59,7 +60,7 @@ CERT_ALL
#undef CERT
#define CERT( _index, _path ) { \
.data = stored_cert_ ## _index ## _data, \
- .len = ( size_t ) stored_cert_ ## _index ## _len, \
+ .len = ABS_VALUE_INIT ( stored_cert_ ## _index ## _len ), \
},
static struct asn1_cursor certstore_raw[] = {
CERT_ALL
@@ -69,66 +70,28 @@ static struct asn1_cursor certstore_raw[] = {
static struct x509_certificate certstore_certs[ sizeof ( certstore_raw ) /
sizeof ( certstore_raw[0] ) ];
-/** Certificate store */
-struct x509_chain certstore = {
- .refcnt = REF_INIT ( ref_no_free ),
- .links = LIST_HEAD_INIT ( certstore.links ),
-};
-
/**
* Mark stored certificate as most recently used
*
+ * @v store Certificate store
* @v cert X.509 certificate
- * @ret cert X.509 certificate
*/
-static struct x509_certificate *
-certstore_found ( struct x509_certificate *cert ) {
+static void certstore_found ( struct x509_chain *store,
+ struct x509_certificate *cert ) {
/* Mark as most recently used */
list_del ( &cert->store.list );
- list_add ( &cert->store.list, &certstore.links );
- DBGC2 ( &certstore, "CERTSTORE found certificate %s\n",
+ list_add ( &cert->store.list, &store->links );
+ DBGC2 ( store, "CERTSTORE found certificate %s\n",
x509_name ( cert ) );
-
- return cert;
-}
-
-/**
- * Find certificate in store
- *
- * @v raw Raw certificate data
- * @ret cert X.509 certificate, or NULL if not found
- */
-struct x509_certificate * certstore_find ( struct asn1_cursor *raw ) {
- struct x509_certificate *cert;
-
- /* Search for certificate within store */
- list_for_each_entry ( cert, &certstore.links, store.list ) {
- if ( asn1_compare ( raw, &cert->raw ) == 0 )
- return certstore_found ( cert );
- }
- return NULL;
}
-/**
- * Find certificate in store corresponding to a private key
- *
- * @v key Private key
- * @ret cert X.509 certificate, or NULL if not found
- */
-struct x509_certificate * certstore_find_key ( struct private_key *key ) {
- struct x509_certificate *cert;
-
- /* Search for certificate within store */
- list_for_each_entry ( cert, &certstore.links, store.list ) {
- if ( pubkey_match ( cert->signature_algorithm->pubkey,
- key->builder.data, key->builder.len,
- cert->subject.public_key.raw.data,
- cert->subject.public_key.raw.len ) == 0 )
- return certstore_found ( cert );
- }
- return NULL;
-}
+/** Certificate store */
+struct x509_chain certstore = {
+ .refcnt = REF_INIT ( ref_no_free ),
+ .links = LIST_HEAD_INIT ( certstore.links ),
+ .found = certstore_found,
+};
/**
* Add certificate to store
@@ -219,7 +182,7 @@ static void certstore_init ( void ) {
/* Skip if certificate already present in store */
raw = &certstore_raw[i];
- if ( ( cert = certstore_find ( raw ) ) != NULL ) {
+ if ( ( cert = x509_find ( &certstore, raw ) ) != NULL ) {
DBGC ( &certstore, "CERTSTORE permanent certificate %d "
"is a duplicate of %s\n", i, x509_name ( cert ));
continue;
@@ -248,6 +211,7 @@ static void certstore_init ( void ) {
/** Certificate store initialisation function */
struct init_fn certstore_init_fn __init_fn ( INIT_LATE ) = {
+ .name = "certstore",
.initialise = certstore_init,
};
@@ -304,3 +268,9 @@ static int certstore_apply_settings ( void ) {
struct settings_applicator certstore_applicator __settings_applicator = {
.apply = certstore_apply_settings,
};
+
+/* Drag in objects via certificate store */
+REQUIRING_SYMBOL ( certstore );
+
+/* Drag in alternative certificate sources */
+REQUIRE_OBJECT ( config_certs );
diff --git a/src/crypto/chap.c b/src/crypto/chap.c
index c90c16def..008229133 100644
--- a/src/crypto/chap.c
+++ b/src/crypto/chap.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stddef.h>
#include <stdlib.h>
diff --git a/src/crypto/cms.c b/src/crypto/cms.c
index 9511cec8a..4c0f3f5a6 100644
--- a/src/crypto/cms.c
+++ b/src/crypto/cms.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -37,8 +38,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/asn1.h>
#include <ipxe/x509.h>
+#include <ipxe/image.h>
#include <ipxe/malloc.h>
-#include <ipxe/uaccess.h>
+#include <ipxe/privkey.h>
#include <ipxe/cms.h>
/* Disambiguate the various error causes */
@@ -58,60 +60,102 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
__einfo_error ( EINFO_EACCES_NO_SIGNATURES )
#define EINFO_EACCES_NO_SIGNATURES \
__einfo_uniqify ( EINFO_EACCES, 0x05, "No signatures present" )
-#define EINVAL_DIGEST \
- __einfo_error ( EINFO_EINVAL_DIGEST )
-#define EINFO_EINVAL_DIGEST \
- __einfo_uniqify ( EINFO_EINVAL, 0x01, "Not a digest algorithm" )
-#define EINVAL_PUBKEY \
- __einfo_error ( EINFO_EINVAL_PUBKEY )
-#define EINFO_EINVAL_PUBKEY \
- __einfo_uniqify ( EINFO_EINVAL, 0x02, "Not a public-key algorithm" )
-#define ENOTSUP_SIGNEDDATA \
- __einfo_error ( EINFO_ENOTSUP_SIGNEDDATA )
-#define EINFO_ENOTSUP_SIGNEDDATA \
- __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Not a digital signature" )
-
-/** "pkcs7-signedData" object identifier */
+#define EACCES_NO_RECIPIENTS \
+ __einfo_error ( EINFO_EACCES_NO_RECIPIENTS )
+#define EINFO_EACCES_NO_RECIPIENTS \
+ __einfo_uniqify ( EINFO_EACCES, 0x06, "No usable recipients" )
+#define EACCES_LEN \
+ __einfo_error ( EINFO_EACCES_LEN )
+#define EINFO_EACCES_LEN \
+ __einfo_uniqify ( EINFO_EACCES, 0x07, "Bad file length" )
+#define EACCES_PAD \
+ __einfo_error ( EINFO_EACCES_PAD )
+#define EINFO_EACCES_PAD \
+ __einfo_uniqify ( EINFO_EACCES, 0x08, "Bad block padding" )
+#define EACCES_MAC \
+ __einfo_error ( EINFO_EACCES_MAC )
+#define EINFO_EACCES_MAC \
+ __einfo_uniqify ( EINFO_EACCES, 0x09, "Invalid MAC" )
+#define ENOTSUP_TYPE \
+ __einfo_error ( EINFO_ENOTSUP_TYPE )
+#define EINFO_ENOTSUP_TYPE \
+ __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unrecognised message type" )
+
+static int cms_parse_signed ( struct cms_message *cms,
+ const struct asn1_cursor *raw );
+static int cms_parse_enveloped ( struct cms_message *cms,
+ const struct asn1_cursor *raw );
+
+/** "id-signedData" object identifier */
static uint8_t oid_signeddata[] = { ASN1_OID_SIGNEDDATA };
-/** "pkcs7-signedData" object identifier cursor */
-static struct asn1_cursor oid_signeddata_cursor =
- ASN1_CURSOR ( oid_signeddata );
+/** "id-envelopedData" object identifier */
+static uint8_t oid_envelopeddata[] = { ASN1_OID_ENVELOPEDDATA };
+
+/** "id-authEnvelopedData" object identifier */
+static uint8_t oid_authenvelopeddata[] = { ASN1_OID_AUTHENVELOPEDDATA };
+
+/** CMS message types */
+static struct cms_type cms_types[] = {
+ {
+ .name = "signed",
+ .oid = ASN1_CURSOR ( oid_signeddata ),
+ .parse = cms_parse_signed,
+ },
+ {
+ .name = "enveloped",
+ .oid = ASN1_CURSOR ( oid_envelopeddata ),
+ .parse = cms_parse_enveloped,
+ },
+ {
+ .name = "authEnveloped",
+ .oid = ASN1_CURSOR ( oid_authenvelopeddata ),
+ .parse = cms_parse_enveloped,
+ }
+};
/**
- * Parse CMS signature content type
+ * Parse CMS message content type
*
- * @v sig CMS signature
+ * @v cms CMS message
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse_content_type ( struct cms_signature *sig,
+static int cms_parse_content_type ( struct cms_message *cms,
const struct asn1_cursor *raw ) {
struct asn1_cursor cursor;
+ struct cms_type *type;
+ unsigned int i;
/* Enter contentType */
memcpy ( &cursor, raw, sizeof ( cursor ) );
asn1_enter ( &cursor, ASN1_OID );
- /* Check OID is pkcs7-signedData */
- if ( asn1_compare ( &cursor, &oid_signeddata_cursor ) != 0 ) {
- DBGC ( sig, "CMS %p does not contain signedData:\n", sig );
- DBGC_HDA ( sig, 0, raw->data, raw->len );
- return -ENOTSUP_SIGNEDDATA;
+ /* Check for a recognised OID */
+ for ( i = 0 ; i < ( sizeof ( cms_types ) /
+ sizeof ( cms_types[0] ) ) ; i++ ) {
+ type = &cms_types[i];
+ if ( asn1_compare ( &cursor, &type->oid ) == 0 ) {
+ cms->type = type;
+ DBGC ( cms, "CMS %p contains %sData\n",
+ cms, type->name );
+ return 0;
+ }
}
- DBGC ( sig, "CMS %p contains signedData\n", sig );
- return 0;
+ DBGC ( cms, "CMS %p is not a recognised message type:\n", cms );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
+ return -ENOTSUP_TYPE;
}
/**
- * Parse CMS signature certificate list
+ * Parse CMS message certificate list
*
- * @v sig CMS signature
+ * @v cms CMS message
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse_certificates ( struct cms_signature *sig,
+static int cms_parse_certificates ( struct cms_message *cms,
const struct asn1_cursor *raw ) {
struct asn1_cursor cursor;
struct x509_certificate *cert;
@@ -125,16 +169,16 @@ static int cms_parse_certificates ( struct cms_signature *sig,
while ( cursor.len ) {
/* Add certificate to chain */
- if ( ( rc = x509_append_raw ( sig->certificates, cursor.data,
+ if ( ( rc = x509_append_raw ( cms->certificates, cursor.data,
cursor.len ) ) != 0 ) {
- DBGC ( sig, "CMS %p could not append certificate: %s\n",
- sig, strerror ( rc) );
- DBGC_HDA ( sig, 0, cursor.data, cursor.len );
+ DBGC ( cms, "CMS %p could not append certificate: %s\n",
+ cms, strerror ( rc) );
+ DBGC_HDA ( cms, 0, cursor.data, cursor.len );
return rc;
}
- cert = x509_last ( sig->certificates );
- DBGC ( sig, "CMS %p found certificate %s\n",
- sig, x509_name ( cert ) );
+ cert = x509_last ( cms->certificates );
+ DBGC ( cms, "CMS %p found certificate %s\n",
+ cms, x509_name ( cert ) );
/* Move to next certificate */
asn1_skip_any ( &cursor );
@@ -144,44 +188,16 @@ static int cms_parse_certificates ( struct cms_signature *sig,
}
/**
- * Identify CMS signature certificate by issuer and serial number
- *
- * @v sig CMS signature
- * @v issuer Issuer
- * @v serial Serial number
- * @ret cert X.509 certificate, or NULL if not found
- */
-static struct x509_certificate *
-cms_find_issuer_serial ( struct cms_signature *sig,
- const struct asn1_cursor *issuer,
- const struct asn1_cursor *serial ) {
- struct x509_link *link;
- struct x509_certificate *cert;
-
- /* Scan through certificate list */
- list_for_each_entry ( link, &sig->certificates->links, list ) {
-
- /* Check issuer and serial number */
- cert = link->cert;
- if ( ( asn1_compare ( issuer, &cert->issuer.raw ) == 0 ) &&
- ( asn1_compare ( serial, &cert->serial.raw ) == 0 ) )
- return cert;
- }
-
- return NULL;
-}
-
-/**
- * Parse CMS signature signer identifier
+ * Parse CMS message participant identifier
*
- * @v sig CMS signature
- * @v info Signer information to fill in
+ * @v cms CMS message
+ * @v part Participant information to fill in
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse_signer_identifier ( struct cms_signature *sig,
- struct cms_signer_info *info,
- const struct asn1_cursor *raw ) {
+static int cms_parse_identifier ( struct cms_message *cms,
+ struct cms_participant *part,
+ const struct asn1_cursor *raw ) {
struct asn1_cursor cursor;
struct asn1_cursor serial;
struct asn1_cursor issuer;
@@ -195,46 +211,46 @@ static int cms_parse_signer_identifier ( struct cms_signature *sig,
/* Identify issuer */
memcpy ( &issuer, &cursor, sizeof ( issuer ) );
if ( ( rc = asn1_shrink ( &issuer, ASN1_SEQUENCE ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not locate issuer: %s\n",
- sig, info, strerror ( rc ) );
- DBGC_HDA ( sig, 0, raw->data, raw->len );
+ DBGC ( cms, "CMS %p/%p could not locate issuer: %s\n",
+ cms, part, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
return rc;
}
- DBGC ( sig, "CMS %p/%p issuer is:\n", sig, info );
- DBGC_HDA ( sig, 0, issuer.data, issuer.len );
+ DBGC ( cms, "CMS %p/%p issuer is:\n", cms, part );
+ DBGC_HDA ( cms, 0, issuer.data, issuer.len );
asn1_skip_any ( &cursor );
/* Identify serialNumber */
memcpy ( &serial, &cursor, sizeof ( serial ) );
if ( ( rc = asn1_shrink ( &serial, ASN1_INTEGER ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not locate serialNumber: %s\n",
- sig, info, strerror ( rc ) );
- DBGC_HDA ( sig, 0, raw->data, raw->len );
+ DBGC ( cms, "CMS %p/%p could not locate serialNumber: %s\n",
+ cms, part, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
return rc;
}
- DBGC ( sig, "CMS %p/%p serial number is:\n", sig, info );
- DBGC_HDA ( sig, 0, serial.data, serial.len );
+ DBGC ( cms, "CMS %p/%p serial number is:\n", cms, part );
+ DBGC_HDA ( cms, 0, serial.data, serial.len );
/* Identify certificate */
- cert = cms_find_issuer_serial ( sig, &issuer, &serial );
+ cert = x509_find_issuer_serial ( cms->certificates, &issuer, &serial );
if ( ! cert ) {
- DBGC ( sig, "CMS %p/%p could not identify signer's "
- "certificate\n", sig, info );
- return -ENOENT;
+ DBGC ( cms, "CMS %p/%p could not identify certificate\n",
+ cms, part );
+ return ( cms_is_signature ( cms ) ? -ENOENT : 0 );
}
/* Append certificate to chain */
- if ( ( rc = x509_append ( info->chain, cert ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not append certificate: %s\n",
- sig, info, strerror ( rc ) );
+ if ( ( rc = x509_append ( part->chain, cert ) ) != 0 ) {
+ DBGC ( cms, "CMS %p/%p could not append certificate: %s\n",
+ cms, part, strerror ( rc ) );
return rc;
}
/* Append remaining certificates to chain */
- if ( ( rc = x509_auto_append ( info->chain,
- sig->certificates ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not append certificates: %s\n",
- sig, info, strerror ( rc ) );
+ if ( ( rc = x509_auto_append ( part->chain,
+ cms->certificates ) ) != 0 ) {
+ DBGC ( cms, "CMS %p/%p could not append certificates: %s\n",
+ cms, part, strerror ( rc ) );
return rc;
}
@@ -242,173 +258,287 @@ static int cms_parse_signer_identifier ( struct cms_signature *sig,
}
/**
- * Parse CMS signature digest algorithm
+ * Parse CMS message digest algorithm
*
- * @v sig CMS signature
- * @v info Signer information to fill in
+ * @v cms CMS message
+ * @v part Participant information to fill in
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse_digest_algorithm ( struct cms_signature *sig,
- struct cms_signer_info *info,
+static int cms_parse_digest_algorithm ( struct cms_message *cms,
+ struct cms_participant *part,
const struct asn1_cursor *raw ) {
struct asn1_algorithm *algorithm;
int rc;
/* Identify algorithm */
if ( ( rc = asn1_digest_algorithm ( raw, &algorithm ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not identify digest algorithm: "
- "%s\n", sig, info, strerror ( rc ) );
- DBGC_HDA ( sig, 0, raw->data, raw->len );
+ DBGC ( cms, "CMS %p/%p could not identify digest algorithm: "
+ "%s\n", cms, part, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
return rc;
}
/* Record digest algorithm */
- info->digest = algorithm->digest;
- DBGC ( sig, "CMS %p/%p digest algorithm is %s\n",
- sig, info, algorithm->name );
+ part->digest = algorithm->digest;
+ DBGC ( cms, "CMS %p/%p digest algorithm is %s\n",
+ cms, part, algorithm->name );
return 0;
}
/**
- * Parse CMS signature algorithm
+ * Parse CMS message public-key algorithm
*
- * @v sig CMS signature
- * @v info Signer information to fill in
+ * @v cms CMS message
+ * @v part Participant information to fill in
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse_signature_algorithm ( struct cms_signature *sig,
- struct cms_signer_info *info,
- const struct asn1_cursor *raw ) {
+static int cms_parse_pubkey_algorithm ( struct cms_message *cms,
+ struct cms_participant *part,
+ const struct asn1_cursor *raw ) {
struct asn1_algorithm *algorithm;
int rc;
/* Identify algorithm */
if ( ( rc = asn1_pubkey_algorithm ( raw, &algorithm ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not identify public-key "
- "algorithm: %s\n", sig, info, strerror ( rc ) );
- DBGC_HDA ( sig, 0, raw->data, raw->len );
+ DBGC ( cms, "CMS %p/%p could not identify public-key "
+ "algorithm: %s\n", cms, part, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
return rc;
}
- /* Record signature algorithm */
- info->pubkey = algorithm->pubkey;
- DBGC ( sig, "CMS %p/%p public-key algorithm is %s\n",
- sig, info, algorithm->name );
+ /* Record public-key algorithm */
+ part->pubkey = algorithm->pubkey;
+ DBGC ( cms, "CMS %p/%p public-key algorithm is %s\n",
+ cms, part, algorithm->name );
return 0;
}
/**
- * Parse CMS signature value
+ * Parse CMS message cipher algorithm
*
- * @v sig CMS signature
- * @v info Signer information to fill in
+ * @v cms CMS message
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse_signature_value ( struct cms_signature *sig,
- struct cms_signer_info *info,
- const struct asn1_cursor *raw ) {
- struct asn1_cursor cursor;
+static int cms_parse_cipher_algorithm ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ struct asn1_algorithm *algorithm;
int rc;
- /* Enter signature */
- memcpy ( &cursor, raw, sizeof ( cursor ) );
- if ( ( rc = asn1_enter ( &cursor, ASN1_OCTET_STRING ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not locate signature:\n",
- sig, info );
- DBGC_HDA ( sig, 0, raw->data, raw->len );
+ /* Identify algorithm */
+ if ( ( rc = asn1_cipher_algorithm ( raw, &algorithm,
+ &cms->iv ) ) != 0 ) {
+ DBGC ( cms, "CMS %p could not identify cipher algorithm: %s\n",
+ cms, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
return rc;
}
- /* Record signature */
- info->signature_len = cursor.len;
- info->signature = malloc ( info->signature_len );
- if ( ! info->signature )
- return -ENOMEM;
- memcpy ( info->signature, cursor.data, info->signature_len );
- DBGC ( sig, "CMS %p/%p signature value is:\n", sig, info );
- DBGC_HDA ( sig, 0, info->signature, info->signature_len );
+ /* Record cipher */
+ cms->cipher = algorithm->cipher;
+ DBGC ( cms, "CMS %p cipher algorithm is %s\n", cms, algorithm->name );
+
+ return 0;
+}
+
+/**
+ * Parse CMS message signature or key value
+ *
+ * @v cms CMS message
+ * @v part Participant information to fill in
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_value ( struct cms_message *cms,
+ struct cms_participant *part,
+ const struct asn1_cursor *raw ) {
+ int rc;
+
+ /* Enter signature or encryptedKey */
+ memcpy ( &part->value, raw, sizeof ( part->value ) );
+ if ( ( rc = asn1_enter ( &part->value, ASN1_OCTET_STRING ) ) != 0 ) {
+ DBGC ( cms, "CMS %p/%p could not locate value:\n",
+ cms, part );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
+ return rc;
+ }
+ DBGC ( cms, "CMS %p/%p value is:\n", cms, part );
+ DBGC_HDA ( cms, 0, part->value.data, part->value.len );
return 0;
}
/**
- * Parse CMS signature signer information
+ * Parse CMS message participant information
*
- * @v sig CMS signature
- * @v info Signer information to fill in
+ * @v cms CMS message
+ * @v part Participant information to fill in
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse_signer_info ( struct cms_signature *sig,
- struct cms_signer_info *info,
+static int cms_parse_participant ( struct cms_message *cms,
+ struct cms_participant *part,
const struct asn1_cursor *raw ) {
struct asn1_cursor cursor;
int rc;
- /* Enter signerInfo */
+ /* Enter signerInfo or ktri */
memcpy ( &cursor, raw, sizeof ( cursor ) );
asn1_enter ( &cursor, ASN1_SEQUENCE );
/* Skip version */
asn1_skip ( &cursor, ASN1_INTEGER );
- /* Parse sid */
- if ( ( rc = cms_parse_signer_identifier ( sig, info, &cursor ) ) != 0 )
+ /* Parse sid or rid */
+ if ( ( rc = cms_parse_identifier ( cms, part, &cursor ) ) != 0 )
return rc;
asn1_skip_any ( &cursor );
- /* Parse digestAlgorithm */
- if ( ( rc = cms_parse_digest_algorithm ( sig, info, &cursor ) ) != 0 )
- return rc;
- asn1_skip_any ( &cursor );
+ /* Parse signature-only objects */
+ if ( cms_is_signature ( cms ) ) {
- /* Skip signedAttrs, if present */
- asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
+ /* Parse digestAlgorithm */
+ if ( ( rc = cms_parse_digest_algorithm ( cms, part,
+ &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+
+ /* Skip signedAttrs, if present */
+ asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
+ }
- /* Parse signatureAlgorithm */
- if ( ( rc = cms_parse_signature_algorithm ( sig, info, &cursor ) ) != 0)
+ /* Parse signatureAlgorithm or contentEncryptionAlgorithm */
+ if ( ( rc = cms_parse_pubkey_algorithm ( cms, part, &cursor ) ) != 0 )
return rc;
asn1_skip_any ( &cursor );
- /* Parse signature */
- if ( ( rc = cms_parse_signature_value ( sig, info, &cursor ) ) != 0 )
+ /* Parse signature or encryptedKey */
+ if ( ( rc = cms_parse_value ( cms, part, &cursor ) ) != 0 )
return rc;
return 0;
}
/**
- * Parse CMS signature from ASN.1 data
+ * Parse CMS message participants information
*
- * @v sig CMS signature
+ * @v cms CMS message
* @v raw ASN.1 cursor
* @ret rc Return status code
*/
-static int cms_parse ( struct cms_signature *sig,
- const struct asn1_cursor *raw ) {
+static int cms_parse_participants ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
struct asn1_cursor cursor;
- struct cms_signer_info *info;
+ struct cms_participant *part;
int rc;
- /* Enter contentInfo */
+ /* Enter signerInfos or recipientInfos */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
+ asn1_enter ( &cursor, ASN1_SET );
+
+ /* Add each signerInfo or recipientInfo. Errors are handled
+ * by ensuring that cms_put() will always be able to free any
+ * allocated memory.
+ */
+ while ( cursor.len ) {
+
+ /* Allocate participant information block */
+ part = zalloc ( sizeof ( *part ) );
+ if ( ! part )
+ return -ENOMEM;
+ list_add ( &part->list, &cms->participants );
+ part->digest = &digest_null;
+ part->pubkey = &pubkey_null;
+
+ /* Allocate certificate chain */
+ part->chain = x509_alloc_chain();
+ if ( ! part->chain )
+ return -ENOMEM;
+
+ /* Parse signerInfo or recipientInfo */
+ if ( ( rc = cms_parse_participant ( cms, part,
+ &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+ }
+
+ return 0;
+}
+
+/**
+ * Parse CMS message encrypted content information
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_encrypted ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Enter encryptedContentInfo */
memcpy ( &cursor, raw, sizeof ( cursor ) );
asn1_enter ( &cursor, ASN1_SEQUENCE );
- /* Parse contentType */
+ /* Skip contentType */
+ asn1_skip ( &cursor, ASN1_OID );
- if ( ( rc = cms_parse_content_type ( sig, &cursor ) ) != 0 )
+ /* Parse contentEncryptionAlgorithm */
+ if ( ( rc = cms_parse_cipher_algorithm ( cms, &cursor ) ) != 0 )
return rc;
- asn1_skip_any ( &cursor );
- /* Enter content */
- asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
+ return 0;
+}
+
+/**
+ * Parse CMS message MAC
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_mac ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ int rc;
+
+ /* Enter mac */
+ memcpy ( &cms->mac, raw, sizeof ( cms->mac ) );
+ if ( ( rc = asn1_enter ( &cms->mac, ASN1_OCTET_STRING ) ) != 0 ) {
+ DBGC ( cms, "CMS %p could not locate mac: %s\n",
+ cms, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, raw->data, raw->len );
+ return rc;
+ }
+ DBGC ( cms, "CMS %p mac is:\n", cms );
+ DBGC_HDA ( cms, 0, cms->mac.data, cms->mac.len );
+
+ return 0;
+}
+
+/**
+ * Parse CMS signed data
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_signed ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Allocate certificate list */
+ cms->certificates = x509_alloc_chain();
+ if ( ! cms->certificates )
+ return -ENOMEM;
/* Enter signedData */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
asn1_enter ( &cursor, ASN1_SEQUENCE );
/* Skip version */
@@ -421,108 +551,159 @@ static int cms_parse ( struct cms_signature *sig,
asn1_skip ( &cursor, ASN1_SEQUENCE );
/* Parse certificates */
- if ( ( rc = cms_parse_certificates ( sig, &cursor ) ) != 0 )
+ if ( ( rc = cms_parse_certificates ( cms, &cursor ) ) != 0 )
return rc;
asn1_skip_any ( &cursor );
/* Skip crls, if present */
asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 1 ) );
- /* Enter signerInfos */
- asn1_enter ( &cursor, ASN1_SET );
+ /* Parse signerInfos */
+ if ( ( rc = cms_parse_participants ( cms, &cursor ) ) != 0 )
+ return rc;
- /* Add each signerInfo. Errors are handled by ensuring that
- * cms_put() will always be able to free any allocated memory.
- */
- while ( cursor.len ) {
+ return 0;
+}
- /* Allocate signer information block */
- info = zalloc ( sizeof ( *info ) );
- if ( ! info )
- return -ENOMEM;
- list_add ( &info->list, &sig->info );
+/**
+ * Parse CMS enveloped data
+ *
+ * @v cms CMS message
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int cms_parse_enveloped ( struct cms_message *cms,
+ const struct asn1_cursor *raw ) {
+ struct asn1_cursor cursor;
+ int rc;
- /* Allocate certificate chain */
- info->chain = x509_alloc_chain();
- if ( ! info->chain )
- return -ENOMEM;
+ /* Enter envelopedData or authEnvelopedData */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
- /* Parse signerInfo */
- if ( ( rc = cms_parse_signer_info ( sig, info,
- &cursor ) ) != 0 )
- return rc;
- asn1_skip_any ( &cursor );
- }
+ /* Skip version */
+ asn1_skip ( &cursor, ASN1_INTEGER );
+
+ /* Skip originatorInfo, if present */
+ asn1_skip_if_exists ( &cursor, ASN1_IMPLICIT_TAG ( 0 ) );
+
+ /* Parse recipientInfos */
+ if ( ( rc = cms_parse_participants ( cms, &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+
+ /* Parse encryptedContentInfo or authEncryptedContentInfo */
+ if ( ( rc = cms_parse_encrypted ( cms, &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+ assert ( cms->cipher != NULL );
+
+ /* Skip unprotectedAttrs or authAttrs, if present */
+ asn1_skip_if_exists ( &cursor, ASN1_IMPLICIT_TAG ( 1 ) );
+
+ /* Parse mac, if present */
+ if ( ( cms->cipher->authsize != 0 ) &&
+ ( ( rc = cms_parse_mac ( cms, &cursor ) ) != 0 ) )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Parse CMS message from ASN.1 data
+ *
+ * @v cms CMS message
+ * @ret rc Return status code
+ */
+static int cms_parse ( struct cms_message *cms ) {
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Enter contentInfo */
+ memcpy ( &cursor, cms->raw, sizeof ( cursor ) );
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
+
+ /* Parse contentType */
+ if ( ( rc = cms_parse_content_type ( cms, &cursor ) ) != 0 )
+ return rc;
+ asn1_skip_any ( &cursor );
+
+ /* Enter content */
+ asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
+
+ /* Parse type-specific content */
+ if ( ( rc = cms->type->parse ( cms, &cursor ) ) != 0 )
+ return rc;
return 0;
}
/**
- * Free CMS signature
+ * Free CMS message
*
* @v refcnt Reference count
*/
static void cms_free ( struct refcnt *refcnt ) {
- struct cms_signature *sig =
- container_of ( refcnt, struct cms_signature, refcnt );
- struct cms_signer_info *info;
- struct cms_signer_info *tmp;
-
- list_for_each_entry_safe ( info, tmp, &sig->info, list ) {
- list_del ( &info->list );
- x509_chain_put ( info->chain );
- free ( info->signature );
- free ( info );
- }
- x509_chain_put ( sig->certificates );
- free ( sig );
+ struct cms_message *cms =
+ container_of ( refcnt, struct cms_message, refcnt );
+ struct cms_participant *part;
+ struct cms_participant *tmp;
+
+ list_for_each_entry_safe ( part, tmp, &cms->participants, list ) {
+ list_del ( &part->list );
+ x509_chain_put ( part->chain );
+ free ( part );
+ }
+ x509_chain_put ( cms->certificates );
+ free ( cms->raw );
+ free ( cms );
}
/**
- * Create CMS signature
+ * Create CMS message
*
- * @v data Raw signature data
- * @v len Length of raw data
- * @ret sig CMS signature
+ * @v image Image
+ * @ret sig CMS message
* @ret rc Return status code
*
- * On success, the caller holds a reference to the CMS signature, and
+ * On success, the caller holds a reference to the CMS message, and
* is responsible for ultimately calling cms_put().
*/
-int cms_signature ( const void *data, size_t len, struct cms_signature **sig ) {
- struct asn1_cursor cursor;
+int cms_message ( struct image *image, struct cms_message **cms ) {
+ int next;
int rc;
- /* Allocate and initialise signature */
- *sig = zalloc ( sizeof ( **sig ) );
- if ( ! *sig ) {
+ /* Allocate and initialise message */
+ *cms = zalloc ( sizeof ( **cms ) );
+ if ( ! *cms ) {
rc = -ENOMEM;
goto err_alloc;
}
- ref_init ( &(*sig)->refcnt, cms_free );
- INIT_LIST_HEAD ( &(*sig)->info );
-
- /* Allocate certificate list */
- (*sig)->certificates = x509_alloc_chain();
- if ( ! (*sig)->certificates ) {
- rc = -ENOMEM;
- goto err_alloc_chain;
+ ref_init ( &(*cms)->refcnt, cms_free );
+ INIT_LIST_HEAD ( &(*cms)->participants );
+ (*cms)->cipher = &cipher_null;
+
+ /* Get raw message data */
+ next = image_asn1 ( image, 0, &(*cms)->raw );
+ if ( next < 0 ) {
+ rc = next;
+ DBGC ( *cms, "CMS %p could not get raw ASN.1 data: %s\n",
+ *cms, strerror ( rc ) );
+ goto err_asn1;
}
- /* Initialise cursor */
- cursor.data = data;
- cursor.len = len;
- asn1_shrink_any ( &cursor );
+ /* Use only first message in image */
+ asn1_shrink_any ( (*cms)->raw );
- /* Parse signature */
- if ( ( rc = cms_parse ( *sig, &cursor ) ) != 0 )
+ /* Parse message */
+ if ( ( rc = cms_parse ( *cms ) ) != 0 )
goto err_parse;
return 0;
err_parse:
- err_alloc_chain:
- cms_put ( *sig );
+ err_asn1:
+ cms_put ( *cms );
err_alloc:
return rc;
}
@@ -530,94 +711,67 @@ int cms_signature ( const void *data, size_t len, struct cms_signature **sig ) {
/**
* Calculate digest of CMS-signed data
*
- * @v sig CMS signature
- * @v info Signer information
+ * @v cms CMS message
+ * @v part Participant information
* @v data Signed data
* @v len Length of signed data
* @v out Digest output
*/
-static void cms_digest ( struct cms_signature *sig,
- struct cms_signer_info *info,
- userptr_t data, size_t len, void *out ) {
- struct digest_algorithm *digest = info->digest;
+static void cms_digest ( struct cms_message *cms,
+ struct cms_participant *part,
+ const void *data, size_t len, void *out ) {
+ struct digest_algorithm *digest = part->digest;
uint8_t ctx[ digest->ctxsize ];
- uint8_t block[ digest->blocksize ];
- size_t offset = 0;
- size_t frag_len;
- /* Initialise digest */
+ /* Calculate digest */
digest_init ( digest, ctx );
-
- /* Process data one block at a time */
- while ( len ) {
- frag_len = len;
- if ( frag_len > sizeof ( block ) )
- frag_len = sizeof ( block );
- copy_from_user ( block, data, offset, frag_len );
- digest_update ( digest, ctx, block, frag_len );
- offset += frag_len;
- len -= frag_len;
- }
-
- /* Finalise digest */
+ digest_update ( digest, ctx, data, len );
digest_final ( digest, ctx, out );
- DBGC ( sig, "CMS %p/%p digest value:\n", sig, info );
- DBGC_HDA ( sig, 0, out, digest->digestsize );
+ DBGC ( cms, "CMS %p/%p digest value:\n", cms, part );
+ DBGC_HDA ( cms, 0, out, digest->digestsize );
}
/**
* Verify digest of CMS-signed data
*
- * @v sig CMS signature
- * @v info Signer information
+ * @v cms CMS message
+ * @v part Participant information
* @v cert Corresponding certificate
* @v data Signed data
* @v len Length of signed data
* @ret rc Return status code
*/
-static int cms_verify_digest ( struct cms_signature *sig,
- struct cms_signer_info *info,
+static int cms_verify_digest ( struct cms_message *cms,
+ struct cms_participant *part,
struct x509_certificate *cert,
- userptr_t data, size_t len ) {
- struct digest_algorithm *digest = info->digest;
- struct pubkey_algorithm *pubkey = info->pubkey;
- struct x509_public_key *public_key = &cert->subject.public_key;
+ const void *data, size_t len ) {
+ struct digest_algorithm *digest = part->digest;
+ struct pubkey_algorithm *pubkey = part->pubkey;
+ const struct asn1_cursor *key = &cert->subject.public_key.raw;
+ const struct asn1_cursor *value = &part->value;
uint8_t digest_out[ digest->digestsize ];
- uint8_t ctx[ pubkey->ctxsize ];
int rc;
/* Generate digest */
- cms_digest ( sig, info, data, len, digest_out );
-
- /* Initialise public-key algorithm */
- if ( ( rc = pubkey_init ( pubkey, ctx, public_key->raw.data,
- public_key->raw.len ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not initialise public key: %s\n",
- sig, info, strerror ( rc ) );
- goto err_init;
- }
+ cms_digest ( cms, part, data, len, digest_out );
/* Verify digest */
- if ( ( rc = pubkey_verify ( pubkey, ctx, digest, digest_out,
- info->signature,
- info->signature_len ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p signature verification failed: %s\n",
- sig, info, strerror ( rc ) );
- goto err_verify;
+ if ( ( rc = pubkey_verify ( pubkey, key, digest, digest_out,
+ value ) ) != 0 ) {
+ DBGC ( cms, "CMS %p/%p signature verification failed: %s\n",
+ cms, part, strerror ( rc ) );
+ return rc;
}
- err_verify:
- pubkey_final ( pubkey, ctx );
- err_init:
- return rc;
+ return 0;
}
/**
- * Verify CMS signature signer information
+ * Verify CMS message signer
*
- * @v sig CMS signature
- * @v info Signer information
+ * @v cms CMS message
+ * @v part Participant information
* @v data Signed data
* @v len Length of signed data
* @v time Time at which to validate certificates
@@ -625,42 +779,42 @@ static int cms_verify_digest ( struct cms_signature *sig,
* @v root Root certificate list, or NULL to use default
* @ret rc Return status code
*/
-static int cms_verify_signer_info ( struct cms_signature *sig,
- struct cms_signer_info *info,
- userptr_t data, size_t len,
- time_t time, struct x509_chain *store,
- struct x509_root *root ) {
+static int cms_verify_signer ( struct cms_message *cms,
+ struct cms_participant *part,
+ const void *data, size_t len,
+ time_t time, struct x509_chain *store,
+ struct x509_root *root ) {
struct x509_certificate *cert;
int rc;
/* Validate certificate chain */
- if ( ( rc = x509_validate_chain ( info->chain, time, store,
+ if ( ( rc = x509_validate_chain ( part->chain, time, store,
root ) ) != 0 ) {
- DBGC ( sig, "CMS %p/%p could not validate chain: %s\n",
- sig, info, strerror ( rc ) );
+ DBGC ( cms, "CMS %p/%p could not validate chain: %s\n",
+ cms, part, strerror ( rc ) );
return rc;
}
/* Extract code-signing certificate */
- cert = x509_first ( info->chain );
+ cert = x509_first ( part->chain );
assert ( cert != NULL );
/* Check that certificate can create digital signatures */
if ( ! ( cert->extensions.usage.bits & X509_DIGITAL_SIGNATURE ) ) {
- DBGC ( sig, "CMS %p/%p certificate cannot create signatures\n",
- sig, info );
+ DBGC ( cms, "CMS %p/%p certificate cannot create signatures\n",
+ cms, part );
return -EACCES_NON_SIGNING;
}
/* Check that certificate can sign code */
if ( ! ( cert->extensions.ext_usage.bits & X509_CODE_SIGNING ) ) {
- DBGC ( sig, "CMS %p/%p certificate is not code-signing\n",
- sig, info );
+ DBGC ( cms, "CMS %p/%p certificate is not code-signing\n",
+ cms, part );
return -EACCES_NON_CODE_SIGNING;
}
/* Verify digest */
- if ( ( rc = cms_verify_digest ( sig, info, cert, data, len ) ) != 0 )
+ if ( ( rc = cms_verify_digest ( cms, part, cert, data, len ) ) != 0 )
return rc;
return 0;
@@ -669,30 +823,37 @@ static int cms_verify_signer_info ( struct cms_signature *sig,
/**
* Verify CMS signature
*
- * @v sig CMS signature
- * @v data Signed data
- * @v len Length of signed data
+ * @v cms CMS message
+ * @v image Signed image
* @v name Required common name, or NULL to check all signatures
* @v time Time at which to validate certificates
* @v store Certificate store, or NULL to use default
* @v root Root certificate list, or NULL to use default
* @ret rc Return status code
*/
-int cms_verify ( struct cms_signature *sig, userptr_t data, size_t len,
+int cms_verify ( struct cms_message *cms, struct image *image,
const char *name, time_t time, struct x509_chain *store,
struct x509_root *root ) {
- struct cms_signer_info *info;
+ struct cms_participant *part;
struct x509_certificate *cert;
int count = 0;
int rc;
- /* Verify using all signerInfos */
- list_for_each_entry ( info, &sig->info, list ) {
- cert = x509_first ( info->chain );
+ /* Mark image as untrusted */
+ image_untrust ( image );
+
+ /* Sanity check */
+ if ( ! cms_is_signature ( cms ) )
+ return -ENOTTY;
+
+ /* Verify using all signers */
+ list_for_each_entry ( part, &cms->participants, list ) {
+ cert = x509_first ( part->chain );
if ( name && ( x509_check_name ( cert, name ) != 0 ) )
continue;
- if ( ( rc = cms_verify_signer_info ( sig, info, data, len, time,
- store, root ) ) != 0 )
+ if ( ( rc = cms_verify_signer ( cms, part, image->data,
+ image->len, time, store,
+ root ) ) != 0 )
return rc;
count++;
}
@@ -700,14 +861,286 @@ int cms_verify ( struct cms_signature *sig, userptr_t data, size_t len,
/* Check that we have verified at least one signature */
if ( count == 0 ) {
if ( name ) {
- DBGC ( sig, "CMS %p had no signatures matching name "
- "%s\n", sig, name );
+ DBGC ( cms, "CMS %p had no signatures matching name "
+ "%s\n", cms, name );
return -EACCES_WRONG_NAME;
} else {
- DBGC ( sig, "CMS %p had no signatures\n", sig );
+ DBGC ( cms, "CMS %p had no signatures\n", cms );
return -EACCES_NO_SIGNATURES;
}
}
+ /* Mark image as trusted */
+ image_trust ( image );
+
return 0;
}
+
+/**
+ * Identify CMS recipient corresponding to private key
+ *
+ * @v cms CMS message
+ * @v private_key Private key
+ * @ret part Participant information, or NULL if not found
+ */
+static struct cms_participant *
+cms_recipient ( struct cms_message *cms, struct private_key *private_key ) {
+ struct cms_participant *part;
+ struct x509_certificate *cert;
+
+ /* Identify certificate (if any) for which we have a private key */
+ cert = x509_find_key ( NULL, private_key );
+ if ( ! cert )
+ return NULL;
+
+ /* Identify corresponding recipient, if any */
+ list_for_each_entry ( part, &cms->participants, list ) {
+ if ( cert == x509_first ( part->chain ) )
+ return part;
+ }
+
+ return NULL;
+}
+
+/**
+ * Set CMS cipher key
+ *
+ * @v cms CMS message
+ * @v part Participant information
+ * @v private_key Private key
+ * @v ctx Cipher context
+ * @ret rc Return status code
+ */
+static int cms_cipher_key ( struct cms_message *cms,
+ struct cms_participant *part,
+ struct private_key *private_key, void *ctx ) {
+ struct cipher_algorithm *cipher = cms->cipher;
+ struct pubkey_algorithm *pubkey = part->pubkey;
+ const struct asn1_cursor *key = privkey_cursor ( private_key );
+ const struct asn1_cursor *value = &part->value;
+ struct asn1_builder cipher_key = { NULL, 0 };
+ int rc;
+
+ /* Decrypt cipher key */
+ if ( ( rc = pubkey_decrypt ( pubkey, key, value,
+ &cipher_key ) ) != 0 ) {
+ DBGC ( cms, "CMS %p/%p could not decrypt cipher key: %s\n",
+ cms, part, strerror ( rc ) );
+ DBGC_HDA ( cms, 0, value->data, value->len );
+ goto err_decrypt;
+ }
+ DBGC ( cms, "CMS %p/%p cipher key:\n", cms, part );
+ DBGC_HDA ( cms, 0, cipher_key.data, cipher_key.len );
+
+ /* Set cipher key */
+ if ( ( rc = cipher_setkey ( cipher, ctx, cipher_key.data,
+ cipher_key.len ) ) != 0 ) {
+ DBGC ( cms, "CMS %p could not set cipher key: %s\n",
+ cms, strerror ( rc ) );
+ goto err_setkey;
+ }
+
+ /* Set cipher initialization vector */
+ cipher_setiv ( cipher, ctx, cms->iv.data, cms->iv.len );
+ if ( cms->iv.len ) {
+ DBGC ( cms, "CMS %p cipher IV:\n", cms );
+ DBGC_HDA ( cms, 0, cms->iv.data, cms->iv.len );
+ }
+
+ err_setkey:
+ err_decrypt:
+ free ( cipher_key.data );
+ return rc;
+}
+
+/**
+ * Initialise cipher for CMS decryption
+ *
+ * @v cms CMS message
+ * @v private_key Private key
+ * @v ctx Cipher context
+ * @ret rc Return status code
+ */
+static int cms_cipher ( struct cms_message *cms,
+ struct private_key *private_key, void *ctx ) {
+ struct cms_participant *part;
+ int rc;
+
+ /* Identify a usable recipient */
+ part = cms_recipient ( cms, private_key );
+ if ( ! part ) {
+ DBGC ( cms, "CMS %p had no usable recipients\n", cms );
+ return -EACCES_NO_RECIPIENTS;
+ }
+
+ /* Decrypt and set cipher key */
+ if ( ( rc = cms_cipher_key ( cms, part, private_key, ctx ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Check CMS padding
+ *
+ * @v cms CMS message
+ * @v data Final block
+ * @v len Final block length
+ * @ret len Padding length, or negative error
+ */
+static int cms_verify_padding ( struct cms_message *cms, const void *data,
+ size_t len ) {
+ struct cipher_algorithm *cipher = cms->cipher;
+ const uint8_t *pad;
+ size_t pad_len;
+ unsigned int i;
+
+ /* Non-block ciphers do not use padding */
+ if ( ! is_block_cipher ( cipher ) )
+ return 0;
+
+ /* Block padding can never produce an empty file */
+ if ( len == 0 ) {
+ DBGC ( cms, "CMS %p invalid empty padding\n", cms );
+ return -EACCES_PAD;
+ }
+
+ /* Sanity check */
+ assert ( len >= cipher->blocksize );
+
+ /* Extract and verify padding */
+ pad = ( data + len - 1 );
+ pad_len = *pad;
+ if ( ( pad_len == 0 ) || ( pad_len > len ) ) {
+ DBGC ( cms, "CMS %p invalid padding length %zd\n",
+ cms, pad_len );
+ return -EACCES_PAD;
+ }
+ for ( i = 0 ; i < pad_len ; i++ ) {
+ if ( *(pad--) != pad_len ) {
+ DBGC ( cms, "CMS %p invalid padding\n", cms );
+ DBGC_HDA ( cms, 0, ( data + len - pad_len ), pad_len );
+ return -EACCES_PAD;
+ }
+ }
+
+ return pad_len;
+}
+
+/**
+ * Decrypt CMS message
+ *
+ * @v cms CMS message
+ * @v image Image to decrypt
+ * @v name Decrypted image name, or NULL to use default
+ * @v private_key Private key
+ * @ret rc Return status code
+ */
+int cms_decrypt ( struct cms_message *cms, struct image *image,
+ const char *name, struct private_key *private_key ) {
+ struct cipher_algorithm *cipher = cms->cipher;
+ const unsigned int original_flags = image->flags;
+ uint8_t ctx[ cipher->ctxsize ];
+ uint8_t ctxdup[ cipher->ctxsize ];
+ uint8_t auth[ cipher->authsize ];
+ uint8_t final[ cipher->blocksize ];
+ size_t final_len;
+ size_t bulk_len;
+ int pad_len;
+ int rc;
+
+ /* Check block size */
+ if ( ( image->len & ( cipher->blocksize - 1 ) ) != 0 ) {
+ DBGC ( cms, "CMS %p invalid length %zd\n", cms, image->len );
+ rc = -EACCES_LEN;
+ goto err_blocksize;
+ }
+
+ /* Initialise cipher */
+ if ( ( rc = cms_cipher ( cms, private_key, ctx ) ) != 0 )
+ goto err_cipher;
+
+ /* Duplicate cipher context for potential reencryption on error */
+ memcpy ( ctxdup, ctx, cipher->ctxsize );
+
+ /* Clear trusted flag before modifying image */
+ image_untrust ( image );
+
+ /* Temporarily unregister image, if applicable */
+ if ( original_flags & IMAGE_REGISTERED ) {
+ image_get ( image );
+ unregister_image ( image );
+ }
+
+ /* Decrypt all but the final block */
+ final_len = ( ( image->len && is_block_cipher ( cipher ) ) ?
+ cipher->blocksize : 0 );
+ bulk_len = ( image->len - final_len );
+ cipher_decrypt ( cipher, ctx, image->data, image->rwdata, bulk_len );
+
+ /* Decrypt final block */
+ cipher_decrypt ( cipher, ctx, ( image->data + bulk_len ), final,
+ final_len );
+
+ /* Check authentication tag, if applicable */
+ cipher_auth ( cipher, ctx, auth );
+ if ( ( cms->mac.len != cipher->authsize ) ||
+ ( memcmp ( cms->mac.data, auth, cipher->authsize ) != 0 ) ) {
+ DBGC ( cms, "CMS %p invalid authentication tag\n", cms );
+ DBGC_HDA ( cms, 0, auth, cipher->authsize );
+ rc = -EACCES_MAC;
+ goto err_auth;
+ }
+
+ /* Check block padding, if applicable */
+ if ( ( pad_len = cms_verify_padding ( cms, final, final_len ) ) < 0 ) {
+ rc = pad_len;
+ goto err_pad;
+ }
+
+ /* Update image name. Do this as the last possible failure, so
+ * that we do not have to include any error-handling code path
+ * to restore the original image name (which may itself fail).
+ */
+ if ( name ) {
+ if ( ( rc = image_set_name ( image, name ) ) != 0 )
+ goto err_set_name;
+ } else {
+ image_strip_suffix ( image );
+ }
+
+ /* Overwrite final fragment and strip block padding. Do this
+ * only once no further failure paths exist, so that we do not
+ * have to include include any error-handling code path to
+ * reconstruct the block padding.
+ */
+ memcpy ( ( image->rwdata + bulk_len ), final, final_len );
+ image->len -= pad_len;
+
+ /* Clear image type and re-register image, if applicable */
+ image->type = NULL;
+ if ( original_flags & IMAGE_REGISTERED ) {
+ register_image ( image );
+ image_put ( image );
+ }
+
+ return 0;
+
+ err_set_name:
+ err_pad:
+ err_auth:
+ /* Reencrypt all overwritten portions. This can be done since
+ * we have deliberately not overwritten the final block
+ * containing the potentially invalid (and therefore
+ * unreproducible) block padding.
+ */
+ cipher_encrypt ( cipher, ctxdup, image->data, image->rwdata, bulk_len );
+ if ( original_flags & IMAGE_REGISTERED ) {
+ register_image ( image ); /* Cannot fail on re-registration */
+ image_put ( image );
+ }
+ image->flags = original_flags;
+ err_cipher:
+ err_blocksize:
+ return rc;
+}
diff --git a/src/crypto/crc32.c b/src/crypto/crc32.c
index cfef68c02..9ab4899c6 100644
--- a/src/crypto/crc32.c
+++ b/src/crypto/crc32.c
@@ -20,6 +20,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/crc32.h>
diff --git a/src/crypto/crypto_null.c b/src/crypto/crypto_null.c
index 0ad463c3e..8637987b1 100644
--- a/src/crypto/crypto_null.c
+++ b/src/crypto/crypto_null.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -93,53 +94,36 @@ struct cipher_algorithm cipher_null = {
.auth = cipher_null_auth,
};
-int pubkey_null_init ( void *ctx __unused, const void *key __unused,
- size_t key_len __unused ) {
+int pubkey_null_encrypt ( const struct asn1_cursor *key __unused,
+ const struct asn1_cursor *plaintext __unused,
+ struct asn1_builder *ciphertext __unused ) {
return 0;
}
-size_t pubkey_null_max_len ( void *ctx __unused ) {
+int pubkey_null_decrypt ( const struct asn1_cursor *key __unused,
+ const struct asn1_cursor *ciphertext __unused,
+ struct asn1_builder *plaintext __unused ) {
return 0;
}
-int pubkey_null_encrypt ( void *ctx __unused, const void *plaintext __unused,
- size_t plaintext_len __unused,
- void *ciphertext __unused ) {
- return 0;
-}
-
-int pubkey_null_decrypt ( void *ctx __unused, const void *ciphertext __unused,
- size_t ciphertext_len __unused,
- void *plaintext __unused ) {
- return 0;
-}
-
-int pubkey_null_sign ( void *ctx __unused,
+int pubkey_null_sign ( const struct asn1_cursor *key __unused,
struct digest_algorithm *digest __unused,
- const void *value __unused, void *signature __unused ) {
+ const void *value __unused,
+ struct asn1_builder *signature __unused ) {
return 0;
}
-int pubkey_null_verify ( void *ctx __unused,
+int pubkey_null_verify ( const struct asn1_cursor *key __unused,
struct digest_algorithm *digest __unused,
const void *value __unused,
- const void *signature __unused ,
- size_t signature_len __unused ) {
+ const struct asn1_cursor *signature __unused ) {
return 0;
}
-void pubkey_null_final ( void *ctx __unused ) {
- /* Do nothing */
-}
-
struct pubkey_algorithm pubkey_null = {
.name = "null",
- .ctxsize = 0,
- .init = pubkey_null_init,
- .max_len = pubkey_null_max_len,
.encrypt = pubkey_null_encrypt,
.decrypt = pubkey_null_decrypt,
.sign = pubkey_null_sign,
.verify = pubkey_null_verify,
- .final = pubkey_null_final,
};
diff --git a/src/crypto/deflate.c b/src/crypto/deflate.c
index 7ad39ec1b..1d54749e0 100644
--- a/src/crypto/deflate.c
+++ b/src/crypto/deflate.c
@@ -22,13 +22,13 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
-#include <ipxe/uaccess.h>
#include <ipxe/deflate.h>
/** @file
@@ -300,24 +300,21 @@ static int deflate_alphabet ( struct deflate *deflate,
* Attempt to accumulate bits from input stream
*
* @v deflate Decompressor
- * @v in Compressed input data
* @v target Number of bits to accumulate
* @ret excess Number of excess bits accumulated (may be negative)
*/
static int deflate_accumulate ( struct deflate *deflate,
- struct deflate_chunk *in,
unsigned int target ) {
uint8_t byte;
while ( deflate->bits < target ) {
/* Check for end of input */
- if ( in->offset >= in->len )
+ if ( deflate->in == deflate->end )
break;
/* Acquire byte from input */
- copy_from_user ( &byte, in->data, in->offset++,
- sizeof ( byte ) );
+ byte = *(deflate->in++);
deflate->accumulator = ( deflate->accumulator |
( byte << deflate->bits ) );
deflate->rotalumucca = ( deflate->rotalumucca |
@@ -359,12 +356,10 @@ static int deflate_consume ( struct deflate *deflate, unsigned int count ) {
* Attempt to extract a fixed number of bits from input stream
*
* @v deflate Decompressor
- * @v in Compressed input data
* @v target Number of bits to extract
* @ret data Extracted bits (or negative if not yet accumulated)
*/
-static int deflate_extract ( struct deflate *deflate, struct deflate_chunk *in,
- unsigned int target ) {
+static int deflate_extract ( struct deflate *deflate, unsigned int target ) {
int excess;
int data;
@@ -373,7 +368,7 @@ static int deflate_extract ( struct deflate *deflate, struct deflate_chunk *in,
return 0;
/* Attempt to accumulate bits */
- excess = deflate_accumulate ( deflate, in, target );
+ excess = deflate_accumulate ( deflate, target );
if ( excess < 0 )
return excess;
@@ -389,12 +384,10 @@ static int deflate_extract ( struct deflate *deflate, struct deflate_chunk *in,
* Attempt to decode a Huffman-coded symbol from input stream
*
* @v deflate Decompressor
- * @v in Compressed input data
* @v alphabet Huffman alphabet
* @ret code Raw code (or negative if not yet accumulated)
*/
static int deflate_decode ( struct deflate *deflate,
- struct deflate_chunk *in,
struct deflate_alphabet *alphabet ) {
struct deflate_huf_symbols *huf_sym;
uint16_t huf;
@@ -407,7 +400,7 @@ static int deflate_decode ( struct deflate *deflate,
* even if the stream still contains some complete
* Huffman-coded symbols.
*/
- deflate_accumulate ( deflate, in, DEFLATE_HUFFMAN_BITS );
+ deflate_accumulate ( deflate, DEFLATE_HUFFMAN_BITS );
/* Normalise the bit-reversed accumulated value to 16 bits */
huf = ( deflate->rotalumucca >> 16 );
@@ -449,24 +442,22 @@ static void deflate_discard_to_byte ( struct deflate *deflate ) {
* Copy data to output buffer (if available)
*
* @v out Output data buffer
- * @v start Source data
- * @v offset Starting offset within source data
+ * @v in Input data
* @v len Length to copy
*/
-static void deflate_copy ( struct deflate_chunk *out,
- userptr_t start, size_t offset, size_t len ) {
- size_t out_offset = out->offset;
+static void deflate_copy ( struct deflate_chunk *out, const void *in,
+ size_t len ) {
+ const uint8_t *in_byte = in;
+ uint8_t *out_byte = ( out->data + out->offset );
size_t copy_len;
/* Copy data one byte at a time, to allow for overlap */
- if ( out_offset < out->len ) {
- copy_len = ( out->len - out_offset );
+ if ( out->offset < out->len ) {
+ copy_len = ( out->len - out->offset );
if ( copy_len > len )
copy_len = len;
- while ( copy_len-- ) {
- memcpy_user ( out->data, out_offset++,
- start, offset++, 1 );
- }
+ while ( copy_len-- )
+ *(out_byte++) = *(in_byte++);
}
out->offset += len;
}
@@ -475,7 +466,8 @@ static void deflate_copy ( struct deflate_chunk *out,
* Inflate compressed data
*
* @v deflate Decompressor
- * @v in Compressed input data
+ * @v data Compressed input data
+ * @v len Length of compressed input data
* @v out Output data buffer
* @ret rc Return status code
*
@@ -489,10 +481,13 @@ static void deflate_copy ( struct deflate_chunk *out,
* caller can use this to find the length of the decompressed data
* before allocating the output data buffer.
*/
-int deflate_inflate ( struct deflate *deflate,
- struct deflate_chunk *in,
+int deflate_inflate ( struct deflate *deflate, const void *data, size_t len,
struct deflate_chunk *out ) {
+ /* Store input data pointers */
+ deflate->in = data;
+ deflate->end = ( data + len );
+
/* This could be implemented more neatly if gcc offered a
* means for enforcing tail recursion.
*/
@@ -509,7 +504,7 @@ int deflate_inflate ( struct deflate *deflate,
int cm;
/* Extract header */
- header = deflate_extract ( deflate, in, ZLIB_HEADER_BITS );
+ header = deflate_extract ( deflate, ZLIB_HEADER_BITS );
if ( header < 0 ) {
deflate->resume = &&zlib_header;
return 0;
@@ -538,7 +533,7 @@ int deflate_inflate ( struct deflate *deflate,
int btype;
/* Extract block header */
- header = deflate_extract ( deflate, in, DEFLATE_HEADER_BITS );
+ header = deflate_extract ( deflate, DEFLATE_HEADER_BITS );
if ( header < 0 ) {
deflate->resume = &&block_header;
return 0;
@@ -571,17 +566,17 @@ int deflate_inflate ( struct deflate *deflate,
}
literal_len: {
- int len;
+ int llen;
/* Extract LEN field */
- len = deflate_extract ( deflate, in, DEFLATE_LITERAL_LEN_BITS );
- if ( len < 0 ) {
+ llen = deflate_extract ( deflate, DEFLATE_LITERAL_LEN_BITS );
+ if ( llen < 0 ) {
deflate->resume = &&literal_len;
return 0;
}
/* Record length of literal data */
- deflate->remaining = len;
+ deflate->remaining = llen;
DBGC2 ( deflate, "DEFLATE %p literal block length %#04zx\n",
deflate, deflate->remaining );
}
@@ -590,7 +585,7 @@ int deflate_inflate ( struct deflate *deflate,
int nlen;
/* Extract NLEN field */
- nlen = deflate_extract ( deflate, in, DEFLATE_LITERAL_LEN_BITS);
+ nlen = deflate_extract ( deflate, DEFLATE_LITERAL_LEN_BITS );
if ( nlen < 0 ) {
deflate->resume = &&literal_nlen;
return 0;
@@ -608,20 +603,20 @@ int deflate_inflate ( struct deflate *deflate,
literal_data: {
size_t in_remaining;
- size_t len;
+ size_t dlen;
/* Calculate available amount of literal data */
- in_remaining = ( in->len - in->offset );
- len = deflate->remaining;
- if ( len > in_remaining )
- len = in_remaining;
+ in_remaining = ( deflate->end - deflate->in );
+ dlen = deflate->remaining;
+ if ( dlen > in_remaining )
+ dlen = in_remaining;
/* Copy data to output buffer */
- deflate_copy ( out, in->data, in->offset, len );
+ deflate_copy ( out, deflate->in, dlen );
/* Consume data from input buffer */
- in->offset += len;
- deflate->remaining -= len;
+ deflate->in += dlen;
+ deflate->remaining -= dlen;
/* Finish processing if we are blocked */
if ( deflate->remaining ) {
@@ -657,7 +652,7 @@ int deflate_inflate ( struct deflate *deflate,
unsigned int hclen;
/* Extract block header */
- header = deflate_extract ( deflate, in, DEFLATE_DYNAMIC_BITS );
+ header = deflate_extract ( deflate, DEFLATE_DYNAMIC_BITS );
if ( header < 0 ) {
deflate->resume = &&dynamic_header;
return 0;
@@ -684,7 +679,7 @@ int deflate_inflate ( struct deflate *deflate,
}
dynamic_codelen: {
- int len;
+ int clen;
unsigned int index;
int rc;
@@ -692,18 +687,18 @@ int deflate_inflate ( struct deflate *deflate,
while ( deflate->length_index < deflate->length_target ) {
/* Extract code length length */
- len = deflate_extract ( deflate, in,
- DEFLATE_CODELEN_BITS );
- if ( len < 0 ) {
+ clen = deflate_extract ( deflate,
+ DEFLATE_CODELEN_BITS );
+ if ( clen < 0 ) {
deflate->resume = &&dynamic_codelen;
return 0;
}
/* Store code length */
index = deflate_codelen_map[deflate->length_index++];
- deflate_set_length ( deflate, index, len );
+ deflate_set_length ( deflate, index, clen );
DBGCP ( deflate, "DEFLATE %p codelen for %d is %d\n",
- deflate, index, len );
+ deflate, index, clen );
}
/* Generate code length alphabet */
@@ -722,25 +717,25 @@ int deflate_inflate ( struct deflate *deflate,
}
dynamic_litlen_distance: {
- int len;
+ int clen;
int index;
/* Decode literal/length/distance code length */
- len = deflate_decode ( deflate, in, &deflate->distance_codelen);
- if ( len < 0 ) {
+ clen = deflate_decode ( deflate, &deflate->distance_codelen );
+ if ( clen < 0 ) {
deflate->resume = &&dynamic_litlen_distance;
return 0;
}
/* Prepare for extra bits */
- if ( len < 16 ) {
- deflate->length = len;
+ if ( clen < 16 ) {
+ deflate->length = clen;
deflate->extra_bits = 0;
deflate->dup_len = 1;
} else {
static const uint8_t dup_len[3] = { 3, 3, 11 };
static const uint8_t extra_bits[3] = { 2, 3, 7 };
- index = ( len - 16 );
+ index = ( clen - 16 );
deflate->dup_len = dup_len[index];
deflate->extra_bits = extra_bits[index];
if ( index )
@@ -753,7 +748,7 @@ int deflate_inflate ( struct deflate *deflate,
unsigned int dup_len;
/* Extract extra bits */
- extra = deflate_extract ( deflate, in, deflate->extra_bits );
+ extra = deflate_extract ( deflate, deflate->extra_bits );
if ( extra < 0 ) {
deflate->resume = &&dynamic_litlen_distance_extra;
return 0;
@@ -830,7 +825,7 @@ int deflate_inflate ( struct deflate *deflate,
while ( 1 ) {
/* Decode Huffman code */
- code = deflate_decode ( deflate, in, &deflate->litlen );
+ code = deflate_decode ( deflate, &deflate->litlen );
if ( code < 0 ) {
deflate->resume = &&lzhuf_litlen;
return 0;
@@ -844,8 +839,7 @@ int deflate_inflate ( struct deflate *deflate,
DBGCP ( deflate, "DEFLATE %p literal %#02x "
"('%c')\n", deflate, byte,
( isprint ( byte ) ? byte : '.' ) );
- deflate_copy ( out, virt_to_user ( &byte ), 0,
- sizeof ( byte ) );
+ deflate_copy ( out, &byte, sizeof ( byte ) );
} else if ( code == DEFLATE_LITLEN_END ) {
@@ -876,7 +870,7 @@ int deflate_inflate ( struct deflate *deflate,
int extra;
/* Extract extra bits */
- extra = deflate_extract ( deflate, in, deflate->extra_bits );
+ extra = deflate_extract ( deflate, deflate->extra_bits );
if ( extra < 0 ) {
deflate->resume = &&lzhuf_litlen_extra;
return 0;
@@ -892,8 +886,7 @@ int deflate_inflate ( struct deflate *deflate,
unsigned int bits;
/* Decode Huffman code */
- code = deflate_decode ( deflate, in,
- &deflate->distance_codelen );
+ code = deflate_decode ( deflate, &deflate->distance_codelen );
if ( code < 0 ) {
deflate->resume = &&lzhuf_distance;
return 0;
@@ -914,7 +907,7 @@ int deflate_inflate ( struct deflate *deflate,
size_t dup_distance;
/* Extract extra bits */
- extra = deflate_extract ( deflate, in, deflate->extra_bits );
+ extra = deflate_extract ( deflate, deflate->extra_bits );
if ( extra < 0 ) {
deflate->resume = &&lzhuf_distance_extra;
return 0;
@@ -934,7 +927,7 @@ int deflate_inflate ( struct deflate *deflate,
}
/* Copy data, allowing for overlap */
- deflate_copy ( out, out->data, ( out->offset - dup_distance ),
+ deflate_copy ( out, ( out->data + out->offset - dup_distance ),
dup_len );
/* Process next literal/length symbol */
@@ -972,7 +965,7 @@ int deflate_inflate ( struct deflate *deflate,
* cases involved in calling deflate_extract() to
* obtain a full 32 bits.
*/
- excess = deflate_accumulate ( deflate, in, ZLIB_ADLER32_BITS );
+ excess = deflate_accumulate ( deflate, ZLIB_ADLER32_BITS );
if ( excess < 0 ) {
deflate->resume = &&zlib_adler32;
return 0;
diff --git a/src/crypto/des.c b/src/crypto/des.c
index 6918bec3e..206f78d50 100644
--- a/src/crypto/des.c
+++ b/src/crypto/des.c
@@ -369,6 +369,7 @@ static void des_init ( void ) {
/** Initialisation function */
struct init_fn des_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "des",
.initialise = des_init,
};
diff --git a/src/crypto/dhe.c b/src/crypto/dhe.c
index 2da107d24..2785a500b 100644
--- a/src/crypto/dhe.c
+++ b/src/crypto/dhe.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -57,8 +58,7 @@ int dhe_key ( const void *modulus, size_t len, const void *generator,
unsigned int size = bigint_required_size ( len );
unsigned int private_size = bigint_required_size ( private_len );
bigint_t ( size ) *mod;
- bigint_t ( private_size ) *exp;
- size_t tmp_len = bigint_mod_exp_tmp_len ( mod, exp );
+ size_t tmp_len = bigint_mod_exp_tmp_len ( mod );
struct {
bigint_t ( size ) modulus;
bigint_t ( size ) generator;
diff --git a/src/crypto/drbg.c b/src/crypto/drbg.c
index a3366e806..c4dc7646d 100644
--- a/src/crypto/drbg.c
+++ b/src/crypto/drbg.c
@@ -34,6 +34,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/ecb.c b/src/crypto/ecb.c
index 3c9cf340c..73eef09c2 100644
--- a/src/crypto/ecb.c
+++ b/src/crypto/ecb.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <assert.h>
#include <ipxe/crypto.h>
diff --git a/src/crypto/ecdhe.c b/src/crypto/ecdhe.c
new file mode 100644
index 000000000..016253457
--- /dev/null
+++ b/src/crypto/ecdhe.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * Elliptic Curve Ephemeral Diffie-Hellman (ECDHE) key exchange
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/ecdhe.h>
+
+/**
+ * Calculate ECDHE key
+ *
+ * @v curve Elliptic curve
+ * @v partner Partner public curve point
+ * @v private Private key
+ * @v public Public curve point to fill in (may overlap partner key)
+ * @v shared Shared secret curve point to fill in
+ * @ret rc Return status code
+ */
+int ecdhe_key ( struct elliptic_curve *curve, const void *partner,
+ const void *private, void *public, void *shared ) {
+ int rc;
+
+ /* Construct shared key */
+ if ( ( rc = elliptic_multiply ( curve, partner, private,
+ shared ) ) != 0 ) {
+ DBGC ( curve, "CURVE %s could not generate shared key: %s\n",
+ curve->name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Construct public key */
+ if ( ( rc = elliptic_multiply ( curve, curve->base, private,
+ public ) ) != 0 ) {
+ DBGC ( curve, "CURVE %s could not generate public key: %s\n",
+ curve->name, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check that partner and shared keys are not the point at infinity */
+ if ( elliptic_is_infinity ( curve, shared ) ) {
+ DBGC ( curve, "CURVE %s constructed point at infinity\n",
+ curve->name );
+ return -EPERM;
+ }
+
+ return 0;
+}
diff --git a/src/crypto/ecdsa.c b/src/crypto/ecdsa.c
new file mode 100644
index 000000000..6f10a1a0f
--- /dev/null
+++ b/src/crypto/ecdsa.c
@@ -0,0 +1,944 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * Elliptic curve digital signature algorithm (ECDSA)
+ *
+ * The elliptic curve public key format is documented in RFC 5480.
+ * The original private key format is documented in RFC 5915, and the
+ * generic container PKCS#8 format documented in RFC 5208.
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ipxe/crypto.h>
+#include <ipxe/bigint.h>
+#include <ipxe/hmac_drbg.h>
+#include <ipxe/ecdsa.h>
+
+/* Disambiguate the various error causes */
+#define EINVAL_POINTSIZE \
+ __einfo_error ( EINFO_EINVAL_POINTSIZE )
+#define EINFO_EINVAL_POINTSIZE \
+ __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid point size" )
+#define EINVAL_KEYSIZE \
+ __einfo_error ( EINFO_EINVAL_KEYSIZE )
+#define EINFO_EINVAL_KEYSIZE \
+ __einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid key size" )
+#define EINVAL_COMPRESSION \
+ __einfo_error ( EINFO_EINVAL_COMPRESSION )
+#define EINFO_EINVAL_COMPRESSION \
+ __einfo_uniqify ( EINFO_EINVAL, 0x03, "Invalid compression")
+#define EINVAL_INFINITY \
+ __einfo_error ( EINFO_EINVAL_INFINITY )
+#define EINFO_EINVAL_INFINITY \
+ __einfo_uniqify ( EINFO_EINVAL, 0x04, "Point is infinity" )
+#define EINVAL_SIGNATURE \
+ __einfo_error ( EINFO_EINVAL_SIGNATURE )
+#define EINFO_EINVAL_SIGNATURE \
+ __einfo_uniqify ( EINFO_EINVAL, 0x05, "Invalid signature" )
+
+/** "ecPublicKey" object identifier */
+static uint8_t oid_ecpublickey[] = { ASN1_OID_ECPUBLICKEY };
+
+/** Generic elliptic curve container algorithm
+ *
+ * The actual curve to be used is identified via the algorithm
+ * parameters, rather than the top-level OID.
+ */
+struct asn1_algorithm ecpubkey_algorithm __asn1_algorithm = {
+ .name = "ecPublicKey",
+ .oid = ASN1_CURSOR ( oid_ecpublickey ),
+ .pubkey = &ecdsa_algorithm,
+};
+
+/** An ECDSA key */
+struct ecdsa_key {
+ /** Elliptic curve */
+ struct elliptic_curve *curve;
+ /** Public curve point */
+ const void *public;
+ /** Private multiple of base curve point (if applicable) */
+ const void *private;
+};
+
+/** ECDSA context */
+struct ecdsa_context {
+ /** Key */
+ struct ecdsa_key key;
+ /** Big integer size */
+ unsigned int size;
+ /** Digest algorithm */
+ struct digest_algorithm *digest;
+ /** Digest length */
+ size_t zlen;
+
+ /** Dynamically allocated storage */
+ void *dynamic;
+ /** Element 0 of modulus N (i.e. curve group order */
+ bigint_element_t *modulus0;
+ /** Element 0 of constant N-2 (for Fermat's little theorem) */
+ bigint_element_t *fermat0;
+ /** Element 0 of Montgomery constant R^2 mod N */
+ bigint_element_t *square0;
+ /** Element 0 of constant 1 (in Montgomery form) */
+ bigint_element_t *one0;
+ /** Element 0 of digest value "z" */
+ bigint_element_t *z0;
+ /** Element 0 of random key "k" */
+ bigint_element_t *k0;
+ /** Element 0 of signature value "r" */
+ bigint_element_t *r0;
+ /** Element 0 of signature value "s" */
+ bigint_element_t *s0;
+ /** Element 0 of temporary value */
+ bigint_element_t *temp0;
+ /** Element 0 of product buffer */
+ bigint_element_t *product0;
+ /** Curve point 1 */
+ void *point1;
+ /** Curve point 2 */
+ void *point2;
+ /** Scalar value */
+ void *scalar;
+ /** HMAC_DRBG state for random value generation */
+ struct hmac_drbg_state *drbg;
+};
+
+/**
+ * Parse ECDSA key
+ *
+ * @v key ECDSA key
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int ecdsa_parse_key ( struct ecdsa_key *key,
+ const struct asn1_cursor *raw ) {
+ struct asn1_algorithm *algorithm;
+ struct asn1_cursor cursor;
+ struct asn1_cursor curve;
+ struct asn1_cursor private;
+ const uint8_t *compression;
+ int is_private;
+ int rc;
+
+ /* Enter subjectPublicKeyInfo/ECPrivateKey */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
+ asn1_invalidate_cursor ( &curve );
+ asn1_invalidate_cursor ( &private );
+
+ /* Determine key format */
+ if ( asn1_type ( &cursor ) == ASN1_INTEGER ) {
+
+ /* Private key */
+ is_private = 1;
+
+ /* Skip version */
+ asn1_skip_any ( &cursor );
+
+ /* Parse privateKeyAlgorithm, if present */
+ if ( asn1_type ( &cursor ) == ASN1_SEQUENCE ) {
+
+ /* PKCS#8 format */
+ DBGC ( key, "ECDSA %p is in PKCS#8 format\n", key );
+
+ /* Parse privateKeyAlgorithm */
+ memcpy ( &curve, &cursor, sizeof ( curve ) );
+ asn1_skip_any ( &cursor );
+
+ /* Enter privateKey */
+ asn1_enter ( &cursor, ASN1_OCTET_STRING );
+
+ /* Enter ECPrivateKey */
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
+
+ /* Skip version */
+ asn1_skip ( &cursor, ASN1_INTEGER );
+ }
+
+ /* Parse privateKey */
+ memcpy ( &private, &cursor, sizeof ( private ) );
+ asn1_enter ( &private, ASN1_OCTET_STRING );
+ asn1_skip_any ( &cursor );
+
+ /* Parse parameters, if present */
+ if ( asn1_type ( &cursor ) == ASN1_EXPLICIT_TAG ( 0 ) ) {
+ memcpy ( &curve, &cursor, sizeof ( curve ) );
+ asn1_enter_any ( &curve );
+ asn1_skip_any ( &cursor );
+ }
+
+ /* Enter publicKey */
+ asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 1 ) );
+
+ } else {
+
+ /* Public key */
+ is_private = 0;
+
+ /* Parse algorithm */
+ memcpy ( &curve, &cursor, sizeof ( curve ) );
+ asn1_skip_any ( &cursor );
+ }
+
+ /* Enter publicKey */
+ asn1_enter_bits ( &cursor, NULL );
+
+ /* Identify curve */
+ if ( ( rc = asn1_curve_algorithm ( &curve, &ecpubkey_algorithm,
+ &algorithm ) ) != 0 ) {
+ DBGC ( key, "ECDSA %p unknown curve: %s\n",
+ key, strerror ( rc ) );
+ DBGC_HDA ( key, 0, raw->data, raw->len );
+ return rc;
+ }
+ key->curve = algorithm->curve;
+ DBGC ( key, "ECDSA %p is a %s (%s) %s key\n", key, algorithm->name,
+ key->curve->name, ( is_private ? "private" : "public" ) );
+
+ /* Check public key length */
+ if ( cursor.len != ( sizeof ( *compression ) +
+ key->curve->pointsize ) ) {
+ DBGC ( key, "ECDSA %p invalid public key length %zd\n",
+ key, cursor.len );
+ DBGC_HDA ( key, 0, raw->data, raw->len );
+ return -EINVAL_POINTSIZE;
+ }
+
+ /* Check that key is uncompressed */
+ compression = cursor.data;
+ if ( *compression != ECDSA_UNCOMPRESSED ) {
+ DBGC ( key, "ECDSA %p invalid compression %#02x\n",
+ key, *compression );
+ DBGC_HDA ( key, 0, raw->data, raw->len );
+ return -EINVAL_COMPRESSION;
+ }
+
+ /* Extract public curve point */
+ key->public = ( cursor.data + sizeof ( *compression ) );
+ DBGC ( key, "ECDSA %p public curve point:\n", key );
+ DBGC_HDA ( key, 0, key->public, key->curve->pointsize );
+
+ /* Check that public key is not the point at infinity */
+ if ( elliptic_is_infinity ( key->curve, key->public ) ) {
+ DBGC ( key, "ECDSA %p public curve point is infinity\n", key );
+ return -EINVAL_INFINITY;
+ }
+
+ /* Extract private key, if applicable */
+ if ( is_private ) {
+
+ /* Check private key length */
+ if ( private.len != key->curve->keysize ) {
+ DBGC ( key, "ECDSA %p invalid private key length "
+ "%zd\n", key, private.len );
+ DBGC_HDA ( key, 0, raw->data, raw->len );
+ return -EINVAL_KEYSIZE;
+ }
+
+ /* Extract private key */
+ key->private = private.data;
+ DBGC ( key, "ECDSA %p private multiplier:\n", key );
+ DBGC_HDA ( key, 0, key->private, key->curve->keysize );
+
+ } else {
+
+ /* No private key */
+ key->private = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * Parse ECDSA signature value
+ *
+ * @v ctx ECDSA context
+ * @v rs0 Element 0 of signature "r" or "s" value
+ * @v raw ASN.1 cursor
+ * @ret rc Return status code
+ */
+static int ecdsa_parse_signature ( struct ecdsa_context *ctx,
+ bigint_element_t *rs0,
+ const struct asn1_cursor *raw ) {
+ size_t keysize = ctx->key.curve->keysize;
+ unsigned int size = ctx->size;
+ bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
+ ( ( void * ) ctx->modulus0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *rs =
+ ( ( void * ) rs0 );
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Enter integer */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
+ if ( ( rc = asn1_enter_unsigned ( &cursor ) ) != 0 ) {
+ DBGC ( ctx, "ECDSA %p invalid integer:\n", ctx );
+ DBGC_HDA ( ctx, 0, raw->data, raw->len );
+ return rc;
+ }
+
+ /* Extract value */
+ if ( cursor.len > keysize ) {
+ DBGC ( ctx, "ECDSA %p invalid signature value:\n", ctx );
+ DBGC_HDA ( ctx, 0, raw->data, raw->len );
+ return -EINVAL_KEYSIZE;
+ }
+ bigint_init ( rs, cursor.data, cursor.len );
+
+ /* Check that value is within the required range */
+ if ( bigint_is_zero ( rs ) || bigint_is_geq ( rs, modulus ) ) {
+ DBGC ( ctx, "ECDSA %p out-of-range signature value:\n", ctx );
+ DBGC_HDA ( ctx, 0, raw->data, raw->len );
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+/**
+ * Prepend ECDSA signature value
+ *
+ * @v ctx ECDSA context
+ * @v rs0 Element 0 of signature "r" or "s" value
+ * @v builder ASN.1 builder
+ * @ret rc Return status code
+ */
+static int ecdsa_prepend_signature ( struct ecdsa_context *ctx,
+ bigint_element_t *rs0,
+ struct asn1_builder *builder ) {
+ size_t keysize = ctx->key.curve->keysize;
+ unsigned int size = ctx->size;
+ bigint_t ( size ) __attribute__ (( may_alias )) *rs =
+ ( ( void * ) rs0 );
+ uint8_t buf[ 1 /* potential sign byte */ + keysize ];
+ uint8_t *data;
+ size_t len;
+ int rc;
+
+ /* Construct value */
+ buf[0] = 0;
+ bigint_done ( rs, &buf[1], keysize );
+
+ /* Strip leading zeros */
+ data = buf;
+ len = sizeof ( buf );
+ while ( ( len > 1 ) && ( data[0] == 0 ) && ( data[1] < 0x80 ) ) {
+ data++;
+ len--;
+ }
+
+ /* Prepend integer */
+ if ( ( rc = asn1_prepend ( builder, ASN1_INTEGER, data, len ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Allocate ECDSA context dynamic storage
+ *
+ * @v ctx ECDSA context
+ * @ret rc Return status code
+ */
+static int ecdsa_alloc ( struct ecdsa_context *ctx ) {
+ struct elliptic_curve *curve = ctx->key.curve;
+ size_t pointsize = curve->pointsize;
+ size_t keysize = curve->keysize;
+ unsigned int size =
+ bigint_required_size ( keysize + 1 /* for addition */ );
+ struct {
+ bigint_t ( size ) modulus;
+ bigint_t ( size ) fermat;
+ bigint_t ( size ) square;
+ bigint_t ( size ) one;
+ bigint_t ( size ) z;
+ bigint_t ( size ) k;
+ bigint_t ( size ) r;
+ bigint_t ( size ) s;
+ bigint_t ( size ) temp;
+ bigint_t ( size * 2 ) product;
+ uint8_t point1[pointsize];
+ uint8_t point2[pointsize];
+ uint8_t scalar[keysize];
+ struct hmac_drbg_state drbg;
+ } *dynamic;
+
+ /* Allocate dynamic storage */
+ dynamic = malloc ( sizeof ( *dynamic ) );
+ if ( ! dynamic )
+ return -ENOMEM;
+
+ /* Populate context */
+ ctx->size = size;
+ ctx->dynamic = dynamic;
+ ctx->modulus0 = dynamic->modulus.element;
+ ctx->fermat0 = dynamic->fermat.element;
+ ctx->square0 = dynamic->square.element;
+ ctx->one0 = dynamic->one.element;
+ ctx->z0 = dynamic->z.element;
+ ctx->k0 = dynamic->k.element;
+ ctx->r0 = dynamic->r.element;
+ ctx->s0 = dynamic->s.element;
+ ctx->temp0 = dynamic->temp.element;
+ ctx->product0 = dynamic->product.element;
+ ctx->point1 = dynamic->point1;
+ ctx->point2 = dynamic->point2;
+ ctx->scalar = dynamic->scalar;
+ ctx->drbg = &dynamic->drbg;
+
+ return 0;
+}
+
+/**
+ * Free ECDSA context dynamic storage
+ *
+ * @v ctx ECDSA context
+ */
+static void ecdsa_free ( struct ecdsa_context *ctx ) {
+
+ /* Free dynamic storage */
+ free ( ctx->dynamic );
+}
+
+/**
+ * Initialise ECDSA values
+ *
+ * @v ctx ECDSA context
+ * @v digest Digest algorithm
+ * @v value Digest value
+ */
+static void ecdsa_init_values ( struct ecdsa_context *ctx,
+ struct digest_algorithm *digest,
+ const void *value ) {
+ struct elliptic_curve *curve = ctx->key.curve;
+ unsigned int size = ctx->size;
+ bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
+ ( ( void * ) ctx->modulus0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *fermat =
+ ( ( void * ) ctx->fermat0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *square =
+ ( ( void * ) ctx->square0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( void * ) ctx->one0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *z =
+ ( ( void * ) ctx->z0 );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product =
+ ( ( void * ) ctx->product0 );
+ static const uint8_t two_raw[] = { 2 };
+ size_t zlen;
+
+ /* Initialise modulus N */
+ bigint_init ( modulus, curve->order, curve->keysize );
+ DBGC2 ( ctx, "ECDSA %p N = %s\n", ctx, bigint_ntoa ( modulus ) );
+
+ /* Calculate N-2 (using Montgomery constant as temporary buffer) */
+ bigint_copy ( modulus, fermat );
+ bigint_init ( square, two_raw, sizeof ( two_raw ) );
+ bigint_subtract ( square, fermat );
+
+ /* Calculate Montgomery constant */
+ bigint_reduce ( modulus, square );
+ DBGC2 ( ctx, "ECDSA %p R^2 = %s mod N\n",
+ ctx, bigint_ntoa ( square ) );
+
+ /* Construct one in Montgomery form */
+ bigint_grow ( square, product );
+ bigint_montgomery ( modulus, product, one );
+ DBGC2 ( ctx, "ECDSA %p R = %s mod N\n",
+ ctx, bigint_ntoa ( one ) );
+
+ /* Initialise digest */
+ ctx->digest = digest;
+ zlen = ctx->key.curve->keysize;
+ if ( zlen > digest->digestsize )
+ zlen = digest->digestsize;
+ ctx->zlen = zlen;
+ bigint_init ( z, value, zlen );
+ DBGC2 ( ctx, "ECDSA %p z = %s (%s)\n",
+ ctx, bigint_ntoa ( z ), digest->name );
+}
+
+/**
+ * Initialise ECDSA context
+ *
+ * @v ctx ECDSA context
+ * @v key Key
+ * @v digest Digest algorithm
+ * @v value Digest value
+ * @ret rc Return status code
+ */
+static int ecdsa_init ( struct ecdsa_context *ctx,
+ const struct asn1_cursor *key,
+ struct digest_algorithm *digest,
+ const void *value ) {
+ int rc;
+
+ /* Parse key */
+ if ( ( rc = ecdsa_parse_key ( &ctx->key, key ) ) != 0 )
+ goto err_parse;
+
+ /* Allocate dynamic storage */
+ if ( ( rc = ecdsa_alloc ( ctx ) ) != 0 )
+ goto err_alloc;
+
+ /* Initialise values */
+ ecdsa_init_values ( ctx, digest, value );
+
+ return 0;
+
+ ecdsa_free ( ctx );
+ err_alloc:
+ err_parse:
+ return rc;
+}
+
+/**
+ * Invert ECDSA value
+ *
+ * @v ctx ECDSA context
+ * @v val0 Element 0 of value to invert
+ */
+static void ecdsa_invert ( struct ecdsa_context *ctx,
+ bigint_element_t *val0 ) {
+ unsigned int size = ctx->size;
+ bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
+ ( ( void * ) ctx->modulus0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *fermat =
+ ( ( void * ) ctx->fermat0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *square =
+ ( ( void * ) ctx->square0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( void * ) ctx->one0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *temp =
+ ( ( void * ) ctx->temp0 );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product =
+ ( ( void * ) ctx->product0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *val =
+ ( ( void * ) val0 );
+
+ /* Convert value to Montgomery form */
+ bigint_multiply ( val, square, product );
+ bigint_montgomery ( modulus, product, temp );
+
+ /* Invert value via Fermat's little theorem */
+ bigint_copy ( one, val );
+ bigint_ladder ( val, temp, fermat, bigint_mod_exp_ladder, modulus,
+ product );
+}
+
+/**
+ * Generate ECDSA "r" and "s" values
+ *
+ * @v ctx ECDSA context
+ * @v sig Signature
+ * @ret rc Return status code
+ */
+static int ecdsa_sign_rs ( struct ecdsa_context *ctx ) {
+ struct digest_algorithm *digest = ctx->digest;
+ struct elliptic_curve *curve = ctx->key.curve;
+ size_t pointsize = curve->pointsize;
+ size_t keysize = curve->keysize;
+ unsigned int size = ctx->size;
+ bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
+ ( ( void * ) ctx->modulus0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *square =
+ ( ( void * ) ctx->square0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( void * ) ctx->one0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *z =
+ ( ( void * ) ctx->z0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *k =
+ ( ( void * ) ctx->k0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *r =
+ ( ( void * ) ctx->r0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *s =
+ ( ( void * ) ctx->s0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *temp =
+ ( ( void * ) ctx->temp0 );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product =
+ ( ( void * ) ctx->product0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *x1 =
+ ( ( void * ) temp );
+ void *point1 = ctx->point1;
+ void *scalar = ctx->scalar;
+ int rc;
+
+ /* Loop until a suitable signature is generated */
+ while ( 1 ) {
+
+ /* Generate pseudo-random data */
+ if ( ( rc = hmac_drbg_generate ( digest, ctx->drbg, NULL, 0,
+ scalar, keysize ) ) != 0 ) {
+ DBGC ( ctx, "ECDSA %p could not generate: %s\n",
+ ctx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check suitability of pseudo-random data */
+ bigint_init ( k, scalar, keysize );
+ DBGC2 ( ctx, "ECDSA %p k = %s\n",
+ ctx, bigint_ntoa ( k ) );
+ if ( bigint_is_zero ( k ) )
+ continue;
+ if ( bigint_is_geq ( k, modulus ) )
+ continue;
+
+ /* Calculate (x1,y1) = k*G */
+ elliptic_multiply ( curve, curve->base, scalar, point1 );
+ bigint_init ( x1, point1, ( pointsize / 2 ) );
+ DBGC2 ( ctx, "ECDSA %p x1 = %s mod N\n",
+ ctx, bigint_ntoa ( x1 ) );
+
+ /* Calculate r = x1 mod N */
+ bigint_multiply ( x1, one, product );
+ bigint_montgomery ( modulus, product, r );
+ DBGC2 ( ctx, "ECDSA %p r = %s\n",
+ ctx, bigint_ntoa ( r ) );
+
+ /* Check suitability of r */
+ if ( bigint_is_zero ( r ) )
+ continue;
+
+ /* Calculate k^-1 mod N (in Montgomery form) */
+ ecdsa_invert ( ctx, k->element );
+ DBGC2 ( ctx, "ECDSA %p (k^-1)R = %s mod N\n",
+ ctx, bigint_ntoa ( k ) );
+
+ /* Calculate r * dA */
+ bigint_init ( temp, ctx->key.private, keysize );
+ DBGC2 ( ctx, "ECDSA %p dA = %s\n",
+ ctx, bigint_ntoa ( temp ) );
+ bigint_multiply ( r, temp, product );
+ bigint_montgomery ( modulus, product, temp );
+ bigint_multiply ( temp, square, product );
+ bigint_montgomery ( modulus, product, temp );
+ DBGC2 ( ctx, "ECDSA %p r*dA = %s mod N\n",
+ ctx, bigint_ntoa ( temp ) );
+
+ /* Calculate k^-1 * (z + r*dA) */
+ bigint_add ( z, temp );
+ DBGC2 ( ctx, "ECDSA %p z+r*dA = %s mod N\n",
+ ctx, bigint_ntoa ( temp ) );
+ bigint_multiply ( k, temp, product );
+ bigint_montgomery ( modulus, product, s );
+ DBGC2 ( ctx, "ECDSA %p s = %s\n",
+ ctx, bigint_ntoa ( s ) );
+
+ /* Check suitability of s */
+ if ( bigint_is_zero ( s ) )
+ continue;
+
+ return 0;
+ }
+}
+
+/**
+ * Verify ECDSA "r" and "s" values
+ *
+ * @v ctx ECDSA context
+ * @v sig Signature
+ * @ret rc Return status code
+ */
+static int ecdsa_verify_rs ( struct ecdsa_context *ctx ) {
+ struct elliptic_curve *curve = ctx->key.curve;
+ size_t pointsize = curve->pointsize;
+ size_t keysize = curve->keysize;
+ const void *public = ctx->key.public;
+ unsigned int size = ctx->size;
+ bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
+ ( ( void * ) ctx->modulus0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( void * ) ctx->one0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *z =
+ ( ( void * ) ctx->z0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *r =
+ ( ( void * ) ctx->r0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *s =
+ ( ( void * ) ctx->s0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *temp =
+ ( ( void * ) ctx->temp0 );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product =
+ ( ( void * ) ctx->product0 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *u1 =
+ ( ( void * ) temp );
+ bigint_t ( size ) __attribute__ (( may_alias )) *u2 =
+ ( ( void * ) temp );
+ bigint_t ( size ) __attribute__ (( may_alias )) *x1 =
+ ( ( void * ) temp );
+ void *point1 = ctx->point1;
+ void *point2 = ctx->point2;
+ void *scalar = ctx->scalar;
+ int valid;
+ int rc;
+
+ DBGC2 ( ctx, "ECDSA %p r = %s\n", ctx, bigint_ntoa ( r ) );
+ DBGC2 ( ctx, "ECDSA %p s = %s\n", ctx, bigint_ntoa ( s ) );
+
+ /* Calculate s^-1 mod N (in Montgomery form) */
+ ecdsa_invert ( ctx, s->element );
+ DBGC2 ( ctx, "ECDSA %p (s^-1)R = %s mod N\n", ctx, bigint_ntoa ( s ) );
+
+ /* Calculate u1 = (z * s^-1) mod N */
+ bigint_multiply ( z, s, product );
+ bigint_montgomery ( modulus, product, u1 );
+ DBGC2 ( ctx, "ECDSA %p u1 = %s mod N\n",
+ ctx, bigint_ntoa ( u1 ) );
+ bigint_done ( u1, scalar, keysize );
+
+ /* Calculate u1 * G */
+ if ( ( rc = elliptic_multiply ( curve, curve->base, scalar,
+ point1 ) ) != 0 ) {
+ DBGC ( ctx, "ECDSA %p could not calculate u1*G: %s\n",
+ ctx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Calculate u2 = (r * s^-1) mod N */
+ bigint_multiply ( r, s, product );
+ bigint_montgomery ( modulus, product, u2 );
+ bigint_done ( u2, scalar, keysize );
+ DBGC2 ( ctx, "ECDSA %p u2 = %s mod N\n",
+ ctx, bigint_ntoa ( u2 ) );
+
+ /* Calculate u2 * Qa */
+ if ( ( rc = elliptic_multiply ( curve, public, scalar,
+ point2 ) ) != 0 ) {
+ DBGC ( ctx, "ECDSA %p could not calculate u2*Qa: %s\n",
+ ctx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Calculate u1 * G + u2 * Qa */
+ if ( ( rc = elliptic_add ( curve, point1, point2, point1 ) ) != 0 ) {
+ DBGC ( ctx, "ECDSA %p could not calculate u1*G+u2*Qa: %s\n",
+ ctx, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Check that result is not the point at infinity */
+ if ( elliptic_is_infinity ( curve, point1 ) ) {
+ DBGC ( ctx, "ECDSA %p result is point at infinity\n", ctx );
+ return -EINVAL;
+ }
+
+ /* Calculate x1 mod N */
+ bigint_init ( x1, point1, ( pointsize / 2 ) );
+ DBGC2 ( ctx, "ECDSA %p x1 = %s mod N\n", ctx, bigint_ntoa ( x1 ) );
+ bigint_multiply ( x1, one, product );
+ bigint_montgomery ( modulus, product, x1 );
+ DBGC2 ( ctx, "ECDSA %p x1 = %s\n", ctx, bigint_ntoa ( x1 ) );
+
+ /* Check signature */
+ bigint_subtract ( x1, r );
+ valid = bigint_is_zero ( r );
+ DBGC2 ( ctx, "ECDSA %p signature is%s valid\n",
+ ctx, ( valid ? "" : " not" ) );
+
+ return ( valid ? 0 : -EINVAL_SIGNATURE );
+}
+
+/**
+ * Encrypt using ECDSA
+ *
+ * @v key Key
+ * @v plaintext Plaintext
+ * @v ciphertext Ciphertext
+ * @ret rc Return status code
+ */
+static int ecdsa_encrypt ( const struct asn1_cursor *key __unused,
+ const struct asn1_cursor *plaintext __unused,
+ struct asn1_builder *ciphertext __unused ) {
+
+ /* Not a defined operation for ECDSA */
+ return -ENOTTY;
+}
+
+/**
+ * Decrypt using ECDSA
+ *
+ * @v key Key
+ * @v ciphertext Ciphertext
+ * @v plaintext Plaintext
+ * @ret rc Return status code
+ */
+static int ecdsa_decrypt ( const struct asn1_cursor *key __unused,
+ const struct asn1_cursor *ciphertext __unused,
+ struct asn1_builder *plaintext __unused ) {
+
+ /* Not a defined operation for ECDSA */
+ return -ENOTTY;
+}
+
+/**
+ * Sign digest value using ECDSA
+ *
+ * @v key Key
+ * @v digest Digest algorithm
+ * @v value Digest value
+ * @v signature Signature
+ * @ret rc Return status code
+ */
+static int ecdsa_sign ( const struct asn1_cursor *key,
+ struct digest_algorithm *digest, const void *value,
+ struct asn1_builder *signature ) {
+ struct ecdsa_context ctx;
+ int rc;
+
+ /* Initialise context */
+ if ( ( rc = ecdsa_init ( &ctx, key, digest, value ) ) != 0 )
+ goto err_init;
+
+ /* Fail unless we have a private key */
+ if ( ! ctx.key.private ) {
+ rc = -ENOTTY;
+ goto err_no_key;
+ }
+
+ /* Instantiate DRBG */
+ hmac_drbg_instantiate ( digest, ctx.drbg, ctx.key.private,
+ ctx.key.curve->keysize, value, ctx.zlen );
+
+ /* Create signature */
+ if ( ( rc = ecdsa_sign_rs ( &ctx ) ) != 0 )
+ goto err_signature;
+
+ /* Construct "r" and "s" values */
+ if ( ( rc = ecdsa_prepend_signature ( &ctx, ctx.s0, signature ) ) != 0)
+ goto err_s;
+ if ( ( rc = ecdsa_prepend_signature ( &ctx, ctx.r0, signature ) ) != 0)
+ goto err_r;
+ if ( ( rc = asn1_wrap ( signature, ASN1_SEQUENCE ) ) != 0 )
+ goto err_wrap;
+
+ /* Free context */
+ ecdsa_free ( &ctx );
+
+ return 0;
+
+ err_wrap:
+ err_r:
+ err_s:
+ err_signature:
+ err_no_key:
+ ecdsa_free ( &ctx );
+ err_init:
+ return rc;
+}
+
+/**
+ * Verify signed digest using ECDSA
+ *
+ * @v key Key
+ * @v digest Digest algorithm
+ * @v value Digest value
+ * @v signature Signature
+ * @ret rc Return status code
+ */
+static int ecdsa_verify ( const struct asn1_cursor *key,
+ struct digest_algorithm *digest, const void *value,
+ const struct asn1_cursor *signature ) {
+ struct ecdsa_context ctx;
+ struct asn1_cursor cursor;
+ int rc;
+
+ /* Initialise context */
+ if ( ( rc = ecdsa_init ( &ctx, key, digest, value ) ) != 0 )
+ goto err_init;
+
+ /* Enter sequence */
+ memcpy ( &cursor, signature, sizeof ( cursor ) );
+ asn1_enter ( &cursor, ASN1_SEQUENCE );
+
+ /* Extract "r" and "s" values */
+ if ( ( rc = ecdsa_parse_signature ( &ctx, ctx.r0, &cursor ) ) != 0 )
+ goto err_r;
+ asn1_skip_any ( &cursor );
+ if ( ( rc = ecdsa_parse_signature ( &ctx, ctx.s0, &cursor ) ) != 0 )
+ goto err_s;
+
+ /* Verify signature */
+ if ( ( rc = ecdsa_verify_rs ( &ctx ) ) != 0 )
+ goto err_verify;
+
+ /* Free context */
+ ecdsa_free ( &ctx );
+
+ return 0;
+
+ err_verify:
+ err_s:
+ err_r:
+ ecdsa_free ( &ctx );
+ err_init:
+ return rc;
+}
+
+/**
+ * Check for matching ECDSA public/private key pair
+ *
+ * @v private_key Private key
+ * @v public_key Public key
+ * @ret rc Return status code
+ */
+static int ecdsa_match ( const struct asn1_cursor *private_key,
+ const struct asn1_cursor *public_key ) {
+ struct elliptic_curve *curve;
+ struct ecdsa_key privkey;
+ struct ecdsa_key pubkey;
+ int rc;
+
+ /* Parse keys */
+ if ( ( rc = ecdsa_parse_key ( &privkey, private_key ) ) != 0 )
+ return rc;
+ if ( ( rc = ecdsa_parse_key ( &pubkey, public_key ) ) != 0 )
+ return rc;
+
+ /* Compare curves */
+ if ( privkey.curve != pubkey.curve )
+ return -ENOTTY;
+ curve = privkey.curve;
+
+ /* Compare public curve points */
+ if ( memcmp ( privkey.public, pubkey.public, curve->pointsize ) != 0 )
+ return -ENOTTY;
+
+ return 0;
+}
+
+/** ECDSA public-key algorithm */
+struct pubkey_algorithm ecdsa_algorithm = {
+ .name = "ecdsa",
+ .encrypt = ecdsa_encrypt,
+ .decrypt = ecdsa_decrypt,
+ .sign = ecdsa_sign,
+ .verify = ecdsa_verify,
+ .match = ecdsa_match,
+};
diff --git a/src/crypto/entropy.c b/src/crypto/entropy.c
index 419007159..ac0e92c42 100644
--- a/src/crypto/entropy.c
+++ b/src/crypto/entropy.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/gcm.c b/src/crypto/gcm.c
index b93925d07..b9c9d3a39 100644
--- a/src/crypto/gcm.c
+++ b/src/crypto/gcm.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/hash_df.c b/src/crypto/hash_df.c
index dc0dc0ce8..ec4bcaebc 100644
--- a/src/crypto/hash_df.c
+++ b/src/crypto/hash_df.c
@@ -34,6 +34,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/hmac.c b/src/crypto/hmac.c
index 7109bbf6a..ed4cefaad 100644
--- a/src/crypto/hmac.c
+++ b/src/crypto/hmac.c
@@ -34,6 +34,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
diff --git a/src/crypto/hmac_drbg.c b/src/crypto/hmac_drbg.c
index 57bde4d1d..bd831e239 100644
--- a/src/crypto/hmac_drbg.c
+++ b/src/crypto/hmac_drbg.c
@@ -34,6 +34,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/md4.c b/src/crypto/md4.c
index dcd86a428..a9184aa57 100644
--- a/src/crypto/md4.c
+++ b/src/crypto/md4.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/md5.c b/src/crypto/md5.c
index 5c62513e2..9418b006c 100644
--- a/src/crypto/md5.c
+++ b/src/crypto/md5.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/mishmash/cmd_md4.c b/src/crypto/mishmash/cmd_md4.c
new file mode 100644
index 000000000..390a533db
--- /dev/null
+++ b/src/crypto/mishmash/cmd_md4.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( FORBIDDEN );
+
+#include <ipxe/md4.h>
+#include <hci/digest_cmd.h>
+
+static int md4sum_exec ( int argc, char **argv ) {
+ return digest_exec ( argc, argv, &md4_algorithm );
+}
+
+COMMAND ( md4sum, md4sum_exec );
diff --git a/src/crypto/mishmash/cmd_sha224.c b/src/crypto/mishmash/cmd_sha224.c
new file mode 100644
index 000000000..fd8095937
--- /dev/null
+++ b/src/crypto/mishmash/cmd_sha224.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/sha256.h>
+#include <hci/digest_cmd.h>
+
+static int sha224sum_exec ( int argc, char **argv ) {
+ return digest_exec ( argc, argv, &sha224_algorithm );
+}
+
+COMMAND ( sha224sum, sha224sum_exec );
diff --git a/src/crypto/mishmash/cmd_sha256.c b/src/crypto/mishmash/cmd_sha256.c
new file mode 100644
index 000000000..259ae3eac
--- /dev/null
+++ b/src/crypto/mishmash/cmd_sha256.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/sha256.h>
+#include <hci/digest_cmd.h>
+
+static int sha256sum_exec ( int argc, char **argv ) {
+ return digest_exec ( argc, argv, &sha256_algorithm );
+}
+
+COMMAND ( sha256sum, sha256sum_exec );
diff --git a/src/crypto/mishmash/cmd_sha384.c b/src/crypto/mishmash/cmd_sha384.c
new file mode 100644
index 000000000..c31154d24
--- /dev/null
+++ b/src/crypto/mishmash/cmd_sha384.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/sha512.h>
+#include <hci/digest_cmd.h>
+
+static int sha384sum_exec ( int argc, char **argv ) {
+ return digest_exec ( argc, argv, &sha384_algorithm );
+}
+
+COMMAND ( sha384sum, sha384sum_exec );
diff --git a/src/crypto/mishmash/cmd_sha512.c b/src/crypto/mishmash/cmd_sha512.c
new file mode 100644
index 000000000..b6207f86d
--- /dev/null
+++ b/src/crypto/mishmash/cmd_sha512.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/sha512.h>
+#include <hci/digest_cmd.h>
+
+static int sha512sum_exec ( int argc, char **argv ) {
+ return digest_exec ( argc, argv, &sha512_algorithm );
+}
+
+COMMAND ( sha512sum, sha512sum_exec );
diff --git a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c
index 05e409f7a..ec2155001 100644
--- a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c
+++ b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha1.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c
index 6ce428642..4e6226e87 100644
--- a/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c
+++ b/src/crypto/mishmash/dhe_rsa_aes_cbc_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c
index dc5cad9f8..6bbe4d00d 100644
--- a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c
+++ b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c
index 0448255f3..336feb195 100644
--- a/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c
+++ b/src/crypto/mishmash/dhe_rsa_aes_gcm_sha384.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha1.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha1.c
new file mode 100644
index 000000000..0d9fcd15d
--- /dev/null
+++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha1.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <byteswap.h>
+#include <ipxe/ecdsa.h>
+#include <ipxe/aes.h>
+#include <ipxe/sha1.h>
+#include <ipxe/sha256.h>
+#include <ipxe/tls.h>
+
+/** TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA cipher suite */
+struct tls_cipher_suite
+tls_ecdhe_ecdsa_with_aes_128_cbc_sha __tls_cipher_suite ( 05 ) = {
+ .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ),
+ .key_len = ( 128 / 8 ),
+ .fixed_iv_len = 0,
+ .record_iv_len = AES_BLOCKSIZE,
+ .mac_len = SHA1_DIGEST_SIZE,
+ .exchange = &tls_ecdhe_exchange_algorithm,
+ .pubkey = &ecdsa_algorithm,
+ .cipher = &aes_cbc_algorithm,
+ .digest = &sha1_algorithm,
+ .handshake = &sha256_algorithm,
+};
+
+/** TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA cipher suite */
+struct tls_cipher_suite
+tls_ecdhe_ecdsa_with_aes_256_cbc_sha __tls_cipher_suite ( 06 ) = {
+ .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ),
+ .key_len = ( 256 / 8 ),
+ .fixed_iv_len = 0,
+ .record_iv_len = AES_BLOCKSIZE,
+ .mac_len = SHA1_DIGEST_SIZE,
+ .exchange = &tls_ecdhe_exchange_algorithm,
+ .pubkey = &ecdsa_algorithm,
+ .cipher = &aes_cbc_algorithm,
+ .digest = &sha1_algorithm,
+ .handshake = &sha256_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha256.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha256.c
new file mode 100644
index 000000000..4b7cf1620
--- /dev/null
+++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha256.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <byteswap.h>
+#include <ipxe/ecdsa.h>
+#include <ipxe/aes.h>
+#include <ipxe/sha256.h>
+#include <ipxe/tls.h>
+
+/** TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 cipher suite */
+struct tls_cipher_suite
+tls_ecdhe_ecdsa_with_aes_128_cbc_sha256 __tls_cipher_suite ( 03 ) = {
+ .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 ),
+ .key_len = ( 128 / 8 ),
+ .fixed_iv_len = 0,
+ .record_iv_len = AES_BLOCKSIZE,
+ .mac_len = SHA256_DIGEST_SIZE,
+ .exchange = &tls_ecdhe_exchange_algorithm,
+ .pubkey = &ecdsa_algorithm,
+ .cipher = &aes_cbc_algorithm,
+ .digest = &sha256_algorithm,
+ .handshake = &sha256_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha384.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha384.c
new file mode 100644
index 000000000..85373911a
--- /dev/null
+++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_cbc_sha384.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <byteswap.h>
+#include <ipxe/ecdsa.h>
+#include <ipxe/aes.h>
+#include <ipxe/sha512.h>
+#include <ipxe/tls.h>
+
+/** TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 cipher suite */
+struct tls_cipher_suite
+tls_ecdhe_ecdsa_with_aes_256_cbc_sha384 __tls_cipher_suite ( 04 ) = {
+ .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 ),
+ .key_len = ( 256 / 8 ),
+ .fixed_iv_len = 0,
+ .record_iv_len = AES_BLOCKSIZE,
+ .mac_len = SHA384_DIGEST_SIZE,
+ .exchange = &tls_ecdhe_exchange_algorithm,
+ .pubkey = &ecdsa_algorithm,
+ .cipher = &aes_cbc_algorithm,
+ .digest = &sha384_algorithm,
+ .handshake = &sha384_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha256.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha256.c
new file mode 100644
index 000000000..5aeb2f3d9
--- /dev/null
+++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha256.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <byteswap.h>
+#include <ipxe/ecdsa.h>
+#include <ipxe/aes.h>
+#include <ipxe/sha256.h>
+#include <ipxe/tls.h>
+
+/** TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 cipher suite */
+struct tls_cipher_suite
+tls_ecdhe_ecdsa_with_aes_128_gcm_sha256 __tls_cipher_suite ( 01 ) = {
+ .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ),
+ .key_len = ( 128 / 8 ),
+ .fixed_iv_len = 4,
+ .record_iv_len = 8,
+ .mac_len = 0,
+ .exchange = &tls_ecdhe_exchange_algorithm,
+ .pubkey = &ecdsa_algorithm,
+ .cipher = &aes_gcm_algorithm,
+ .digest = &sha256_algorithm,
+ .handshake = &sha256_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha384.c b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha384.c
new file mode 100644
index 000000000..3dc6149d7
--- /dev/null
+++ b/src/crypto/mishmash/ecdhe_ecdsa_aes_gcm_sha384.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <byteswap.h>
+#include <ipxe/ecdsa.h>
+#include <ipxe/aes.h>
+#include <ipxe/sha512.h>
+#include <ipxe/tls.h>
+
+/** TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 cipher suite */
+struct tls_cipher_suite
+tls_ecdhe_ecdsa_with_aes_256_gcm_sha384 __tls_cipher_suite ( 02 ) = {
+ .code = htons ( TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ),
+ .key_len = ( 256 / 8 ),
+ .fixed_iv_len = 4,
+ .record_iv_len = 8,
+ .mac_len = 0,
+ .exchange = &tls_ecdhe_exchange_algorithm,
+ .pubkey = &ecdsa_algorithm,
+ .cipher = &aes_gcm_algorithm,
+ .digest = &sha384_algorithm,
+ .handshake = &sha384_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c
index c23f65cc0..46b42ac1e 100644
--- a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c
+++ b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha1.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c
index 431e2e304..dd524ec78 100644
--- a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c
+++ b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c
index c52976809..7524d1ccc 100644
--- a/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c
+++ b/src/crypto/mishmash/ecdhe_rsa_aes_cbc_sha384.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c
index 4f4e38c69..978be2a4c 100644
--- a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c
+++ b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c
index 0bc7c305f..5ca6f0457 100644
--- a/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c
+++ b/src/crypto/mishmash/ecdhe_rsa_aes_gcm_sha384.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/ecdsa_sha224.c b/src/crypto/mishmash/ecdsa_sha224.c
new file mode 100644
index 000000000..92aa881cd
--- /dev/null
+++ b/src/crypto/mishmash/ecdsa_sha224.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/ecdsa.h>
+#include <ipxe/sha256.h>
+#include <ipxe/asn1.h>
+#include <ipxe/tls.h>
+
+/** "ecdsa-with-SHA224" object identifier */
+static uint8_t oid_ecdsa_with_sha224[] = { ASN1_OID_ECDSA_WITH_SHA224 };
+
+/** "ecdsa-with-SHA224" OID-identified algorithm */
+struct asn1_algorithm ecdsa_with_sha224_algorithm __asn1_algorithm = {
+ .name = "ecdsaWithSHA224",
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha224_algorithm,
+ .oid = ASN1_CURSOR ( oid_ecdsa_with_sha224 ),
+};
+
+/** ECDSA with SHA-224 signature hash algorithm */
+struct tls_signature_hash_algorithm
+tls_ecdsa_sha224 __tls_sig_hash_algorithm = {
+ .code = {
+ .signature = TLS_ECDSA_ALGORITHM,
+ .hash = TLS_SHA224_ALGORITHM,
+ },
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha224_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdsa_sha256.c b/src/crypto/mishmash/ecdsa_sha256.c
new file mode 100644
index 000000000..025d6ec73
--- /dev/null
+++ b/src/crypto/mishmash/ecdsa_sha256.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/ecdsa.h>
+#include <ipxe/sha256.h>
+#include <ipxe/asn1.h>
+#include <ipxe/tls.h>
+
+/** "ecdsa-with-SHA256" object identifier */
+static uint8_t oid_ecdsa_with_sha256[] = { ASN1_OID_ECDSA_WITH_SHA256 };
+
+/** "ecdsa-with-SHA256" OID-identified algorithm */
+struct asn1_algorithm ecdsa_with_sha256_algorithm __asn1_algorithm = {
+ .name = "ecdsaWithSHA256",
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha256_algorithm,
+ .oid = ASN1_CURSOR ( oid_ecdsa_with_sha256 ),
+};
+
+/** ECDSA with SHA-256 signature hash algorithm */
+struct tls_signature_hash_algorithm
+tls_ecdsa_sha256 __tls_sig_hash_algorithm = {
+ .code = {
+ .signature = TLS_ECDSA_ALGORITHM,
+ .hash = TLS_SHA256_ALGORITHM,
+ },
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha256_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdsa_sha384.c b/src/crypto/mishmash/ecdsa_sha384.c
new file mode 100644
index 000000000..d7a0ca5d6
--- /dev/null
+++ b/src/crypto/mishmash/ecdsa_sha384.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/ecdsa.h>
+#include <ipxe/sha512.h>
+#include <ipxe/asn1.h>
+#include <ipxe/tls.h>
+
+/** "ecdsa-with-SHA384" object identifier */
+static uint8_t oid_ecdsa_with_sha384[] = { ASN1_OID_ECDSA_WITH_SHA384 };
+
+/** "ecdsa-with-SHA384" OID-identified algorithm */
+struct asn1_algorithm ecdsa_with_sha384_algorithm __asn1_algorithm = {
+ .name = "ecdsaWithSHA384",
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha384_algorithm,
+ .oid = ASN1_CURSOR ( oid_ecdsa_with_sha384 ),
+};
+
+/** ECDSA with SHA-384 signature hash algorithm */
+struct tls_signature_hash_algorithm
+tls_ecdsa_sha384 __tls_sig_hash_algorithm = {
+ .code = {
+ .signature = TLS_ECDSA_ALGORITHM,
+ .hash = TLS_SHA384_ALGORITHM,
+ },
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha384_algorithm,
+};
diff --git a/src/crypto/mishmash/ecdsa_sha512.c b/src/crypto/mishmash/ecdsa_sha512.c
new file mode 100644
index 000000000..15391abf2
--- /dev/null
+++ b/src/crypto/mishmash/ecdsa_sha512.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/ecdsa.h>
+#include <ipxe/sha512.h>
+#include <ipxe/asn1.h>
+#include <ipxe/tls.h>
+
+/** "ecdsa-with-SHA512" object identifier */
+static uint8_t oid_ecdsa_with_sha512[] = { ASN1_OID_ECDSA_WITH_SHA512 };
+
+/** "ecdsa-with-SHA512" OID-identified algorithm */
+struct asn1_algorithm ecdsa_with_sha512_algorithm __asn1_algorithm = {
+ .name = "ecdsaWithSHA512",
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha512_algorithm,
+ .oid = ASN1_CURSOR ( oid_ecdsa_with_sha512 ),
+};
+
+/** ECDSA with SHA-512 signature hash algorithm */
+struct tls_signature_hash_algorithm
+tls_ecdsa_sha512 __tls_sig_hash_algorithm = {
+ .code = {
+ .signature = TLS_ECDSA_ALGORITHM,
+ .hash = TLS_SHA512_ALGORITHM,
+ },
+ .pubkey = &ecdsa_algorithm,
+ .digest = &sha512_algorithm,
+};
diff --git a/src/crypto/mishmash/oid_aes_cbc.c b/src/crypto/mishmash/oid_aes_cbc.c
new file mode 100644
index 000000000..d5b81541a
--- /dev/null
+++ b/src/crypto/mishmash/oid_aes_cbc.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/aes.h>
+#include <ipxe/asn1.h>
+
+/** "aes128-cbc" object identifier */
+static uint8_t oid_aes_128_cbc[] = { ASN1_OID_AES128_CBC };
+
+/** "aes192-cbc" object identifier */
+static uint8_t oid_aes_192_cbc[] = { ASN1_OID_AES192_CBC };
+
+/** "aes256-cbc" object identifier */
+static uint8_t oid_aes_256_cbc[] = { ASN1_OID_AES256_CBC };
+
+/** "aes128-cbc" OID-identified algorithm */
+struct asn1_algorithm aes_128_cbc_algorithm __asn1_algorithm = {
+ .name = "aes128-cbc",
+ .cipher = &aes_cbc_algorithm,
+ .oid = ASN1_CURSOR ( oid_aes_128_cbc ),
+ .parse = asn1_parse_cbc,
+};
+
+/** "aes192-cbc" OID-identified algorithm */
+struct asn1_algorithm aes_192_cbc_algorithm __asn1_algorithm = {
+ .name = "aes192-cbc",
+ .cipher = &aes_cbc_algorithm,
+ .oid = ASN1_CURSOR ( oid_aes_192_cbc ),
+ .parse = asn1_parse_cbc,
+};
+
+/** "aes256-cbc" OID-identified algorithm */
+struct asn1_algorithm aes_256_cbc_algorithm __asn1_algorithm = {
+ .name = "aes256-cbc",
+ .cipher = &aes_cbc_algorithm,
+ .oid = ASN1_CURSOR ( oid_aes_256_cbc ),
+ .parse = asn1_parse_cbc,
+};
diff --git a/src/crypto/mishmash/oid_aes_gcm.c b/src/crypto/mishmash/oid_aes_gcm.c
new file mode 100644
index 000000000..6be1a132d
--- /dev/null
+++ b/src/crypto/mishmash/oid_aes_gcm.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <ipxe/aes.h>
+#include <ipxe/asn1.h>
+
+/** "aes128-gcm" object identifier */
+static uint8_t oid_aes_128_gcm[] = { ASN1_OID_AES128_GCM };
+
+/** "aes192-gcm" object identifier */
+static uint8_t oid_aes_192_gcm[] = { ASN1_OID_AES192_GCM };
+
+/** "aes256-gcm" object identifier */
+static uint8_t oid_aes_256_gcm[] = { ASN1_OID_AES256_GCM };
+
+/** "aes128-gcm" OID-identified algorithm */
+struct asn1_algorithm aes_128_gcm_algorithm __asn1_algorithm = {
+ .name = "aes128-gcm",
+ .cipher = &aes_gcm_algorithm,
+ .oid = ASN1_CURSOR ( oid_aes_128_gcm ),
+ .parse = asn1_parse_gcm,
+};
+
+/** "aes192-gcm" OID-identified algorithm */
+struct asn1_algorithm aes_192_gcm_algorithm __asn1_algorithm = {
+ .name = "aes192-gcm",
+ .cipher = &aes_gcm_algorithm,
+ .oid = ASN1_CURSOR ( oid_aes_192_gcm ),
+ .parse = asn1_parse_gcm,
+};
+
+/** "aes256-gcm" OID-identified algorithm */
+struct asn1_algorithm aes_256_gcm_algorithm __asn1_algorithm = {
+ .name = "aes256-gcm",
+ .cipher = &aes_gcm_algorithm,
+ .oid = ASN1_CURSOR ( oid_aes_256_gcm ),
+ .parse = asn1_parse_gcm,
+};
diff --git a/src/crypto/mishmash/oid_md4.c b/src/crypto/mishmash/oid_md4.c
index d42f2df19..03b893d47 100644
--- a/src/crypto/mishmash/oid_md4.c
+++ b/src/crypto/mishmash/oid_md4.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( FORBIDDEN );
#include <ipxe/md4.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_md5.c b/src/crypto/mishmash/oid_md5.c
index f56dd8b8d..0095fbe0e 100644
--- a/src/crypto/mishmash/oid_md5.c
+++ b/src/crypto/mishmash/oid_md5.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( FORBIDDEN );
#include <ipxe/md5.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_p256.c b/src/crypto/mishmash/oid_p256.c
new file mode 100644
index 000000000..81ae1d11e
--- /dev/null
+++ b/src/crypto/mishmash/oid_p256.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <byteswap.h>
+#include <ipxe/p256.h>
+#include <ipxe/asn1.h>
+#include <ipxe/tls.h>
+
+/** "prime256v1" object identifier */
+static uint8_t oid_prime256v1[] = { ASN1_OID_PRIME256V1 };
+
+/** "prime256v1" OID-identified algorithm */
+struct asn1_algorithm prime256v1_algorithm __asn1_algorithm = {
+ .name = "prime256v1",
+ .curve = &p256_curve,
+ .oid = ASN1_CURSOR ( oid_prime256v1 ),
+};
+
+/** P-256 named curve */
+struct tls_named_curve tls_secp256r1_named_curve __tls_named_curve ( 01 ) = {
+ .curve = &p256_curve,
+ .code = htons ( TLS_NAMED_CURVE_SECP256R1 ),
+ .format = TLS_POINT_FORMAT_UNCOMPRESSED,
+ .pre_master_secret_len = P256_LEN,
+};
diff --git a/src/crypto/mishmash/oid_p384.c b/src/crypto/mishmash/oid_p384.c
new file mode 100644
index 000000000..a7d36aee4
--- /dev/null
+++ b/src/crypto/mishmash/oid_p384.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#include <byteswap.h>
+#include <ipxe/p384.h>
+#include <ipxe/asn1.h>
+#include <ipxe/tls.h>
+
+/** "secp384r1" object identifier */
+static uint8_t oid_secp384r1[] = { ASN1_OID_SECP384R1 };
+
+/** "secp384r1" OID-identified algorithm */
+struct asn1_algorithm secp384r1_algorithm __asn1_algorithm = {
+ .name = "secp384r1",
+ .curve = &p384_curve,
+ .oid = ASN1_CURSOR ( oid_secp384r1 ),
+};
+
+/** P-384 named curve */
+struct tls_named_curve tls_secp384r1_named_curve __tls_named_curve ( 01 ) = {
+ .curve = &p384_curve,
+ .code = htons ( TLS_NAMED_CURVE_SECP384R1 ),
+ .format = TLS_POINT_FORMAT_UNCOMPRESSED,
+ .pre_master_secret_len = P384_LEN,
+};
diff --git a/src/crypto/mishmash/oid_rsa.c b/src/crypto/mishmash/oid_rsa.c
index 582022628..02bb59edb 100644
--- a/src/crypto/mishmash/oid_rsa.c
+++ b/src/crypto/mishmash/oid_rsa.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rsa.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_sha1.c b/src/crypto/mishmash/oid_sha1.c
index 5dae6d27c..5ddd2aba8 100644
--- a/src/crypto/mishmash/oid_sha1.c
+++ b/src/crypto/mishmash/oid_sha1.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/sha1.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_sha224.c b/src/crypto/mishmash/oid_sha224.c
index ee7ed22e4..6658bda56 100644
--- a/src/crypto/mishmash/oid_sha224.c
+++ b/src/crypto/mishmash/oid_sha224.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/sha256.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_sha256.c b/src/crypto/mishmash/oid_sha256.c
index 963fddb63..8da40a70b 100644
--- a/src/crypto/mishmash/oid_sha256.c
+++ b/src/crypto/mishmash/oid_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/sha256.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_sha384.c b/src/crypto/mishmash/oid_sha384.c
index 81ff48bbf..57c1ab53b 100644
--- a/src/crypto/mishmash/oid_sha384.c
+++ b/src/crypto/mishmash/oid_sha384.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/sha512.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_sha512.c b/src/crypto/mishmash/oid_sha512.c
index 78bae48b4..73d7cb78f 100644
--- a/src/crypto/mishmash/oid_sha512.c
+++ b/src/crypto/mishmash/oid_sha512.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/sha512.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_sha512_224.c b/src/crypto/mishmash/oid_sha512_224.c
index 6f61f9cac..a6291097b 100644
--- a/src/crypto/mishmash/oid_sha512_224.c
+++ b/src/crypto/mishmash/oid_sha512_224.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/sha512.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_sha512_256.c b/src/crypto/mishmash/oid_sha512_256.c
index bce4762e4..d36199372 100644
--- a/src/crypto/mishmash/oid_sha512_256.c
+++ b/src/crypto/mishmash/oid_sha512_256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/sha512.h>
#include <ipxe/asn1.h>
diff --git a/src/crypto/mishmash/oid_x25519.c b/src/crypto/mishmash/oid_x25519.c
index 2f8aa065b..2907eb461 100644
--- a/src/crypto/mishmash/oid_x25519.c
+++ b/src/crypto/mishmash/oid_x25519.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/x25519.h>
@@ -42,4 +43,5 @@ struct asn1_algorithm x25519_algorithm __asn1_algorithm = {
struct tls_named_curve tls_x25519_named_curve __tls_named_curve ( 01 ) = {
.curve = &x25519_curve,
.code = htons ( TLS_NAMED_CURVE_X25519 ),
+ .pre_master_secret_len = sizeof ( struct x25519_value ),
};
diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/src/crypto/mishmash/rsa_aes_cbc_sha1.c
index 0862fb5ac..35f5f6eb7 100644
--- a/src/crypto/mishmash/rsa_aes_cbc_sha1.c
+++ b/src/crypto/mishmash/rsa_aes_cbc_sha1.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/src/crypto/mishmash/rsa_aes_cbc_sha256.c
index e5928db82..22705df7e 100644
--- a/src/crypto/mishmash/rsa_aes_cbc_sha256.c
+++ b/src/crypto/mishmash/rsa_aes_cbc_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/rsa_aes_gcm_sha256.c b/src/crypto/mishmash/rsa_aes_gcm_sha256.c
index b18bbd844..d3fd00f1e 100644
--- a/src/crypto/mishmash/rsa_aes_gcm_sha256.c
+++ b/src/crypto/mishmash/rsa_aes_gcm_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/rsa_aes_gcm_sha384.c b/src/crypto/mishmash/rsa_aes_gcm_sha384.c
index 06558aaed..908db086a 100644
--- a/src/crypto/mishmash/rsa_aes_gcm_sha384.c
+++ b/src/crypto/mishmash/rsa_aes_gcm_sha384.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <byteswap.h>
#include <ipxe/rsa.h>
diff --git a/src/crypto/mishmash/rsa_md5.c b/src/crypto/mishmash/rsa_md5.c
index 051afe264..00808c23f 100644
--- a/src/crypto/mishmash/rsa_md5.c
+++ b/src/crypto/mishmash/rsa_md5.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( FORBIDDEN );
#include <ipxe/rsa.h>
#include <ipxe/md5.h>
diff --git a/src/crypto/mishmash/rsa_sha1.c b/src/crypto/mishmash/rsa_sha1.c
index 264f871f1..8907ac08a 100644
--- a/src/crypto/mishmash/rsa_sha1.c
+++ b/src/crypto/mishmash/rsa_sha1.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rsa.h>
#include <ipxe/sha1.h>
diff --git a/src/crypto/mishmash/rsa_sha224.c b/src/crypto/mishmash/rsa_sha224.c
index 1465a033d..b676d41f3 100644
--- a/src/crypto/mishmash/rsa_sha224.c
+++ b/src/crypto/mishmash/rsa_sha224.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rsa.h>
#include <ipxe/sha256.h>
diff --git a/src/crypto/mishmash/rsa_sha256.c b/src/crypto/mishmash/rsa_sha256.c
index 7283c3e29..8a6a7a5cf 100644
--- a/src/crypto/mishmash/rsa_sha256.c
+++ b/src/crypto/mishmash/rsa_sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rsa.h>
#include <ipxe/sha256.h>
diff --git a/src/crypto/mishmash/rsa_sha384.c b/src/crypto/mishmash/rsa_sha384.c
index 6f8c29b29..cc1878bd4 100644
--- a/src/crypto/mishmash/rsa_sha384.c
+++ b/src/crypto/mishmash/rsa_sha384.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rsa.h>
#include <ipxe/sha512.h>
diff --git a/src/crypto/mishmash/rsa_sha512.c b/src/crypto/mishmash/rsa_sha512.c
index bb4463a5a..9c995e1c8 100644
--- a/src/crypto/mishmash/rsa_sha512.c
+++ b/src/crypto/mishmash/rsa_sha512.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <ipxe/rsa.h>
#include <ipxe/sha512.h>
diff --git a/src/crypto/ntlm.c b/src/crypto/ntlm.c
index fb120f8db..f9ce51bde 100644
--- a/src/crypto/ntlm.c
+++ b/src/crypto/ntlm.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/ocsp.c b/src/crypto/ocsp.c
index cc957b40c..5d6acb605 100644
--- a/src/crypto/ocsp.c
+++ b/src/crypto/ocsp.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
@@ -158,8 +159,8 @@ static int ocsp_request ( struct ocsp_check *ocsp ) {
digest_final ( digest, digest_ctx, name_digest );
digest_init ( digest, digest_ctx );
digest_update ( digest, digest_ctx,
- ocsp->issuer->subject.public_key.raw_bits.data,
- ocsp->issuer->subject.public_key.raw_bits.len );
+ ocsp->issuer->subject.public_key.value.data,
+ ocsp->issuer->subject.public_key.value.len );
digest_final ( digest, digest_ctx, pubkey_digest );
/* Construct request */
@@ -422,8 +423,8 @@ static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp,
/* Generate SHA1 hash of certificate's public key */
digest_init ( &sha1_algorithm, ctx );
digest_update ( &sha1_algorithm, ctx,
- cert->subject.public_key.raw_bits.data,
- cert->subject.public_key.raw_bits.len );
+ cert->subject.public_key.value.data,
+ cert->subject.public_key.value.len );
digest_final ( &sha1_algorithm, ctx, digest );
/* Compare responder key hash with hash of certificate's public key */
@@ -701,7 +702,7 @@ static int ocsp_parse_basic_response ( struct ocsp_check *ocsp,
const struct asn1_cursor *raw ) {
struct ocsp_response *response = &ocsp->response;
struct asn1_algorithm **algorithm = &response->algorithm;
- struct asn1_bit_string *signature = &response->signature;
+ struct asn1_cursor *signature = &response->signature;
struct asn1_cursor cursor;
int rc;
@@ -726,7 +727,8 @@ static int ocsp_parse_basic_response ( struct ocsp_check *ocsp,
asn1_skip_any ( &cursor );
/* Parse signature */
- if ( ( rc = asn1_integral_bit_string ( &cursor, signature ) ) != 0 ) {
+ memcpy ( signature, &cursor, sizeof ( *signature ) );
+ if ( ( rc = asn1_enter_bits ( signature, NULL ) ) != 0 ) {
DBGC ( ocsp, "OCSP %p \"%s\" cannot parse signature: %s\n",
ocsp, x509_name ( ocsp->cert ), strerror ( rc ) );
return rc;
@@ -844,10 +846,9 @@ static int ocsp_check_signature ( struct ocsp_check *ocsp,
struct ocsp_response *response = &ocsp->response;
struct digest_algorithm *digest = response->algorithm->digest;
struct pubkey_algorithm *pubkey = response->algorithm->pubkey;
- struct x509_public_key *public_key = &signer->subject.public_key;
+ struct asn1_cursor *key = &signer->subject.public_key.raw;
uint8_t digest_ctx[ digest->ctxsize ];
uint8_t digest_out[ digest->digestsize ];
- uint8_t pubkey_ctx[ pubkey->ctxsize ];
int rc;
/* Generate digest */
@@ -856,30 +857,17 @@ static int ocsp_check_signature ( struct ocsp_check *ocsp,
response->tbs.len );
digest_final ( digest, digest_ctx, digest_out );
- /* Initialise public-key algorithm */
- if ( ( rc = pubkey_init ( pubkey, pubkey_ctx, public_key->raw.data,
- public_key->raw.len ) ) != 0 ) {
- DBGC ( ocsp, "OCSP %p \"%s\" could not initialise public key: "
- "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
- goto err_init;
- }
-
/* Verify digest */
- if ( ( rc = pubkey_verify ( pubkey, pubkey_ctx, digest, digest_out,
- response->signature.data,
- response->signature.len ) ) != 0 ) {
+ if ( ( rc = pubkey_verify ( pubkey, key, digest, digest_out,
+ &response->signature ) ) != 0 ) {
DBGC ( ocsp, "OCSP %p \"%s\" signature verification failed: "
"%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ));
- goto err_verify;
+ return rc;
}
DBGC2 ( ocsp, "OCSP %p \"%s\" signature is correct\n",
ocsp, x509_name ( ocsp->cert ) );
-
- err_verify:
- pubkey_final ( pubkey, pubkey_ctx );
- err_init:
- return rc;
+ return 0;
}
/**
diff --git a/src/crypto/p256.c b/src/crypto/p256.c
new file mode 100644
index 000000000..a513555b3
--- /dev/null
+++ b/src/crypto/p256.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * NIST P-256 elliptic curve
+ *
+ */
+
+#include <ipxe/p256.h>
+
+/** P-256 field prime */
+static const uint8_t p256_prime[P256_LEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/** P-256 constant "a" */
+static const uint8_t p256_a[P256_LEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc
+};
+
+/** P-256 constant "b" */
+static const uint8_t p256_b[P256_LEN] = {
+ 0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7, 0xb3, 0xeb, 0xbd,
+ 0x55, 0x76, 0x98, 0x86, 0xbc, 0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53,
+ 0xb0, 0xf6, 0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b
+};
+
+/** P-256 base point */
+static const uint8_t p256_base[ P256_LEN * 2 ] = {
+ 0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47, 0xf8, 0xbc, 0xe6,
+ 0xe5, 0x63, 0xa4, 0x40, 0xf2, 0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb,
+ 0x33, 0xa0, 0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96, 0x4f,
+ 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b, 0x8e, 0xe7, 0xeb, 0x4a,
+ 0x7c, 0x0f, 0x9e, 0x16, 0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e,
+ 0xce, 0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5
+};
+
+/** P-256 group order */
+static const uint8_t p256_order[P256_LEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
+ 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
+};
+
+/** P-256 elliptic curve */
+WEIERSTRASS_CURVE ( p256, p256_curve, P256_LEN,
+ p256_prime, p256_a, p256_b, p256_base, p256_order );
diff --git a/src/crypto/p384.c b/src/crypto/p384.c
new file mode 100644
index 000000000..bdd23d460
--- /dev/null
+++ b/src/crypto/p384.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * NIST P-384 elliptic curve
+ *
+ */
+
+#include <ipxe/p384.h>
+
+/** P-384 field prime */
+static const uint8_t p384_prime[P384_LEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff
+};
+
+/** P-384 constant "a" */
+static const uint8_t p384_a[P384_LEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xfc
+};
+
+/** P-384 constant "b" */
+static const uint8_t p384_b[P384_LEN] = {
+ 0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4, 0x98, 0x8e, 0x05,
+ 0x6b, 0xe3, 0xf8, 0x2d, 0x19, 0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81,
+ 0x41, 0x12, 0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a, 0xc6,
+ 0x56, 0x39, 0x8d, 0x8a, 0x2e, 0xd1, 0x9d, 0x2a, 0x85, 0xc8, 0xed,
+ 0xd3, 0xec, 0x2a, 0xef
+};
+
+/** P-384 base point */
+static const uint8_t p384_base[ P384_LEN * 2 ] = {
+ 0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37, 0x8e, 0xb1, 0xc7,
+ 0x1e, 0xf3, 0x20, 0xad, 0x74, 0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7,
+ 0x9b, 0x98, 0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38, 0x55,
+ 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c, 0x3a, 0x54, 0x5e, 0x38,
+ 0x72, 0x76, 0x0a, 0xb7, 0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c,
+ 0x6f, 0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29, 0xf8, 0xf4,
+ 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c, 0xe9, 0xda, 0x31, 0x13, 0xb5,
+ 0xf0, 0xb8, 0xc0, 0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d,
+ 0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f
+};
+
+/** P-384 group order */
+static const uint8_t p384_order[P384_LEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf, 0x58,
+ 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a, 0xec, 0xec, 0x19, 0x6a,
+ 0xcc, 0xc5, 0x29, 0x73
+};
+
+/** P-384 elliptic curve */
+WEIERSTRASS_CURVE ( p384, p384_curve, P384_LEN,
+ p384_prime, p384_a, p384_b, p384_base, p384_order );
diff --git a/src/crypto/privkey.c b/src/crypto/privkey.c
index c15edf130..c67a4400b 100644
--- a/src/crypto/privkey.c
+++ b/src/crypto/privkey.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
@@ -53,7 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/* Raw private key data */
extern char private_key_data[];
-extern char private_key_len[];
+extern size_t ABS_SYMBOL ( private_key_len );
__asm__ ( ".section \".rodata\", \"a\", " PROGBITS "\n\t"
"\nprivate_key_data:\n\t"
#ifdef PRIVATE_KEY
@@ -68,7 +69,7 @@ struct private_key private_key = {
.refcnt = REF_INIT ( ref_no_free ),
.builder = {
.data = private_key_data,
- .len = ( ( size_t ) private_key_len ),
+ .len = ABS_VALUE_INIT ( private_key_len ),
},
};
diff --git a/src/crypto/random_nz.c b/src/crypto/random_nz.c
index 5fe576e05..96b12359c 100644
--- a/src/crypto/random_nz.c
+++ b/src/crypto/random_nz.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/rbg.c b/src/crypto/rbg.c
index 4b45b3474..17914542e 100644
--- a/src/crypto/rbg.c
+++ b/src/crypto/rbg.c
@@ -34,6 +34,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -75,6 +76,9 @@ static int rbg_startup ( void ) {
int len;
int rc;
+ /* Record that startup has been attempted (even if unsuccessful) */
+ rbg.started = 1;
+
/* Try to obtain system UUID for use as personalisation
* string, in accordance with ANS X9.82 Part 3-2007 Section
* 8.5.2. If no UUID is available, proceed without a
@@ -98,6 +102,33 @@ static int rbg_startup ( void ) {
}
/**
+ * Generate bits using RBG
+ *
+ * @v additional Additional input
+ * @v additional_len Length of additional input
+ * @v prediction_resist Prediction resistance is required
+ * @v data Output buffer
+ * @v len Length of output buffer
+ * @ret rc Return status code
+ *
+ * This is the RBG_Generate function defined in ANS X9.82 Part 4
+ * (April 2011 Draft) Section 9.1.2.2.
+ */
+int rbg_generate ( const void *additional, size_t additional_len,
+ int prediction_resist, void *data, size_t len ) {
+
+ /* Attempt startup, if not already attempted */
+ if ( ! rbg.started )
+ rbg_startup();
+
+ /* Generate bits. The DRBG will itself return an error if it
+ * is not valid (e.g. due to an instantiation failure).
+ */
+ return drbg_generate ( &rbg.state, additional, additional_len,
+ prediction_resist, data, len );
+}
+
+/**
* Shut down RBG
*
*/
@@ -105,16 +136,21 @@ static void rbg_shutdown ( void ) {
/* Uninstantiate DRBG */
drbg_uninstantiate ( &rbg.state );
+
+ /* Clear startup attempted flag */
+ rbg.started = 0;
}
/** RBG startup function */
static void rbg_startup_fn ( void ) {
- /* Start up RBG. There is no way to report an error at this
- * stage, but a failed startup will result in an invalid DRBG
- * that refuses to generate bits.
+ /* Start up RBG (if not already started on demand). There is
+ * no way to report an error at this stage, but a failed
+ * startup will result in an invalid DRBG that refuses to
+ * generate bits.
*/
- rbg_startup();
+ if ( ! rbg.started )
+ rbg_startup();
}
/** RBG shutdown function */
diff --git a/src/crypto/rootcert.c b/src/crypto/rootcert.c
index 0835ff071..6eb08256a 100644
--- a/src/crypto/rootcert.c
+++ b/src/crypto/rootcert.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <ipxe/crypto.h>
@@ -42,10 +43,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define FINGERPRINT_LEN SHA256_DIGEST_SIZE
/* Allow trusted certificates to be overridden if not explicitly specified */
-#ifdef TRUSTED
-#define ALLOW_TRUST_OVERRIDE 0
-#else
-#define ALLOW_TRUST_OVERRIDE 1
+#ifndef ALLOW_TRUST_OVERRIDE
+ #ifdef TRUSTED
+ #define ALLOW_TRUST_OVERRIDE 0
+ #else
+ #define ALLOW_TRUST_OVERRIDE 1
+ #endif
#endif
/* Use iPXE root CA if no trusted certificates are explicitly specified */
@@ -58,6 +61,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
0xed, 0x1a,
#endif
+/** Flag indicating if root of trust may be overridden at runtime */
+const int allow_trust_override = ALLOW_TRUST_OVERRIDE;
+
/** Root certificate fingerprints */
static const uint8_t fingerprints[] = { TRUSTED };
diff --git a/src/crypto/rsa.c b/src/crypto/rsa.c
index 16c67d822..be055d881 100644
--- a/src/crypto/rsa.c
+++ b/src/crypto/rsa.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <stdlib.h>
@@ -47,6 +48,28 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define EINFO_EACCES_VERIFY \
__einfo_uniqify ( EINFO_EACCES, 0x01, "RSA signature incorrect" )
+/** An RSA context */
+struct rsa_context {
+ /** Allocated memory */
+ void *dynamic;
+ /** Modulus */
+ bigint_element_t *modulus0;
+ /** Modulus size */
+ unsigned int size;
+ /** Modulus length */
+ size_t max_len;
+ /** Exponent */
+ bigint_element_t *exponent0;
+ /** Exponent size */
+ unsigned int exponent_size;
+ /** Input buffer */
+ bigint_element_t *input0;
+ /** Output buffer */
+ bigint_element_t *output0;
+ /** Temporary working space for modular exponentiation */
+ void *tmp;
+};
+
/**
* Identify RSA prefix
*
@@ -69,10 +92,9 @@ rsa_find_prefix ( struct digest_algorithm *digest ) {
*
* @v context RSA context
*/
-static void rsa_free ( struct rsa_context *context ) {
+static inline void rsa_free ( struct rsa_context *context ) {
free ( context->dynamic );
- context->dynamic = NULL;
}
/**
@@ -88,8 +110,7 @@ static int rsa_alloc ( struct rsa_context *context, size_t modulus_len,
unsigned int size = bigint_required_size ( modulus_len );
unsigned int exponent_size = bigint_required_size ( exponent_len );
bigint_t ( size ) *modulus;
- bigint_t ( exponent_size ) *exponent;
- size_t tmp_len = bigint_mod_exp_tmp_len ( modulus, exponent );
+ size_t tmp_len = bigint_mod_exp_tmp_len ( modulus );
struct {
bigint_t ( size ) modulus;
bigint_t ( exponent_size ) exponent;
@@ -98,9 +119,6 @@ static int rsa_alloc ( struct rsa_context *context, size_t modulus_len,
uint8_t tmp[tmp_len];
} __attribute__ (( packed )) *dynamic;
- /* Free any existing dynamic storage */
- rsa_free ( context );
-
/* Allocate dynamic storage */
dynamic = malloc ( sizeof ( *dynamic ) );
if ( ! dynamic )
@@ -121,34 +139,6 @@ static int rsa_alloc ( struct rsa_context *context, size_t modulus_len,
}
/**
- * Parse RSA integer
- *
- * @v integer Integer to fill in
- * @v raw ASN.1 cursor
- * @ret rc Return status code
- */
-static int rsa_parse_integer ( struct asn1_cursor *integer,
- const struct asn1_cursor *raw ) {
-
- /* Enter integer */
- memcpy ( integer, raw, sizeof ( *integer ) );
- asn1_enter ( integer, ASN1_INTEGER );
-
- /* Skip initial sign byte if applicable */
- if ( ( integer->len > 1 ) &&
- ( *( ( uint8_t * ) integer->data ) == 0x00 ) ) {
- integer->data++;
- integer->len--;
- }
-
- /* Fail if cursor or integer are invalid */
- if ( ! integer->len )
- return -EINVAL;
-
- return 0;
-}
-
-/**
* Parse RSA modulus and exponent
*
* @v modulus Modulus to fill in
@@ -159,7 +149,6 @@ static int rsa_parse_integer ( struct asn1_cursor *integer,
static int rsa_parse_mod_exp ( struct asn1_cursor *modulus,
struct asn1_cursor *exponent,
const struct asn1_cursor *raw ) {
- struct asn1_bit_string bits;
struct asn1_cursor cursor;
int is_private;
int rc;
@@ -178,8 +167,8 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus,
asn1_skip_any ( &cursor );
/* Enter privateKey, if present */
- if ( asn1_check_algorithm ( &cursor,
- &rsa_encryption_algorithm ) == 0 ) {
+ if ( asn1_check_algorithm ( &cursor, &rsa_encryption_algorithm,
+ NULL ) == 0 ) {
/* Skip privateKeyAlgorithm */
asn1_skip_any ( &cursor );
@@ -203,17 +192,15 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus,
asn1_skip ( &cursor, ASN1_SEQUENCE );
/* Enter subjectPublicKey */
- if ( ( rc = asn1_integral_bit_string ( &cursor, &bits ) ) != 0 )
- return rc;
- cursor.data = bits.data;
- cursor.len = bits.len;
+ asn1_enter_bits ( &cursor, NULL );
/* Enter RSAPublicKey */
asn1_enter ( &cursor, ASN1_SEQUENCE );
}
/* Extract modulus */
- if ( ( rc = rsa_parse_integer ( modulus, &cursor ) ) != 0 )
+ memcpy ( modulus, &cursor, sizeof ( *modulus ) );
+ if ( ( rc = asn1_enter_unsigned ( modulus ) ) != 0 )
return rc;
asn1_skip_any ( &cursor );
@@ -222,7 +209,8 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus,
asn1_skip ( &cursor, ASN1_INTEGER );
/* Extract publicExponent/privateExponent */
- if ( ( rc = rsa_parse_integer ( exponent, &cursor ) ) != 0 )
+ memcpy ( exponent, &cursor, sizeof ( *exponent ) );
+ if ( ( rc = asn1_enter_unsigned ( exponent ) ) != 0 )
return rc;
return 0;
@@ -231,29 +219,23 @@ static int rsa_parse_mod_exp ( struct asn1_cursor *modulus,
/**
* Initialise RSA cipher
*
- * @v ctx RSA context
+ * @v context RSA context
* @v key Key
- * @v key_len Length of key
* @ret rc Return status code
*/
-static int rsa_init ( void *ctx, const void *key, size_t key_len ) {
- struct rsa_context *context = ctx;
+static int rsa_init ( struct rsa_context *context,
+ const struct asn1_cursor *key ) {
struct asn1_cursor modulus;
struct asn1_cursor exponent;
- struct asn1_cursor cursor;
int rc;
/* Initialise context */
memset ( context, 0, sizeof ( *context ) );
- /* Initialise cursor */
- cursor.data = key;
- cursor.len = key_len;
-
/* Parse modulus and exponent */
- if ( ( rc = rsa_parse_mod_exp ( &modulus, &exponent, &cursor ) ) != 0 ){
+ if ( ( rc = rsa_parse_mod_exp ( &modulus, &exponent, key ) ) != 0 ){
DBGC ( context, "RSA %p invalid modulus/exponent:\n", context );
- DBGC_HDA ( context, 0, cursor.data, cursor.len );
+ DBGC_HDA ( context, 0, key->data, key->len );
goto err_parse;
}
@@ -281,18 +263,6 @@ static int rsa_init ( void *ctx, const void *key, size_t key_len ) {
}
/**
- * Calculate RSA maximum output length
- *
- * @v ctx RSA context
- * @ret max_len Maximum output length
- */
-static size_t rsa_max_len ( void *ctx ) {
- struct rsa_context *context = ctx;
-
- return context->max_len;
-}
-
-/**
* Perform RSA cipher operation
*
* @v context RSA context
@@ -320,111 +290,158 @@ static void rsa_cipher ( struct rsa_context *context,
/**
* Encrypt using RSA
*
- * @v ctx RSA context
+ * @v key Key
* @v plaintext Plaintext
- * @v plaintext_len Length of plaintext
* @v ciphertext Ciphertext
* @ret ciphertext_len Length of ciphertext, or negative error
*/
-static int rsa_encrypt ( void *ctx, const void *plaintext,
- size_t plaintext_len, void *ciphertext ) {
- struct rsa_context *context = ctx;
+static int rsa_encrypt ( const struct asn1_cursor *key,
+ const struct asn1_cursor *plaintext,
+ struct asn1_builder *ciphertext ) {
+ struct rsa_context context;
void *temp;
uint8_t *encoded;
- size_t max_len = ( context->max_len - 11 );
- size_t random_nz_len = ( max_len - plaintext_len + 8 );
+ size_t max_len;
+ size_t random_nz_len;
int rc;
+ DBGC ( &context, "RSA %p encrypting:\n", &context );
+ DBGC_HDA ( &context, 0, plaintext->data, plaintext->len );
+
+ /* Initialise context */
+ if ( ( rc = rsa_init ( &context, key ) ) != 0 )
+ goto err_init;
+
+ /* Calculate lengths */
+ max_len = ( context.max_len - 11 );
+ random_nz_len = ( max_len - plaintext->len + 8 );
+
/* Sanity check */
- if ( plaintext_len > max_len ) {
- DBGC ( context, "RSA %p plaintext too long (%zd bytes, max "
- "%zd)\n", context, plaintext_len, max_len );
- return -ERANGE;
+ if ( plaintext->len > max_len ) {
+ DBGC ( &context, "RSA %p plaintext too long (%zd bytes, max "
+ "%zd)\n", &context, plaintext->len, max_len );
+ rc = -ERANGE;
+ goto err_sanity;
}
- DBGC ( context, "RSA %p encrypting:\n", context );
- DBGC_HDA ( context, 0, plaintext, plaintext_len );
/* Construct encoded message (using the big integer output
* buffer as temporary storage)
*/
- temp = context->output0;
+ temp = context.output0;
encoded = temp;
encoded[0] = 0x00;
encoded[1] = 0x02;
if ( ( rc = get_random_nz ( &encoded[2], random_nz_len ) ) != 0 ) {
- DBGC ( context, "RSA %p could not generate random data: %s\n",
- context, strerror ( rc ) );
- return rc;
+ DBGC ( &context, "RSA %p could not generate random data: %s\n",
+ &context, strerror ( rc ) );
+ goto err_random;
}
encoded[ 2 + random_nz_len ] = 0x00;
- memcpy ( &encoded[ context->max_len - plaintext_len ],
- plaintext, plaintext_len );
+ memcpy ( &encoded[ context.max_len - plaintext->len ],
+ plaintext->data, plaintext->len );
+
+ /* Create space for ciphertext */
+ if ( ( rc = asn1_grow ( ciphertext, context.max_len ) ) != 0 )
+ goto err_grow;
/* Encipher the encoded message */
- rsa_cipher ( context, encoded, ciphertext );
- DBGC ( context, "RSA %p encrypted:\n", context );
- DBGC_HDA ( context, 0, ciphertext, context->max_len );
+ rsa_cipher ( &context, encoded, ciphertext->data );
+ DBGC ( &context, "RSA %p encrypted:\n", &context );
+ DBGC_HDA ( &context, 0, ciphertext->data, context.max_len );
+
+ /* Free context */
+ rsa_free ( &context );
- return context->max_len;
+ return 0;
+
+ err_grow:
+ err_random:
+ err_sanity:
+ rsa_free ( &context );
+ err_init:
+ return rc;
}
/**
* Decrypt using RSA
*
- * @v ctx RSA context
+ * @v key Key
* @v ciphertext Ciphertext
- * @v ciphertext_len Ciphertext length
* @v plaintext Plaintext
- * @ret plaintext_len Plaintext length, or negative error
+ * @ret rc Return status code
*/
-static int rsa_decrypt ( void *ctx, const void *ciphertext,
- size_t ciphertext_len, void *plaintext ) {
- struct rsa_context *context = ctx;
+static int rsa_decrypt ( const struct asn1_cursor *key,
+ const struct asn1_cursor *ciphertext,
+ struct asn1_builder *plaintext ) {
+ struct rsa_context context;
void *temp;
uint8_t *encoded;
uint8_t *end;
uint8_t *zero;
uint8_t *start;
- size_t plaintext_len;
+ size_t len;
+ int rc;
+
+ DBGC ( &context, "RSA %p decrypting:\n", &context );
+ DBGC_HDA ( &context, 0, ciphertext->data, ciphertext->len );
+
+ /* Initialise context */
+ if ( ( rc = rsa_init ( &context, key ) ) != 0 )
+ goto err_init;
/* Sanity check */
- if ( ciphertext_len != context->max_len ) {
- DBGC ( context, "RSA %p ciphertext incorrect length (%zd "
+ if ( ciphertext->len != context.max_len ) {
+ DBGC ( &context, "RSA %p ciphertext incorrect length (%zd "
"bytes, should be %zd)\n",
- context, ciphertext_len, context->max_len );
- return -ERANGE;
+ &context, ciphertext->len, context.max_len );
+ rc = -ERANGE;
+ goto err_sanity;
}
- DBGC ( context, "RSA %p decrypting:\n", context );
- DBGC_HDA ( context, 0, ciphertext, ciphertext_len );
/* Decipher the message (using the big integer input buffer as
* temporary storage)
*/
- temp = context->input0;
+ temp = context.input0;
encoded = temp;
- rsa_cipher ( context, ciphertext, encoded );
+ rsa_cipher ( &context, ciphertext->data, encoded );
/* Parse the message */
- end = ( encoded + context->max_len );
- if ( ( encoded[0] != 0x00 ) || ( encoded[1] != 0x02 ) )
- goto invalid;
+ end = ( encoded + context.max_len );
+ if ( ( encoded[0] != 0x00 ) || ( encoded[1] != 0x02 ) ) {
+ rc = -EINVAL;
+ goto err_invalid;
+ }
zero = memchr ( &encoded[2], 0, ( end - &encoded[2] ) );
- if ( ! zero )
- goto invalid;
+ if ( ! zero ) {
+ DBGC ( &context, "RSA %p invalid decrypted message:\n",
+ &context );
+ DBGC_HDA ( &context, 0, encoded, context.max_len );
+ rc = -EINVAL;
+ goto err_invalid;
+ }
start = ( zero + 1 );
- plaintext_len = ( end - start );
+ len = ( end - start );
+
+ /* Create space for plaintext */
+ if ( ( rc = asn1_grow ( plaintext, len ) ) != 0 )
+ goto err_grow;
/* Copy out message */
- memcpy ( plaintext, start, plaintext_len );
- DBGC ( context, "RSA %p decrypted:\n", context );
- DBGC_HDA ( context, 0, plaintext, plaintext_len );
+ memcpy ( plaintext->data, start, len );
+ DBGC ( &context, "RSA %p decrypted:\n", &context );
+ DBGC_HDA ( &context, 0, plaintext->data, len );
- return plaintext_len;
+ /* Free context */
+ rsa_free ( &context );
- invalid:
- DBGC ( context, "RSA %p invalid decrypted message:\n", context );
- DBGC_HDA ( context, 0, encoded, context->max_len );
- return -EINVAL;
+ return 0;
+
+ err_grow:
+ err_invalid:
+ err_sanity:
+ rsa_free ( &context );
+ err_init:
+ return rc;
}
/**
@@ -458,9 +475,9 @@ static int rsa_encode_digest ( struct rsa_context *context,
/* Sanity check */
max_len = ( context->max_len - 11 );
if ( digestinfo_len > max_len ) {
- DBGC ( context, "RSA %p %s digestInfo too long (%zd bytes, max"
- "%zd)\n",
- context, digest->name, digestinfo_len, max_len );
+ DBGC ( context, "RSA %p %s digestInfo too long (%zd bytes, "
+ "max %zd)\n", context, digest->name, digestinfo_len,
+ max_len );
return -ERANGE;
}
DBGC ( context, "RSA %p encoding %s digest:\n",
@@ -488,137 +505,149 @@ static int rsa_encode_digest ( struct rsa_context *context,
/**
* Sign digest value using RSA
*
- * @v ctx RSA context
+ * @v key Key
* @v digest Digest algorithm
* @v value Digest value
* @v signature Signature
- * @ret signature_len Signature length, or negative error
+ * @ret rc Return status code
*/
-static int rsa_sign ( void *ctx, struct digest_algorithm *digest,
- const void *value, void *signature ) {
- struct rsa_context *context = ctx;
- void *temp;
+static int rsa_sign ( const struct asn1_cursor *key,
+ struct digest_algorithm *digest, const void *value,
+ struct asn1_builder *signature ) {
+ struct rsa_context context;
int rc;
- DBGC ( context, "RSA %p signing %s digest:\n", context, digest->name );
- DBGC_HDA ( context, 0, value, digest->digestsize );
+ DBGC ( &context, "RSA %p signing %s digest:\n",
+ &context, digest->name );
+ DBGC_HDA ( &context, 0, value, digest->digestsize );
- /* Encode digest (using the big integer output buffer as
- * temporary storage)
- */
- temp = context->output0;
- if ( ( rc = rsa_encode_digest ( context, digest, value, temp ) ) != 0 )
- return rc;
+ /* Initialise context */
+ if ( ( rc = rsa_init ( &context, key ) ) != 0 )
+ goto err_init;
+
+ /* Create space for encoded digest and signature */
+ if ( ( rc = asn1_grow ( signature, context.max_len ) ) != 0 )
+ goto err_grow;
+
+ /* Encode digest */
+ if ( ( rc = rsa_encode_digest ( &context, digest, value,
+ signature->data ) ) != 0 )
+ goto err_encode;
/* Encipher the encoded digest */
- rsa_cipher ( context, temp, signature );
- DBGC ( context, "RSA %p signed %s digest:\n", context, digest->name );
- DBGC_HDA ( context, 0, signature, context->max_len );
+ rsa_cipher ( &context, signature->data, signature->data );
+ DBGC ( &context, "RSA %p signed %s digest:\n", &context, digest->name );
+ DBGC_HDA ( &context, 0, signature->data, signature->len );
+
+ /* Free context */
+ rsa_free ( &context );
+
+ return 0;
- return context->max_len;
+ err_encode:
+ err_grow:
+ rsa_free ( &context );
+ err_init:
+ return rc;
}
/**
* Verify signed digest value using RSA
*
- * @v ctx RSA context
+ * @v key Key
* @v digest Digest algorithm
* @v value Digest value
* @v signature Signature
- * @v signature_len Signature length
* @ret rc Return status code
*/
-static int rsa_verify ( void *ctx, struct digest_algorithm *digest,
- const void *value, const void *signature,
- size_t signature_len ) {
- struct rsa_context *context = ctx;
+static int rsa_verify ( const struct asn1_cursor *key,
+ struct digest_algorithm *digest, const void *value,
+ const struct asn1_cursor *signature ) {
+ struct rsa_context context;
void *temp;
void *expected;
void *actual;
int rc;
+ DBGC ( &context, "RSA %p verifying %s digest:\n",
+ &context, digest->name );
+ DBGC_HDA ( &context, 0, value, digest->digestsize );
+ DBGC_HDA ( &context, 0, signature->data, signature->len );
+
+ /* Initialise context */
+ if ( ( rc = rsa_init ( &context, key ) ) != 0 )
+ goto err_init;
+
/* Sanity check */
- if ( signature_len != context->max_len ) {
- DBGC ( context, "RSA %p signature incorrect length (%zd "
+ if ( signature->len != context.max_len ) {
+ DBGC ( &context, "RSA %p signature incorrect length (%zd "
"bytes, should be %zd)\n",
- context, signature_len, context->max_len );
- return -ERANGE;
+ &context, signature->len, context.max_len );
+ rc = -ERANGE;
+ goto err_sanity;
}
- DBGC ( context, "RSA %p verifying %s digest:\n",
- context, digest->name );
- DBGC_HDA ( context, 0, value, digest->digestsize );
- DBGC_HDA ( context, 0, signature, signature_len );
/* Decipher the signature (using the big integer input buffer
* as temporary storage)
*/
- temp = context->input0;
+ temp = context.input0;
expected = temp;
- rsa_cipher ( context, signature, expected );
- DBGC ( context, "RSA %p deciphered signature:\n", context );
- DBGC_HDA ( context, 0, expected, context->max_len );
+ rsa_cipher ( &context, signature->data, expected );
+ DBGC ( &context, "RSA %p deciphered signature:\n", &context );
+ DBGC_HDA ( &context, 0, expected, context.max_len );
/* Encode digest (using the big integer output buffer as
* temporary storage)
*/
- temp = context->output0;
+ temp = context.output0;
actual = temp;
- if ( ( rc = rsa_encode_digest ( context, digest, value, actual ) ) !=0 )
- return rc;
+ if ( ( rc = rsa_encode_digest ( &context, digest, value,
+ actual ) ) != 0 )
+ goto err_encode;
/* Verify the signature */
- if ( memcmp ( actual, expected, context->max_len ) != 0 ) {
- DBGC ( context, "RSA %p signature verification failed\n",
- context );
- return -EACCES_VERIFY;
+ if ( memcmp ( actual, expected, context.max_len ) != 0 ) {
+ DBGC ( &context, "RSA %p signature verification failed\n",
+ &context );
+ rc = -EACCES_VERIFY;
+ goto err_verify;
}
- DBGC ( context, "RSA %p signature verified successfully\n", context );
- return 0;
-}
+ /* Free context */
+ rsa_free ( &context );
-/**
- * Finalise RSA cipher
- *
- * @v ctx RSA context
- */
-static void rsa_final ( void *ctx ) {
- struct rsa_context *context = ctx;
+ DBGC ( &context, "RSA %p signature verified successfully\n", &context );
+ return 0;
- rsa_free ( context );
+ err_verify:
+ err_encode:
+ err_sanity:
+ rsa_free ( &context );
+ err_init:
+ return rc;
}
/**
* Check for matching RSA public/private key pair
*
* @v private_key Private key
- * @v private_key_len Private key length
* @v public_key Public key
- * @v public_key_len Public key length
* @ret rc Return status code
*/
-static int rsa_match ( const void *private_key, size_t private_key_len,
- const void *public_key, size_t public_key_len ) {
+static int rsa_match ( const struct asn1_cursor *private_key,
+ const struct asn1_cursor *public_key ) {
struct asn1_cursor private_modulus;
struct asn1_cursor private_exponent;
- struct asn1_cursor private_cursor;
struct asn1_cursor public_modulus;
struct asn1_cursor public_exponent;
- struct asn1_cursor public_cursor;
int rc;
- /* Initialise cursors */
- private_cursor.data = private_key;
- private_cursor.len = private_key_len;
- public_cursor.data = public_key;
- public_cursor.len = public_key_len;
-
/* Parse moduli and exponents */
if ( ( rc = rsa_parse_mod_exp ( &private_modulus, &private_exponent,
- &private_cursor ) ) != 0 )
+ private_key ) ) != 0 )
return rc;
if ( ( rc = rsa_parse_mod_exp ( &public_modulus, &public_exponent,
- &public_cursor ) ) != 0 )
+ public_key ) ) != 0 )
return rc;
/* Compare moduli */
@@ -631,14 +660,10 @@ static int rsa_match ( const void *private_key, size_t private_key_len,
/** RSA public-key algorithm */
struct pubkey_algorithm rsa_algorithm = {
.name = "rsa",
- .ctxsize = RSA_CTX_SIZE,
- .init = rsa_init,
- .max_len = rsa_max_len,
.encrypt = rsa_encrypt,
.decrypt = rsa_decrypt,
.sign = rsa_sign,
.verify = rsa_verify,
- .final = rsa_final,
.match = rsa_match,
};
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
index 8eecc75b3..023becec6 100644
--- a/src/crypto/sha1.c
+++ b/src/crypto/sha1.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/sha224.c b/src/crypto/sha224.c
index e54a0abb0..7e0cfd34e 100644
--- a/src/crypto/sha224.c
+++ b/src/crypto/sha224.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
index c30300eb4..742393612 100644
--- a/src/crypto/sha256.c
+++ b/src/crypto/sha256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/sha384.c b/src/crypto/sha384.c
index f1af6fc6f..3e5e98a31 100644
--- a/src/crypto/sha384.c
+++ b/src/crypto/sha384.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/sha512.c b/src/crypto/sha512.c
index d7d44b284..724cb71a5 100644
--- a/src/crypto/sha512.c
+++ b/src/crypto/sha512.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/sha512_224.c b/src/crypto/sha512_224.c
index b6728726c..3b256a3b9 100644
--- a/src/crypto/sha512_224.c
+++ b/src/crypto/sha512_224.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/sha512_256.c b/src/crypto/sha512_256.c
index 8163631e0..04df3f5bc 100644
--- a/src/crypto/sha512_256.c
+++ b/src/crypto/sha512_256.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
diff --git a/src/crypto/weierstrass.c b/src/crypto/weierstrass.c
new file mode 100644
index 000000000..a64626c85
--- /dev/null
+++ b/src/crypto/weierstrass.c
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+/** @file
+ *
+ * Weierstrass elliptic curves
+ *
+ * The implementation is based upon Algorithm 1 from "Complete
+ * addition formulas for prime order elliptic curves" (Joost Renes,
+ * Craig Costello, and Lejla Batina), available from
+ *
+ * https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/complete-2.pdf
+ *
+ * The steps within the algorithm have been reordered and temporary
+ * variables shuffled to reduce stack usage, and calculations are
+ * carried out modulo small multiples of the field prime in order to
+ * elide reductions after intermediate addition and subtraction
+ * operations.
+ *
+ * The algorithm is encoded using a bytecode representation, since
+ * this substantially reduces the code size compared to direct
+ * implementation of the big integer operations.
+ */
+
+#include <errno.h>
+#include <ipxe/weierstrass.h>
+
+/** Big integer register names */
+enum weierstrass_register {
+
+ /*
+ * Read-only registers
+ */
+
+ /* Curve constant "a" (for multiply), zero (for add/subtract) */
+ WEIERSTRASS_a = 0,
+ /* Curve constant "3b" */
+ WEIERSTRASS_3b,
+ /* Augend (x,y,z) co-ordinates */
+ WEIERSTRASS_x1,
+ WEIERSTRASS_y1,
+ WEIERSTRASS_z1,
+ /* Addend (x,y,z) co-ordinates */
+ WEIERSTRASS_x2,
+ WEIERSTRASS_y2,
+ WEIERSTRASS_z2,
+
+ /*
+ * Read-write registers
+ */
+
+ /* Temporary working registers */
+ WEIERSTRASS_Wt,
+ WEIERSTRASS_Wxy,
+ WEIERSTRASS_Wyz,
+ WEIERSTRASS_Wzx,
+ /* Low half of multiplication product */
+ WEIERSTRASS_Wp,
+ /* Result (x,y,z) co-ordinates */
+ WEIERSTRASS_x3,
+ WEIERSTRASS_y3,
+ WEIERSTRASS_z3,
+
+ /* Number of registers */
+ WEIERSTRASS_NUM_REGISTERS = 16
+};
+
+/** Zero register (for add/subtract operations */
+#define WEIERSTRASS_zero WEIERSTRASS_a
+
+/** Construct big integer register index */
+#define WEIERSTRASS_REGISTER( name ) _C2 ( WEIERSTRASS_, name )
+
+/** Bytecode operation codes */
+enum weierstrass_opcode {
+ /** Subtract big integers (and add nothing)*/
+ WEIERSTRASS_OP_SUB_0N = 0,
+ /** Subtract big integers (and add 2N) */
+ WEIERSTRASS_OP_SUB_2N = WEIERSTRASS_2N,
+ /** Subtract big integers (and add 4N) */
+ WEIERSTRASS_OP_SUB_4N = WEIERSTRASS_4N,
+ /** Add big integers */
+ WEIERSTRASS_OP_ADD,
+ /** Multiply big integers (and perform Montgomery reduction) */
+ WEIERSTRASS_OP_MUL,
+};
+
+/**
+ * Define a bytecode operation
+ *
+ * @v opcode Operation code
+ * @v dest Destination big integer register name
+ * @v left Left source big integer register name
+ * @v right Right source big integer register name
+ */
+#define WEIERSTRASS_OP( opcode, dest, left, right ) \
+ ( ( (opcode) << 12 ) | \
+ ( WEIERSTRASS_REGISTER ( dest ) << 8 ) | \
+ ( WEIERSTRASS_REGISTER ( left ) << 4 ) | \
+ ( WEIERSTRASS_REGISTER ( right ) << 0 ) )
+
+/** Extract bytecode operation code */
+#define WEIERSTRASS_OPCODE( op ) ( ( (op) >> 12 ) & 0xf )
+
+/** Extract destination big integer register */
+#define WEIERSTRASS_DEST( op ) ( ( (op) >> 8 ) & 0xf )
+
+/** Extract left source big integer register */
+#define WEIERSTRASS_LEFT( op ) ( ( (op) >> 4 ) & 0xf )
+
+/** Extract right source big integer register */
+#define WEIERSTRASS_RIGHT( op ) ( ( (op) >> 0 ) & 0xf )
+
+/** Define a three-argument addition operation */
+#define WEIERSTRASS_ADD3( dest, augend, addend ) \
+ WEIERSTRASS_OP ( WEIERSTRASS_OP_ADD, dest, augend, addend )
+
+/** Define a two-argument addition operation */
+#define WEIERSTRASS_ADD2( augend, addend ) \
+ WEIERSTRASS_ADD3 ( augend, augend, addend )
+
+/** Define a move operation */
+#define WEIERSTRASS_MOV( dest, source ) \
+ WEIERSTRASS_ADD3( dest, source, zero )
+
+/** Define a three-argument subtraction operation */
+#define WEIERSTRASS_SUB3( dest, minuend, subtrahend, multiple ) \
+ WEIERSTRASS_OP ( _C2 ( WEIERSTRASS_OP_SUB_, multiple ), \
+ dest, minuend, subtrahend )
+
+/** Define a two-argument subtraction operation */
+#define WEIERSTRASS_SUB2( minuend, subtrahend, multiple ) \
+ WEIERSTRASS_SUB3 ( minuend, minuend, subtrahend, multiple )
+
+/** Define a stop operation */
+#define WEIERSTRASS_STOP WEIERSTRASS_SUB2 ( zero, zero, 0N )
+
+/** Define a three-argument multiplication operation */
+#define WEIERSTRASS_MUL3( dest, multiplicand, multiplier ) \
+ WEIERSTRASS_OP ( WEIERSTRASS_OP_MUL, dest, multiplicand, multiplier )
+
+/** Define a two-argument multiplication operation */
+#define WEIERSTRASS_MUL2( multiplicand, multiplier ) \
+ WEIERSTRASS_MUL3 ( multiplicand, multiplicand, multiplier )
+
+/**
+ * Initialise curve
+ *
+ * @v curve Weierstrass curve
+ */
+static void weierstrass_init_curve ( struct weierstrass_curve *curve ) {
+ unsigned int size = curve->size;
+ bigint_t ( size ) __attribute__ (( may_alias )) *prime =
+ ( ( void * ) curve->prime[0] );
+ bigint_t ( size ) __attribute__ (( may_alias )) *fermat =
+ ( ( void * ) curve->fermat );
+ bigint_t ( size ) __attribute__ (( may_alias )) *square =
+ ( ( void * ) curve->square );
+ bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( void * ) curve->one );
+ bigint_t ( size ) __attribute__ (( may_alias )) *a =
+ ( ( void * ) curve->a );
+ bigint_t ( size ) __attribute__ (( may_alias )) *b3 =
+ ( ( void * ) curve->b3 );
+ bigint_t ( size ) __attribute__ (( may_alias )) *mont =
+ ( ( void * ) curve->mont[0] );
+ bigint_t ( size ) __attribute__ (( may_alias )) *temp =
+ ( ( void * ) curve->prime[1] );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias )) *product =
+ ( ( void * ) temp );
+ bigint_t ( size ) __attribute__ (( may_alias )) *two =
+ ( ( void * ) temp );
+ static const uint8_t one_raw[] = { 1 };
+ static const uint8_t two_raw[] = { 2 };
+ size_t len = curve->len;
+ unsigned int i;
+
+ /* Initialise field prime */
+ bigint_init ( prime, curve->prime_raw, len );
+ DBGC ( curve, "WEIERSTRASS %s N = %s\n",
+ curve->name, bigint_ntoa ( prime ) );
+
+ /* Calculate Montgomery constant R^2 mod N */
+ bigint_reduce ( prime, square );
+ DBGC ( curve, "WEIERSTRASS %s R^2 = %s mod N\n",
+ curve->name, bigint_ntoa ( square ) );
+
+ /* Calculate constant "3b" */
+ bigint_init ( b3, curve->b_raw, len );
+ DBGC ( curve, "WEIERSTRASS %s b = %s\n",
+ curve->name, bigint_ntoa ( b3 ) );
+ bigint_copy ( b3, a );
+ bigint_add ( b3, b3 );
+ bigint_add ( a, b3 );
+
+ /* Initialise "a" */
+ bigint_init ( a, curve->a_raw, len );
+ DBGC ( curve, "WEIERSTRASS %s a = %s\n",
+ curve->name, bigint_ntoa ( a ) );
+
+ /* Initialise "1" */
+ bigint_init ( one, one_raw, sizeof ( one_raw ) );
+
+ /* Convert relevant constants to Montgomery form
+ *
+ * We rely on the fact that the prime multiples have not yet
+ * been calculated, and so can be used as a temporary buffer.
+ */
+ for ( i = 0 ; i < WEIERSTRASS_NUM_MONT ; i++ ) {
+ static const char *names[] = { " ", " a", "3b" };
+ bigint_multiply ( &mont[i], square, product );
+ bigint_montgomery ( prime, product, &mont[i] );
+ DBGC ( curve, "WEIERSTRASS %s %sR = %s mod N\n",
+ curve->name, names[i], bigint_ntoa ( &mont[i] ) );
+ }
+
+ /* Calculate constant "N-2"
+ *
+ * We rely on the fact that the prime multiples have not yet
+ * been calculated, and so can be used as a temporary buffer.
+ */
+ bigint_copy ( prime, fermat );
+ bigint_init ( two, two_raw, sizeof ( two_raw ) );
+ bigint_subtract ( two, fermat );
+ DBGC ( curve, "WEIERSTRASS %s N-2 = %s\n",
+ curve->name, bigint_ntoa ( fermat ) );
+
+ /* Calculate multiples of field prime */
+ for ( i = 1 ; i < WEIERSTRASS_NUM_MULTIPLES ; i++ ) {
+ bigint_copy ( &prime[ i - 1 ], &prime[i] );
+ bigint_add ( &prime[i], &prime[i] );
+ DBGC ( curve, "WEIERSTRASS %s %dN = %s\n",
+ curve->name, ( 1 << i ), bigint_ntoa ( &prime[i] ) );
+ }
+}
+
+/**
+ * Execute bytecode instruction
+ *
+ * @v curve Weierstrass curve
+ * @v regs Registers
+ * @v size Big integer size
+ * @v op Operation
+ */
+static void weierstrass_exec ( const struct weierstrass_curve *curve,
+ void **regs, unsigned int size,
+ unsigned int op ) {
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *prime = ( ( const void * ) curve->prime[0] );
+ bigint_t ( size * 2 ) __attribute__ (( may_alias ))
+ *product = regs[WEIERSTRASS_Wp];
+ bigint_t ( size ) __attribute__ (( may_alias )) *dest;
+ const bigint_t ( size ) __attribute__ (( may_alias )) *left;
+ const bigint_t ( size ) __attribute__ (( may_alias )) *right;
+ const bigint_t ( size ) __attribute__ (( may_alias )) *addend;
+ const bigint_t ( size ) __attribute__ (( may_alias )) *subtrahend;
+ unsigned int op_code;
+ unsigned int op_dest;
+ unsigned int op_left;
+ unsigned int op_right;
+
+ /* Decode instruction */
+ op_code = WEIERSTRASS_OPCODE ( op );
+ op_dest = WEIERSTRASS_DEST ( op );
+ op_left = WEIERSTRASS_LEFT ( op );
+ op_right = WEIERSTRASS_RIGHT ( op );
+ dest = regs[op_dest];
+ left = regs[op_left];
+ right = regs[op_right];
+
+ /* Check destination is a writable register */
+ assert ( op_dest >= WEIERSTRASS_Wt );
+
+ /* Handle multiplications */
+ if ( op_code == WEIERSTRASS_OP_MUL ) {
+ assert ( op_left != WEIERSTRASS_Wp );
+ assert ( op_right != WEIERSTRASS_Wp );
+ bigint_multiply ( left, right, product );
+ bigint_montgomery_relaxed ( prime, product, dest );
+ DBGCP ( curve, "WEIERSTRASS %s R%d := R%d x R%d = %s\n",
+ curve->name, op_dest, op_left, op_right,
+ bigint_ntoa ( dest ) );
+ return;
+ }
+
+ /* Copy left source, if required */
+ if ( op_dest != op_left )
+ bigint_copy ( left, dest );
+
+ /* Do nothing more if addend/subtrahend is zero */
+ if ( ! op_right ) {
+ DBGCP ( curve, "WEIERSTRASS %s R%d := R%d = %s\n",
+ curve->name, op_dest, op_left, bigint_ntoa ( dest ) );
+ return;
+ }
+
+ /* Determine addend and subtrahend */
+ addend = NULL;
+ subtrahend = NULL;
+ if ( op_code == WEIERSTRASS_OP_ADD ) {
+ DBGCP ( curve, "WEIERSTRASS %s R%d := R%d + R%d = ",
+ curve->name, op_dest, op_left, op_right );
+ addend = ( ( const void * ) right );
+ } else {
+ subtrahend = ( ( const void * ) right );
+ if ( op_code > WEIERSTRASS_OP_SUB_0N ) {
+ DBGCP ( curve, "WEIERSTRASS %s R%d := R%d - R%d + "
+ "%dN = ", curve->name, op_dest, op_left,
+ op_right, ( 1 << op_code ) );
+ addend = ( ( const void * ) curve->prime[op_code] );
+ } else {
+ DBGCP ( curve, "WEIERSTRASS %s R%d := R%d - R%d = ",
+ curve->name, op_dest, op_left, op_right );
+ }
+ }
+
+ /* Perform addition and subtraction */
+ if ( addend )
+ bigint_add ( addend, dest );
+ if ( subtrahend )
+ bigint_subtract ( subtrahend, dest );
+ DBGCP ( curve, "%s\n", bigint_ntoa ( dest ) );
+}
+
+/**
+ * Add points on curve
+ *
+ * @v curve Weierstrass curve
+ * @v augend0 Element 0 of point (x1,y1,z1) to be added
+ * @v addend0 Element 0 of point (x2,y2,z2) to be added
+ * @v result0 Element 0 of point (x3,y3,z3) to hold result
+ *
+ * Points are represented in projective coordinates, with all values
+ * in Montgomery form and in the range [0,4N) where N is the field
+ * prime.
+ *
+ * The augend may have the same value as the addend (i.e. this routine
+ * may be used to perform point doubling as well as point addition),
+ * and either or both may be the point at infinity.
+ *
+ * The result may overlap either input, since the inputs are fully
+ * consumed before the result is written.
+ */
+static void weierstrass_add_raw ( const struct weierstrass_curve *curve,
+ const bigint_element_t *augend0,
+ const bigint_element_t *addend0,
+ bigint_element_t *result0 ) {
+ unsigned int size = curve->size;
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *prime = ( ( const void * ) curve->prime[0] );
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *a = ( ( const void * ) curve->a );
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *b3 = ( ( const void * ) curve->b3 );
+ const weierstrass_t ( size ) __attribute__ (( may_alias ))
+ *augend = ( ( const void * ) augend0 );
+ const weierstrass_t ( size ) __attribute__ (( may_alias ))
+ *addend = ( ( const void * ) addend0 );
+ weierstrass_t ( size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+ struct {
+ bigint_t ( size ) Wt;
+ bigint_t ( size ) Wxy;
+ bigint_t ( size ) Wyz;
+ bigint_t ( size ) Wzx;
+ bigint_t ( size * 2 ) Wp;
+ } temp;
+ void *regs[WEIERSTRASS_NUM_REGISTERS];
+ unsigned int schedule;
+ const uint16_t *op;
+ unsigned int i;
+
+ /* On entry, we assume that x1, x2, y1, y2, z1, z2 are all in
+ * the range [0,4N). Additions will extend the range.
+ * Subtractions will extend the range (and require an addition
+ * of a suitable multiple of the modulus to ensure that the
+ * result is a positive value). Relaxed Montgomery
+ * multiplications will reduce the range to [0,2N). The
+ * outputs x3, y3, z3 will be in the range [0,4N) and
+ * therefore usable as subsequent inputs.
+ */
+ static const uint16_t ops[] = {
+ /* [Wxy] Qxy = (x1+y1)*(x2+y2) (mod 2N) */
+ WEIERSTRASS_ADD3 ( Wt, x1, y1 ),
+ WEIERSTRASS_ADD3 ( Wxy, x2, y2 ),
+ WEIERSTRASS_MUL2 ( Wxy, Wt ),
+ /* [Wyz] Qyz = (y1+z1)*(y2+z2) (mod 2N) */
+ WEIERSTRASS_ADD3 ( Wt, y1, z1 ),
+ WEIERSTRASS_ADD3 ( Wyz, y2, z2 ),
+ WEIERSTRASS_MUL2 ( Wyz, Wt ),
+ /* [Wzx] Qzx = (z1+x1)*(z2+x2) (mod 2N) */
+ WEIERSTRASS_ADD3 ( Wt, z1, x1 ),
+ WEIERSTRASS_ADD3 ( Wzx, z2, x2 ),
+ WEIERSTRASS_MUL2 ( Wzx, Wt ),
+ /* [x3] Px = x1*x2 (mod 2N) */
+ WEIERSTRASS_MUL3 ( x3, x1, x2 ),
+ /* [y3] Py = y1*y2 (mod 2N) */
+ WEIERSTRASS_MUL3 ( y3, y1, y2 ),
+ /* [z3] Pz = z1*z2 (mod 2N) */
+ WEIERSTRASS_MUL3 ( z3, z1, z2 ),
+ /* [Wxy] Rxy = Qxy - Px - Py (mod 6N)
+ * = (x1+y1)*(x2+y2) - x1*x2 - y1*y2 (mod 6N)
+ * = x1*y2 + x2*y1 (mod 6N)
+ */
+ WEIERSTRASS_SUB2 ( Wxy, x3, 0N ),
+ WEIERSTRASS_SUB2 ( Wxy, y3, 4N ),
+ /* [Wyz] Ryz = Qyz - Py - Pz (mod 6N)
+ * = (y1+z1)*(y2+z2) - y1*y2 - z1*z2 (mod 6N)
+ * = y1*z2 + y2*z1 (mod 6N)
+ */
+ WEIERSTRASS_SUB2 ( Wyz, y3, 0N ),
+ WEIERSTRASS_SUB2 ( Wyz, z3, 4N ),
+ /* [Wzx] Rzx = Qzx - Pz - Px (mod 6N)
+ * = (z1+x1)*(z2+x2) - z1*z2 - x1*x2 (mod 6N)
+ * = x1*z2 + x2*z1 (mod 6N)
+ */
+ WEIERSTRASS_SUB2 ( Wzx, z3, 0N ),
+ WEIERSTRASS_SUB2 ( Wzx, x3, 4N ),
+ /* [Wt] aRzx = a * Rzx (mod 2N)
+ * = a * (x1*z2 + x2*z1) (mod 2N)
+ */
+ WEIERSTRASS_MUL3 ( Wt, a, Wzx ),
+ /* [Wp] 3bPz = 3b * Pz (mod 2N)
+ * = 3b*z1*z2 (mod 2N)
+ */
+ WEIERSTRASS_MUL3 ( Wp, 3b, z3 ),
+ /* [Wp] Sy = aRzx + 3bPz (mod 4N)
+ * = a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 4N)
+ */
+ WEIERSTRASS_ADD2 ( Wp, Wt ),
+ /* [Wt] Syz = Py + Sy (mod 6N)
+ * = y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 6N)
+ */
+ WEIERSTRASS_ADD3 ( Wt, y3, Wp ),
+ /* [y3] Sxy = Py - Sy (mod 6N)
+ * = y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2 (mod 6N)
+ */
+ WEIERSTRASS_SUB2 ( y3, Wp, 4N ),
+ /* [z3] aPz = a * Pz (mod 2N)
+ * = a * z1*z2 (mod 2N)
+ */
+ WEIERSTRASS_MUL2 ( z3, a ),
+ /* [Wzx] 3bRzx = 3b * Rzx (mod 2N)
+ * = 3b * (x1*z2 + x2*z1) (mod 2N)
+ */
+ WEIERSTRASS_MUL2 ( Wzx, 3b ),
+ /* [x3] aPzx' = Px - aPz (mod 4N)
+ * = x1*x2 - a*z1*z2 (mod 4N)
+ */
+ WEIERSTRASS_SUB2 ( x3, z3, 2N ),
+ /* [Wp] Szx = a * aPzx' (mod 2N)
+ * = a * (x1*x2 - a*z1*z2) (mod 2N)
+ * = a*x1*x2 - (a^2)*z1*z2 (mod 2N)
+ */
+ WEIERSTRASS_MUL3 ( Wp, a, x3 ),
+ /* [x3] Px = aPzx' + aPz (mod 6N)
+ * = x1*x2 - a*z1*z2 + a*z1*z2 (mod 6N)
+ * = x1*x2 (mod 6N)
+ */
+ WEIERSTRASS_ADD2 ( x3, z3 ),
+ /* [Wzx] Tzx = 3bRzx + Szx (mod 4N)
+ * = a*x1*x2 + 3b*(x1*z2 + x2*z1) -
+ * (a^2)*z1*z2 (mod 4N)
+ */
+ WEIERSTRASS_ADD2 ( Wzx, Wp ),
+ /* [z3] aPzx = Px + aPz (mod 8N)
+ * = x1*x2 + a*z1*z2 (mod 8N)
+ */
+ WEIERSTRASS_ADD2 ( z3, x3 ),
+ /* [x3] 2Px = Px + Px (mod 12N)
+ * = 2*x1*x2 (mod 12N)
+ */
+ WEIERSTRASS_ADD2 ( x3, x3 ),
+ /* [x3] Tyz = 2Px + aPzx (mod 20N)
+ * = 2*x1*x2 + x1*x2 + a*z1*z2 (mod 20N)
+ * = 3*x1*x2 + a*z1*z2 (mod 20N)
+ */
+ WEIERSTRASS_ADD2 ( x3, z3 ),
+ /* [z3] Syz = Syz (mod 6N)
+ * = y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2 (mod 6N)
+ */
+ WEIERSTRASS_MOV ( z3, Wt ),
+ /* [Wt] Tyz = Tyz (mod 20N)
+ * = 3*x1*x2 + a*z1*z2 (mod 20N)
+ */
+ WEIERSTRASS_MOV ( Wt, x3 ),
+ /* [x3] Ux = Rxy * Sxy (mod 2N)
+ * = (x1*y2 + x2*y1) *
+ * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2) (mod 2N)
+ */
+ WEIERSTRASS_MUL3 ( x3, Wxy, y3 ),
+ /* [y3] Uy = Syz * Sxy (mod 2N)
+ * = (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) *
+ * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2) (mod 2N)
+ */
+ WEIERSTRASS_MUL2 ( y3, z3 ),
+ /* [z3] Uz = Ryz * Syz (mod 2N)
+ * = (y1*z2 + y2*z1) *
+ * (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) (mod 2N)
+ */
+ WEIERSTRASS_MUL2 ( z3, Wyz ),
+ /* [Wp] Vx = Ryz * Tzx (mod 2N)
+ * = (y1*z2 + y2*z1) *
+ * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2)
+ * (mod 2N)
+ */
+ WEIERSTRASS_MUL3 ( Wp, Wyz, Wzx ),
+ /* [x3] x3 = Ux - Vx (mod 4N)
+ * = ((x1*y2 + x2*y1) *
+ * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2)) -
+ * ((y1*z2 + y2*z1) *
+ * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2))
+ * (mod 4N)
+ */
+ WEIERSTRASS_SUB2 ( x3, Wp, 2N ),
+ /* [Wp] Vy = Tyz * Tzx (mod 2N)
+ * = (3*x1*x2 + a*z1*z2) *
+ * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2)
+ * (mod 2N)
+ */
+ WEIERSTRASS_MUL3 ( Wp, Wt, Wzx ),
+ /* [y3] y3 = Vy + Uy (mod 4N)
+ * = ((3*x1*x2 + a*z1*z2) *
+ * (a*x1*x2 + 3b*(x1*z2 + x2*z1) - (a^2)*z1*z2)) +
+ * ((y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2) *
+ * (y1*y2 - a*(x1*z2 + x2*z1) - 3b*z1*z2))
+ * (mod 4N)
+ */
+ WEIERSTRASS_ADD2 ( y3, Wp ),
+ /* [Wp] Vz = Rxy * Tyz (mod 2N)
+ * = (x1*y2 + x2*y1) * (3*x1*x2 + a*z1*z2) (mod 2N)
+ */
+ WEIERSTRASS_MUL3 ( Wp, Wxy, Wt ),
+ /* [z3] z3 = Uz + Vz (mod 4N)
+ * = ((y1*z2 + y2*z1) *
+ * (y1*y2 + a*(x1*z2 + x2*z1) + 3b*z1*z2)) +
+ * ((x1*y2 + x2*y1) * (3*x1*x2 + a*z1*z2))
+ * (mod 4N)
+ */
+ WEIERSTRASS_ADD2 ( z3, Wp ),
+ /* Stop */
+ WEIERSTRASS_STOP
+ };
+
+ /* Initialise register list */
+ regs[WEIERSTRASS_a] = ( ( void * ) a );
+ regs[WEIERSTRASS_3b] = ( ( void * ) b3 );
+ regs[WEIERSTRASS_x1] = ( ( void * ) &augend->x );
+ regs[WEIERSTRASS_x2] = ( ( void * ) &addend->x );
+ regs[WEIERSTRASS_x3] = ( ( void * ) &result->x );
+ regs[WEIERSTRASS_Wt] = &temp;
+ schedule = ( ( ( 1 << WEIERSTRASS_NUM_REGISTERS ) - 1 )
+ - ( 1 << WEIERSTRASS_a )
+ - ( 1 << WEIERSTRASS_3b )
+ - ( 1 << WEIERSTRASS_x1 )
+ - ( 1 << WEIERSTRASS_x2 )
+ - ( 1 << WEIERSTRASS_x3 )
+ - ( 1 << WEIERSTRASS_Wt ) );
+ for ( i = 0 ; schedule ; i++, schedule >>= 1 ) {
+ if ( schedule & 1 )
+ regs[i] = ( regs[ i - 1 ] + sizeof ( *prime ) );
+ }
+ DBGC2 ( curve, "WEIERSTRASS %s augend (%s,",
+ curve->name, bigint_ntoa ( &augend->x ) );
+ DBGC2 ( curve, "%s,", bigint_ntoa ( &augend->y ) );
+ DBGC2 ( curve, "%s)\n", bigint_ntoa ( &augend->z ) );
+ DBGC2 ( curve, "WEIERSTRASS %s addend (%s,",
+ curve->name, bigint_ntoa ( &addend->x ) );
+ DBGC2 ( curve, "%s,", bigint_ntoa ( &addend->y ) );
+ DBGC2 ( curve, "%s)\n", bigint_ntoa ( &addend->z ) );
+
+ /* Sanity checks */
+ assert ( regs[WEIERSTRASS_a] == a );
+ assert ( regs[WEIERSTRASS_3b] == b3 );
+ assert ( regs[WEIERSTRASS_x1] == &augend->x );
+ assert ( regs[WEIERSTRASS_y1] == &augend->y );
+ assert ( regs[WEIERSTRASS_z1] == &augend->z );
+ assert ( regs[WEIERSTRASS_x2] == &addend->x );
+ assert ( regs[WEIERSTRASS_y2] == &addend->y );
+ assert ( regs[WEIERSTRASS_z2] == &addend->z );
+ assert ( regs[WEIERSTRASS_x3] == &result->x );
+ assert ( regs[WEIERSTRASS_y3] == &result->y );
+ assert ( regs[WEIERSTRASS_z3] == &result->z );
+ assert ( regs[WEIERSTRASS_Wt] == &temp.Wt );
+ assert ( regs[WEIERSTRASS_Wxy] == &temp.Wxy );
+ assert ( regs[WEIERSTRASS_Wyz] == &temp.Wyz );
+ assert ( regs[WEIERSTRASS_Wzx] == &temp.Wzx );
+ assert ( regs[WEIERSTRASS_Wp] == &temp.Wp );
+
+ /* Execute bytecode instruction sequence */
+ for ( op = ops ; *op != WEIERSTRASS_STOP ; op++ )
+ weierstrass_exec ( curve, regs, size, *op );
+ DBGC2 ( curve, "WEIERSTRASS %s result (%s,",
+ curve->name, bigint_ntoa ( &result->x ) );
+ DBGC2 ( curve, "%s,", bigint_ntoa ( &result->y ) );
+ DBGC2 ( curve, "%s)\n", bigint_ntoa ( &result->z ) );
+}
+
+/**
+ * Add points on curve
+ *
+ * @v curve Weierstrass curve
+ * @v augend Point (x1,y1,z1) to be added
+ * @v addend Point (x2,y2,z2) to be added
+ * @v result0 Point (x3,y3,z3) to hold result
+ */
+#define weierstrass_add( curve, augend, addend, result ) do { \
+ weierstrass_add_raw ( (curve), (augend)->all.element, \
+ (addend)->all.element, \
+ (result)->all.element ); \
+ } while ( 0 )
+
+/**
+ * Add points on curve as part of a Montgomery ladder
+ *
+ * @v operand Element 0 of first input operand (may overlap result)
+ * @v result Element 0 of second input operand and result
+ * @v size Number of elements in operands and result
+ * @v ctx Operation context
+ * @v tmp Temporary working space (not used)
+ */
+static void weierstrass_add_ladder ( const bigint_element_t *operand0,
+ bigint_element_t *result0,
+ unsigned int size, const void *ctx,
+ void *tmp __unused ) {
+ const struct weierstrass_curve *curve = ctx;
+ const weierstrass_t ( curve->size ) __attribute__ (( may_alias ))
+ *operand = ( ( const void * ) operand0 );
+ weierstrass_t ( curve->size ) __attribute__ (( may_alias ))
+ *result = ( ( void * ) result0 );
+
+ /* Add curve points */
+ assert ( size == bigint_size ( &operand->all ) );
+ assert ( size == bigint_size ( &result->all ) );
+ weierstrass_add ( curve, operand, result, result );
+}
+
+/**
+ * Verify freshly initialised point is on curve
+ *
+ * @v curve Weierstrass curve
+ * @v point0 Element 0 of point (x,y,z) to be verified
+ * @ret rc Return status code
+ *
+ * As with point addition, points are represented in projective
+ * coordinates, with all values in Montgomery form and in the range
+ * [0,4N) where N is the field prime.
+ *
+ * This verification logic is valid only for points that have been
+ * freshly constructed via weierstrass_init() (i.e. must either have
+ * z=1 or be the point at infinity (0,1,0)).
+ */
+static int weierstrass_verify_raw ( const struct weierstrass_curve *curve,
+ const bigint_element_t *point0 ) {
+ unsigned int size = curve->size;
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *prime = ( ( const void * ) curve->prime[0] );
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *a = ( ( const void * ) curve->a );
+ const bigint_t ( size ) __attribute__ (( may_alias ))
+ *b3 = ( ( const void * ) curve->b3 );
+ const weierstrass_t ( size ) __attribute__ (( may_alias ))
+ *point = ( ( const void * ) point0 );
+ struct {
+ bigint_t ( size ) Wt;
+ bigint_t ( size * 2 ) Wp;
+ } temp;
+ void *regs[WEIERSTRASS_NUM_REGISTERS];
+ const uint16_t *op;
+
+ /* Calculate 3*(x^3 + a*x + b - y^2) */
+ static const uint16_t ops[] = {
+ /* [Wt] Tx = x^2 (mod 2N) */
+ WEIERSTRASS_MUL3 ( Wt, x1, x1 ),
+ /* [Wt] Txa = Tx + a (mod 3N)
+ * = x^2 + a (mod 3N)
+ */
+ WEIERSTRASS_MOV ( Wp, a ),
+ WEIERSTRASS_ADD2 ( Wt, Wp ),
+ /* [Wt] Txax = Txa * x (mod 2N)
+ * = (x^2 + a)*x (mod 2N)
+ * = x^3 + a*x (mod 2N)
+ */
+ WEIERSTRASS_MUL2 ( Wt, x1 ),
+ /* [Wp] Ty = y^2 (mod 2N) */
+ WEIERSTRASS_MUL3 ( Wp, y1, y1 ),
+ /* [Wt] Txaxy = Txax - Ty (mod 4N)
+ * = x^3 + a*x - y^2 (mod 4N)
+ */
+ WEIERSTRASS_SUB2 ( Wt, Wp, 2N ),
+ /* [Wp] 2Txaxy = Txaxy + Txaxy (mod 8N)
+ * = 2*(x^3 + a*x - y^2) (mod 8N)
+ */
+ WEIERSTRASS_ADD3 ( Wp, Wt, Wt ),
+ /* [Wt] 3Txaxy = 2Txaxy + Txaxy (mod 12N)
+ * = 3*(x^3 + a*x - y^2) (mod 12N)
+ */
+ WEIERSTRASS_ADD2 ( Wt, Wp ),
+ /* [Wt] 3Txaxyb = 3Txaxy + 3b (mod 13N)
+ * = 3*(x^3 + a*x - y^2) + 3b (mod 13N)
+ * = 3*(x^3 + a*x + b - y^2) (mod 13N)
+ */
+ WEIERSTRASS_ADD2 ( Wt, 3b ),
+ /* [Wt] check = 3Txaxyb * z (mod 2N)
+ * = 3*(x^3 + a*x + b - y^2) * z (mod 2N)
+ */
+ WEIERSTRASS_MUL2 ( Wt, z1 ),
+ /* Stop */
+ WEIERSTRASS_STOP
+ };
+
+ /* Initialise register list */
+ regs[WEIERSTRASS_a] = ( ( void * ) a );
+ regs[WEIERSTRASS_3b] = ( ( void * ) b3 );
+ regs[WEIERSTRASS_x1] = ( ( void * ) &point->x );
+ regs[WEIERSTRASS_y1] = ( ( void * ) &point->y );
+ regs[WEIERSTRASS_z1] = ( ( void * ) &point->z );
+ regs[WEIERSTRASS_Wt] = &temp.Wt;
+ regs[WEIERSTRASS_Wp] = &temp.Wp;
+
+ /* Execute bytecode instruction sequence */
+ for ( op = ops ; *op != WEIERSTRASS_STOP ; op++ )
+ weierstrass_exec ( curve, regs, size, *op );
+
+ /* Check that result is zero (modulo the field prime) */
+ bigint_grow ( &temp.Wt, &temp.Wp );
+ bigint_montgomery ( prime, &temp.Wp, &temp.Wt );
+ if ( ! bigint_is_zero ( &temp.Wt ) ) {
+ DBGC ( curve, "WEIERSTRASS %s base point is not on curve\n",
+ curve->name );
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * Verify freshly initialised point is on curve
+ *
+ * @v curve Weierstrass curve
+ * @v point Point (x,y,z) to be verified
+ * @ret rc Return status code
+ */
+#define weierstrass_verify( curve, point ) ( { \
+ weierstrass_verify_raw ( (curve), (point)->all.element ); \
+ } )
+
+/**
+ * Initialise curve point
+ *
+ * @v curve Weierstrass curve
+ * @v point0 Element 0 of point (x,y,z) to be filled in
+ * @v temp0 Element 0 of temporary point buffer
+ * @v data Raw curve point
+ * @ret rc Return status code
+ */
+static int weierstrass_init_raw ( struct weierstrass_curve *curve,
+ bigint_element_t *point0,
+ bigint_element_t *temp0, const void *data ) {
+ unsigned int size = curve->size;
+ size_t len = curve->len;
+ const bigint_t ( size ) __attribute__ (( may_alias )) *prime =
+ ( ( const void * ) curve->prime[0] );
+ const bigint_t ( size ) __attribute__ (( may_alias )) *prime2 =
+ ( ( const void * ) curve->prime[WEIERSTRASS_2N] );
+ const bigint_t ( size ) __attribute__ (( may_alias )) *square =
+ ( ( const void * ) curve->square );
+ const bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( const void * ) curve->one );
+ weierstrass_t ( size ) __attribute__ (( may_alias ))
+ *point = ( ( void * ) point0 );
+ union {
+ bigint_t ( size * 2 ) product;
+ weierstrass_t ( size ) point;
+ } __attribute__ (( may_alias )) *temp = ( ( void * ) temp0 );
+ size_t offset;
+ int is_infinite;
+ unsigned int i;
+ int rc;
+
+ /* Initialise curve, if not already done
+ *
+ * The least significant element of the field prime must be
+ * odd, and so the least significant element of the
+ * (initialised) first multiple of the field prime must be
+ * non-zero.
+ */
+ if ( ! prime2->element[0] )
+ weierstrass_init_curve ( curve );
+
+ /* Convert input to projective coordinates in Montgomery form */
+ DBGC ( curve, "WEIERSTRASS %s point (", curve->name );
+ for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) {
+ bigint_init ( &point->axis[i], ( data + offset ), len );
+ DBGC ( curve, "%s%s", ( i ? "," : "" ),
+ bigint_ntoa ( &point->axis[i] ) );
+ bigint_multiply ( &point->axis[i], square, &temp->product );
+ bigint_montgomery_relaxed ( prime, &temp->product,
+ &point->axis[i] );
+ }
+ memset ( &point->z, 0, sizeof ( point->z ) );
+ is_infinite = bigint_is_zero ( &point->xy );
+ bigint_copy ( one, &point->axis[ is_infinite ? 1 : 2 ] );
+ DBGC ( curve, ")\n" );
+
+ /* Verify point is on curve */
+ if ( ( rc = weierstrass_verify ( curve, point ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Initialise curve point
+ *
+ * @v curve Weierstrass curve
+ * @v point Point (x,y,z) to be filled in
+ * @v temp Temporary point buffer
+ * @v data Raw curve point
+ * @ret rc Return status code
+ */
+#define weierstrass_init( curve, point, temp, data ) ( { \
+ weierstrass_init_raw ( (curve), (point)->all.element, \
+ (temp)->all.element, (data) ); \
+ } )
+
+/**
+ * Finalise curve point
+ *
+ * @v curve Weierstrass curve
+ * @v point0 Element 0 of point (x,y,z)
+ * @v temp0 Element 0 of temporary point buffer
+ * @v out Output buffer
+ */
+static void weierstrass_done_raw ( struct weierstrass_curve *curve,
+ bigint_element_t *point0,
+ bigint_element_t *temp0, void *out ) {
+ unsigned int size = curve->size;
+ size_t len = curve->len;
+ const bigint_t ( size ) __attribute__ (( may_alias )) *prime =
+ ( ( const void * ) curve->prime[0] );
+ const bigint_t ( size ) __attribute__ (( may_alias )) *fermat =
+ ( ( const void * ) curve->fermat );
+ const bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( const void * ) curve->one );
+ weierstrass_t ( size ) __attribute__ (( may_alias ))
+ *point = ( ( void * ) point0 );
+ union {
+ bigint_t ( size * 2 ) product;
+ weierstrass_t ( size ) point;
+ } __attribute__ (( may_alias )) *temp = ( ( void * ) temp0 );
+ size_t offset;
+ unsigned int i;
+
+ /* Invert result Z co-ordinate (via Fermat's little theorem) */
+ bigint_copy ( one, &temp->point.z );
+ bigint_ladder ( &temp->point.z, &point->z, fermat,
+ bigint_mod_exp_ladder, prime, &temp->product );
+
+ /* Convert result back to affine co-ordinates */
+ DBGC ( curve, "WEIERSTRASS %s result (", curve->name );
+ for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) {
+ bigint_multiply ( &point->axis[i], &temp->point.z,
+ &temp->product );
+ bigint_montgomery_relaxed ( prime, &temp->product,
+ &point->axis[i] );
+ bigint_grow ( &point->axis[i], &temp->product );
+ bigint_montgomery ( prime, &temp->product, &point->axis[i] );
+ DBGC ( curve, "%s%s", ( i ? "," : "" ),
+ bigint_ntoa ( &point->axis[i] ) );
+ bigint_done ( &point->axis[i], ( out + offset ), len );
+ }
+ DBGC ( curve, ")\n" );
+}
+
+/**
+ * Finalise curve point
+ *
+ * @v curve Weierstrass curve
+ * @v point Point (x,y,z)
+ * @v temp Temporary point buffer
+ * @v out Output buffer
+ * @ret rc Return status code
+ */
+#define weierstrass_done( curve, point, temp, out ) ( { \
+ weierstrass_done_raw ( (curve), (point)->all.element, \
+ (temp)->all.element, (out) ); \
+ } )
+
+/**
+ * Check if this is the point at infinity
+ *
+ * @v point Curve point
+ * @ret is_infinity This is the point at infinity
+ */
+int weierstrass_is_infinity ( struct weierstrass_curve *curve,
+ const void *point ) {
+ unsigned int size = curve->size;
+ size_t len = curve->len;
+ struct {
+ bigint_t ( size ) axis;
+ } temp;
+ size_t offset;
+ int is_finite = 0;
+ unsigned int i;
+
+ /* We use all zeroes to represent the point at infinity */
+ DBGC ( curve, "WEIERSTRASS %s point (", curve->name );
+ for ( i = 0, offset = 0 ; i < WEIERSTRASS_AXES ; i++, offset += len ) {
+ bigint_init ( &temp.axis, ( point + offset ), len );
+ DBGC ( curve, "%s%s", ( i ? "," : "" ),
+ bigint_ntoa ( &temp.axis ) );
+ is_finite |= ( ! bigint_is_zero ( &temp.axis ) );
+ }
+ DBGC ( curve, ") is%s infinity\n", ( is_finite ? " not" : "" ) );
+
+ return ( ! is_finite );
+}
+
+/**
+ * Multiply curve point by scalar
+ *
+ * @v curve Weierstrass curve
+ * @v base Base point
+ * @v scalar Scalar multiple
+ * @v result Result point to fill in
+ * @ret rc Return status code
+ */
+int weierstrass_multiply ( struct weierstrass_curve *curve, const void *base,
+ const void *scalar, void *result ) {
+ unsigned int size = curve->size;
+ size_t len = curve->len;
+ const bigint_t ( size ) __attribute__ (( may_alias )) *one =
+ ( ( const void * ) curve->one );
+ struct {
+ weierstrass_t ( size ) result;
+ weierstrass_t ( size ) multiple;
+ bigint_t ( bigint_required_size ( len ) ) scalar;
+ } temp;
+ int rc;
+
+ /* Convert input to projective coordinates in Montgomery form */
+ if ( ( rc = weierstrass_init ( curve, &temp.multiple, &temp.result,
+ base ) ) != 0 ) {
+ return rc;
+ }
+
+ /* Construct identity element (the point at infinity) */
+ memset ( &temp.result, 0, sizeof ( temp.result ) );
+ bigint_copy ( one, &temp.result.y );
+
+ /* Initialise scalar */
+ bigint_init ( &temp.scalar, scalar, len );
+ DBGC ( curve, "WEIERSTRASS %s scalar %s\n",
+ curve->name, bigint_ntoa ( &temp.scalar ) );
+
+ /* Perform multiplication via Montgomery ladder */
+ bigint_ladder ( &temp.result.all, &temp.multiple.all, &temp.scalar,
+ weierstrass_add_ladder, curve, NULL );
+
+ /* Convert result back to affine co-ordinates */
+ weierstrass_done ( curve, &temp.result, &temp.multiple, result );
+
+ return 0;
+}
+
+/**
+ * Add curve points (as a one-off operation)
+ *
+ * @v curve Weierstrass curve
+ * @v addend Curve point to add
+ * @v augend Curve point to add
+ * @v result Curve point to hold result
+ * @ret rc Return status code
+ */
+int weierstrass_add_once ( struct weierstrass_curve *curve,
+ const void *addend, const void *augend,
+ void *result ) {
+ unsigned int size = curve->size;
+ struct {
+ weierstrass_t ( size ) addend;
+ weierstrass_t ( size ) augend;
+ weierstrass_t ( size ) result;
+ } temp;
+ int rc;
+
+ /* Convert inputs to projective coordinates in Montgomery form */
+ if ( ( rc = weierstrass_init ( curve, &temp.addend, &temp.result,
+ addend ) ) != 0 ) {
+ return rc;
+ }
+ if ( ( rc = weierstrass_init ( curve, &temp.augend, &temp.result,
+ augend ) ) != 0 ) {
+ return rc;
+ }
+
+ /* Add curve points */
+ weierstrass_add ( curve, &temp.augend, &temp.addend, &temp.result );
+
+ /* Convert result back to affine co-ordinates */
+ weierstrass_done ( curve, &temp.result, &temp.addend, result );
+
+ return 0;
+}
diff --git a/src/crypto/x25519.c b/src/crypto/x25519.c
index d58f7168c..95c42ea13 100644
--- a/src/crypto/x25519.c
+++ b/src/crypto/x25519.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -334,6 +335,7 @@ static void x25519_init_constants ( void ) {
/** Initialisation function */
struct init_fn x25519_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "x25519",
.initialise = x25519_init_constants,
};
@@ -563,8 +565,8 @@ void x25519_invert ( const union x25519_oct258 *invertend,
* @v value Big integer to be subtracted from, if possible
*/
static void x25519_reduce_by ( const x25519_t *subtrahend, x25519_t *value ) {
- unsigned int max_bit = ( ( 8 * sizeof ( *value ) ) - 1 );
x25519_t tmp;
+ int underflow;
/* Conditionally subtract subtrahend
*
@@ -572,8 +574,8 @@ static void x25519_reduce_by ( const x25519_t *subtrahend, x25519_t *value ) {
* time) if the subtraction underflows.
*/
bigint_copy ( value, &tmp );
- bigint_subtract ( subtrahend, value );
- bigint_swap ( value, &tmp, bigint_bit_is_set ( value, max_bit ) );
+ underflow = bigint_subtract ( subtrahend, value );
+ bigint_swap ( value, &tmp, underflow );
}
/**
@@ -783,16 +785,29 @@ static void x25519_reverse ( struct x25519_value *value ) {
}
/**
+ * Check if X25519 value is zero
+ *
+ * @v value Value to check
+ * @ret is_zero Value is zero
+ */
+int x25519_is_zero ( const struct x25519_value *value ) {
+ x25519_t point;
+
+ /* Check if value is zero */
+ bigint_init ( &point, value->raw, sizeof ( value->raw ) );
+ return bigint_is_zero ( &point );
+}
+
+/**
* Calculate X25519 key
*
* @v base Base point
* @v scalar Scalar multiple
* @v result Point to hold result (may overlap base point)
- * @ret rc Return status code
*/
-int x25519_key ( const struct x25519_value *base,
- const struct x25519_value *scalar,
- struct x25519_value *result ) {
+void x25519_key ( const struct x25519_value *base,
+ const struct x25519_value *scalar,
+ struct x25519_value *result ) {
struct x25519_value *tmp = result;
union x25519_quad257 point;
@@ -813,15 +828,24 @@ int x25519_key ( const struct x25519_value *base,
/* Reverse result */
bigint_done ( &point.value, result->raw, sizeof ( result->raw ) );
x25519_reverse ( result );
+}
+
+/**
+ * Check if this is the point at infinity
+ *
+ * @v point Curve point
+ * @ret is_infinity This is the point at infinity
+ */
+static int x25519_curve_is_infinity ( const void *point ) {
- /* Fail if result was all zeros (as required by RFC8422) */
- return ( bigint_is_zero ( &point.value ) ? -EPERM : 0 );
+ /* We use all zeroes for the point at infinity (as per RFC8422) */
+ return x25519_is_zero ( point );
}
/**
* Multiply scalar by curve point
*
- * @v base Base point (or NULL to use generator)
+ * @v base Base point
* @v scalar Scalar multiple
* @v result Result point to fill in
* @ret rc Return status code
@@ -829,16 +853,32 @@ int x25519_key ( const struct x25519_value *base,
static int x25519_curve_multiply ( const void *base, const void *scalar,
void *result ) {
- /* Use base point if applicable */
- if ( ! base )
- base = &x25519_generator;
+ x25519_key ( base, scalar, result );
+ return 0;
+}
+
+/**
+ * Add curve points (as a one-off operation)
+ *
+ * @v addend Curve point to add
+ * @v augend Curve point to add
+ * @v result Curve point to hold result
+ * @ret rc Return status code
+ */
+static int x25519_curve_add ( const void *addend __unused,
+ const void *augend __unused,
+ void *result __unused ) {
- return x25519_key ( base, scalar, result );
+ return -ENOTTY;
}
/** X25519 elliptic curve */
struct elliptic_curve x25519_curve = {
.name = "x25519",
+ .pointsize = sizeof ( struct x25519_value ),
.keysize = sizeof ( struct x25519_value ),
+ .base = x25519_generator.raw,
+ .is_infinity = x25519_curve_is_infinity,
.multiply = x25519_curve_multiply,
+ .add = x25519_curve_add,
};
diff --git a/src/crypto/x509.c b/src/crypto/x509.c
index 92318093e..6a3fe423b 100644
--- a/src/crypto/x509.c
+++ b/src/crypto/x509.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <string.h>
@@ -38,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/rsa.h>
#include <ipxe/rootcert.h>
#include <ipxe/certstore.h>
+#include <ipxe/privkey.h>
#include <ipxe/socket.h>
#include <ipxe/in.h>
#include <ipxe/image.h>
@@ -231,7 +233,7 @@ static int x509_parse_serial ( struct x509_certificate *cert,
cert, strerror ( rc ) );
return rc;
}
- DBGC2 ( cert, "X509 %p issuer is:\n", cert );
+ DBGC2 ( cert, "X509 %p serial is:\n", cert );
DBGC2_HDA ( cert, 0, serial->raw.data, serial->raw.len );
return 0;
@@ -391,7 +393,7 @@ static int x509_parse_public_key ( struct x509_certificate *cert,
const struct asn1_cursor *raw ) {
struct x509_public_key *public_key = &cert->subject.public_key;
struct asn1_algorithm **algorithm = &public_key->algorithm;
- struct asn1_bit_string *raw_bits = &public_key->raw_bits;
+ struct asn1_cursor *value = &public_key->value;
struct asn1_cursor cursor;
int rc;
@@ -415,8 +417,9 @@ static int x509_parse_public_key ( struct x509_certificate *cert,
cert, (*algorithm)->name );
asn1_skip_any ( &cursor );
- /* Parse bit string */
- if ( ( rc = asn1_bit_string ( &cursor, raw_bits ) ) != 0 ) {
+ /* Parse subjectPublicKey */
+ memcpy ( value, &cursor, sizeof ( *value ) );
+ if ( ( rc = asn1_enter_bits ( value, NULL ) ) != 0 ) {
DBGC ( cert, "X509 %p could not parse public key bits: %s\n",
cert, strerror ( rc ) );
return rc;
@@ -497,8 +500,9 @@ static int x509_parse_basic_constraints ( struct x509_certificate *cert,
static int x509_parse_key_usage ( struct x509_certificate *cert,
const struct asn1_cursor *raw ) {
struct x509_key_usage *usage = &cert->extensions.usage;
- struct asn1_bit_string bit_string;
+ struct asn1_cursor cursor;
const uint8_t *bytes;
+ unsigned int unused;
size_t len;
unsigned int i;
int rc;
@@ -506,16 +510,17 @@ static int x509_parse_key_usage ( struct x509_certificate *cert,
/* Mark extension as present */
usage->present = 1;
- /* Parse bit string */
- if ( ( rc = asn1_bit_string ( raw, &bit_string ) ) != 0 ) {
+ /* Enter bit string */
+ memcpy ( &cursor, raw, sizeof ( cursor ) );
+ if ( ( rc = asn1_enter_bits ( &cursor, &unused ) ) != 0 ) {
DBGC ( cert, "X509 %p could not parse key usage: %s\n",
cert, strerror ( rc ) );
return rc;
}
/* Parse key usage bits */
- bytes = bit_string.data;
- len = bit_string.len;
+ bytes = cursor.data;
+ len = cursor.len;
if ( len > sizeof ( usage->bits ) )
len = sizeof ( usage->bits );
for ( i = 0 ; i < len ; i++ ) {
@@ -1004,7 +1009,7 @@ int x509_parse ( struct x509_certificate *cert,
const struct asn1_cursor *raw ) {
struct x509_signature *signature = &cert->signature;
struct asn1_algorithm **signature_algorithm = &signature->algorithm;
- struct asn1_bit_string *signature_value = &signature->value;
+ struct asn1_cursor *signature_value = &signature->value;
struct asn1_cursor cursor;
int rc;
@@ -1032,8 +1037,8 @@ int x509_parse ( struct x509_certificate *cert,
asn1_skip_any ( &cursor );
/* Parse signatureValue */
- if ( ( rc = asn1_integral_bit_string ( &cursor,
- signature_value ) ) != 0 ) {
+ memcpy ( signature_value, &cursor, sizeof ( *signature_value ) );
+ if ( ( rc = asn1_enter_bits ( signature_value, NULL ) ) != 0 ) {
DBGC ( cert, "X509 %p could not parse signature value: %s\n",
cert, strerror ( rc ) );
return rc;
@@ -1078,7 +1083,7 @@ int x509_certificate ( const void *data, size_t len,
asn1_shrink_any ( &cursor );
/* Return stored certificate, if present */
- if ( ( *cert = certstore_find ( &cursor ) ) != NULL ) {
+ if ( ( *cert = x509_find ( NULL, &cursor ) ) != NULL ) {
/* Add caller's reference */
x509_get ( *cert );
@@ -1124,7 +1129,6 @@ static int x509_check_signature ( struct x509_certificate *cert,
struct pubkey_algorithm *pubkey = algorithm->pubkey;
uint8_t digest_ctx[ digest->ctxsize ];
uint8_t digest_out[ digest->digestsize ];
- uint8_t pubkey_ctx[ pubkey->ctxsize ];
int rc;
/* Sanity check */
@@ -1148,15 +1152,8 @@ static int x509_check_signature ( struct x509_certificate *cert,
}
/* Verify signature using signer's public key */
- if ( ( rc = pubkey_init ( pubkey, pubkey_ctx, public_key->raw.data,
- public_key->raw.len ) ) != 0 ) {
- DBGC ( cert, "X509 %p \"%s\" cannot initialise public key: "
- "%s\n", cert, x509_name ( cert ), strerror ( rc ) );
- goto err_pubkey_init;
- }
- if ( ( rc = pubkey_verify ( pubkey, pubkey_ctx, digest, digest_out,
- signature->value.data,
- signature->value.len ) ) != 0 ) {
+ if ( ( rc = pubkey_verify ( pubkey, &public_key->raw, digest,
+ digest_out, &signature->value ) ) != 0 ) {
DBGC ( cert, "X509 %p \"%s\" signature verification failed: "
"%s\n", cert, x509_name ( cert ), strerror ( rc ) );
goto err_pubkey_verify;
@@ -1166,8 +1163,6 @@ static int x509_check_signature ( struct x509_certificate *cert,
rc = 0;
err_pubkey_verify:
- pubkey_final ( pubkey, pubkey_ctx );
- err_pubkey_init:
err_mismatch:
return rc;
}
@@ -1331,9 +1326,9 @@ int x509_is_valid ( struct x509_certificate *cert, struct x509_root *root ) {
* @v issuer Issuing X.509 certificate (or NULL)
* @v root Root certificate list
*/
-static void x509_set_valid ( struct x509_certificate *cert,
- struct x509_certificate *issuer,
- struct x509_root *root ) {
+void x509_set_valid ( struct x509_certificate *cert,
+ struct x509_certificate *issuer,
+ struct x509_root *root ) {
unsigned int max_path_remaining;
/* Sanity checks */
@@ -1642,11 +1637,17 @@ struct x509_chain * x509_alloc_chain ( void ) {
*/
int x509_append ( struct x509_chain *chain, struct x509_certificate *cert ) {
struct x509_link *link;
+ int rc;
+
+ /* Ensure allocation of link cannot invalidate certificate */
+ x509_get ( cert );
/* Allocate link */
link = zalloc ( sizeof ( *link ) );
- if ( ! link )
- return -ENOMEM;
+ if ( ! link ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
/* Add link to chain */
link->cert = x509_get ( cert );
@@ -1654,7 +1655,12 @@ int x509_append ( struct x509_chain *chain, struct x509_certificate *cert ) {
DBGC ( chain, "X509 chain %p added X509 %p \"%s\"\n",
chain, cert, x509_name ( cert ) );
- return 0;
+ /* Success */
+ rc = 0;
+
+ x509_put ( cert );
+ err_alloc:
+ return rc;
}
/**
@@ -1711,25 +1717,139 @@ void x509_truncate ( struct x509_chain *chain, struct x509_link *link ) {
}
/**
+ * Mark X.509 certificate as found
+ *
+ * @v store Certificate store
+ * @v cert X.509 certificate
+ * @ret cert X.509 certificate
+ */
+static struct x509_certificate * x509_found ( struct x509_chain *store,
+ struct x509_certificate *cert ) {
+
+ /* Sanity check */
+ assert ( store != NULL );
+
+ /* Mark as found, if applicable */
+ if ( store->found )
+ store->found ( store, cert );
+
+ return cert;
+}
+
+/**
+ * Identify X.509 certificate by raw certificate data
+ *
+ * @v store Certificate store, or NULL to use default
+ * @v raw Raw certificate data
+ * @ret cert X.509 certificate, or NULL if not found
+ */
+struct x509_certificate * x509_find ( struct x509_chain *store,
+ const struct asn1_cursor *raw ) {
+ struct x509_link *link;
+ struct x509_certificate *cert;
+
+ /* Use default certificate store if none specified */
+ if ( ! store )
+ store = &certstore;
+
+ /* Search for certificate within store */
+ list_for_each_entry ( link, &store->links, list ) {
+
+ /* Check raw certificate data */
+ cert = link->cert;
+ if ( asn1_compare ( raw, &cert->raw ) == 0 )
+ return x509_found ( store, cert );
+ }
+
+ return NULL;
+}
+
+/**
* Identify X.509 certificate by subject
*
- * @v certs X.509 certificate list
+ * @v store Certificate store, or NULL to use default
* @v subject Subject
* @ret cert X.509 certificate, or NULL if not found
*/
-static struct x509_certificate *
-x509_find_subject ( struct x509_chain *certs,
+struct x509_certificate *
+x509_find_subject ( struct x509_chain *store,
const struct asn1_cursor *subject ) {
struct x509_link *link;
struct x509_certificate *cert;
+ /* Use default certificate store if none specified */
+ if ( ! store )
+ store = &certstore;
+
/* Scan through certificate list */
- list_for_each_entry ( link, &certs->links, list ) {
+ list_for_each_entry ( link, &store->links, list ) {
/* Check subject */
cert = link->cert;
if ( asn1_compare ( subject, &cert->subject.raw ) == 0 )
- return cert;
+ return x509_found ( store, cert );
+ }
+
+ return NULL;
+}
+
+/**
+ * Identify X.509 certificate by issuer and serial number
+ *
+ * @v store Certificate store, or NULL to use default
+ * @v issuer Issuer
+ * @v serial Serial number
+ * @ret cert X.509 certificate, or NULL if not found
+ */
+struct x509_certificate *
+x509_find_issuer_serial ( struct x509_chain *store,
+ const struct asn1_cursor *issuer,
+ const struct asn1_cursor *serial ) {
+ struct x509_link *link;
+ struct x509_certificate *cert;
+
+ /* Use default certificate store if none specified */
+ if ( ! store )
+ store = &certstore;
+
+ /* Scan through certificate list */
+ list_for_each_entry ( link, &store->links, list ) {
+
+ /* Check issuer and serial number */
+ cert = link->cert;
+ if ( ( asn1_compare ( issuer, &cert->issuer.raw ) == 0 ) &&
+ ( asn1_compare ( serial, &cert->serial.raw ) == 0 ) )
+ return x509_found ( store, cert );
+ }
+
+ return NULL;
+}
+
+/**
+ * Identify X.509 certificate by corresponding public key
+ *
+ * @v store Certificate store, or NULL to use default
+ * @v key Private key
+ * @ret cert X.509 certificate, or NULL if not found
+ */
+struct x509_certificate * x509_find_key ( struct x509_chain *store,
+ struct private_key *key ) {
+ struct x509_link *link;
+ struct x509_certificate *cert;
+
+ /* Use default certificate store if none specified */
+ if ( ! store )
+ store = &certstore;
+
+ /* Scan through certificate list */
+ list_for_each_entry ( link, &store->links, list ) {
+
+ /* Check public key */
+ cert = link->cert;
+ if ( pubkey_match ( cert->signature_algorithm->pubkey,
+ privkey_cursor ( key ),
+ &cert->subject.public_key.raw ) == 0 )
+ return x509_found ( store, cert );
}
return NULL;
@@ -1739,13 +1859,13 @@ x509_find_subject ( struct x509_chain *certs,
* Append X.509 certificates to X.509 certificate chain
*
* @v chain X.509 certificate chain
- * @v certs X.509 certificate list
+ * @v store Certificate store, or NULL to use default
* @ret rc Return status code
*
* Certificates will be automatically appended to the chain based upon
* the subject and issuer names.
*/
-int x509_auto_append ( struct x509_chain *chain, struct x509_chain *certs ) {
+int x509_auto_append ( struct x509_chain *chain, struct x509_chain *store ) {
struct x509_certificate *cert;
struct x509_certificate *previous;
int rc;
@@ -1762,7 +1882,7 @@ int x509_auto_append ( struct x509_chain *chain, struct x509_chain *certs ) {
/* Find issuing certificate */
previous = cert;
- cert = x509_find_subject ( certs, &cert->issuer.raw );
+ cert = x509_find_subject ( store, &cert->issuer.raw );
if ( ! cert )
break;
if ( cert == previous )
@@ -1791,10 +1911,6 @@ int x509_validate_chain ( struct x509_chain *chain, time_t time,
struct x509_link *link;
int rc;
- /* Use default certificate store if none specified */
- if ( ! store )
- store = &certstore;
-
/* Append any applicable certificates from the certificate store */
if ( ( rc = x509_auto_append ( chain, store ) ) != 0 )
return rc;