summaryrefslogtreecommitdiffstats
path: root/src/crypto/asn1.c
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/asn1.c
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/asn1.c')
-rw-r--r--src/crypto/asn1.c372
1 files changed, 260 insertions, 112 deletions
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