]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Add DER image format
authorMichael Brown <mcb30@ipxe.org>
Thu, 28 Jul 2016 15:18:23 +0000 (16:18 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 29 Jul 2016 00:12:58 +0000 (01:12 +0100)
Add DER-encoded ASN.1 as an image format.  There is no fixed signature
for DER files.  We treat an image as DER if it comprises a single
valid SEQUENCE object covering the entire length of the image.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/config/config.c
src/config/general.h
src/image/der.c [new file with mode: 0644]
src/include/ipxe/der.h [new file with mode: 0644]
src/include/ipxe/errfile.h
src/tests/asn1_test.c [new file with mode: 0644]
src/tests/asn1_test.h [new file with mode: 0644]
src/tests/der_test.c [new file with mode: 0644]
src/tests/tests.c

index e24cfe0d050a83c1443d901b4763c3546e846752..acdbebaac37388c4a88087e021f902dea26e8c18 100644 (file)
@@ -188,6 +188,9 @@ REQUIRE_OBJECT ( pnm );
 #ifdef IMAGE_PNG
 REQUIRE_OBJECT ( png );
 #endif
+#ifdef IMAGE_DER
+REQUIRE_OBJECT ( der );
+#endif
 
 /*
  * Drag in all requested commands
index a71ba726fc21ffe3694a6db514bb111c1370434f..6ff4b74a4a238227034824083dd09e8c80c96c1c 100644 (file)
@@ -112,6 +112,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 //#define      IMAGE_SDI               /* SDI image support */
 //#define      IMAGE_PNM               /* PNM image support */
 //#define      IMAGE_PNG               /* PNG image support */
+//#define      IMAGE_DER               /* DER image support */
 
 /*
  * Command-line commands to include
diff --git a/src/image/der.c b/src/image/der.c
new file mode 100644 (file)
index 0000000..fa17e56
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2016 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 );
+
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/asn1.h>
+#include <ipxe/der.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/image.h>
+
+/** @file
+ *
+ * DER-encoded ASN.1 data
+ *
+ */
+
+/**
+ * Extract ASN.1 object from image
+ *
+ * @v image            DER image
+ * @v offset           Offset within image
+ * @v cursor           ASN.1 cursor to fill in
+ * @ret next           Offset to next image, or negative error
+ *
+ * The caller is responsible for eventually calling free() on the
+ * allocated ASN.1 cursor.
+ */
+static int der_asn1 ( struct image *image, size_t offset __unused,
+                     struct asn1_cursor **cursor ) {
+       void *data;
+
+       /* Allocate cursor and data buffer */
+       *cursor = malloc ( sizeof ( **cursor ) + image->len );
+       if ( ! *cursor )
+               return -ENOMEM;
+       data = ( ( ( void * ) *cursor ) + sizeof ( **cursor ) );
+
+       /* Populate cursor and data buffer */
+       (*cursor)->data = data;
+       (*cursor)->len = image->len;
+       copy_from_user ( data, image->data, 0, image->len );
+
+       return image->len;
+}
+
+/**
+ * Probe DER image
+ *
+ * @v image            DER image
+ * @ret rc             Return status code
+ */
+static int der_probe ( struct image *image ) {
+       struct asn1_cursor cursor;
+       uint8_t buf[8];
+       size_t extra;
+       size_t total;
+       int len;
+       int rc;
+
+       /* Sanity check: no realistic DER image can be smaller than this */
+       if ( image->len < sizeof ( buf ) )
+               return -ENOEXEC;
+
+       /* Prepare partial cursor */
+       cursor.data = buf;
+       cursor.len = sizeof ( buf );
+       copy_from_user ( buf, image->data, 0, sizeof ( buf ) );
+       extra = ( image->len - sizeof ( buf ) );
+
+       /* Get length of ASN.1 sequence */
+       len = asn1_start ( &cursor, ASN1_SEQUENCE, extra );
+       if ( len < 0 ) {
+               rc = len;
+               DBGC ( image, "DER %s is not valid ASN.1: %s\n",
+                      image->name, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Add length of tag and length bytes consumed by asn1_start() */
+       total = ( len + ( cursor.data - ( ( void * ) buf ) ) );
+       assert ( total <= image->len );
+
+       /* Check that image comprises a single well-formed ASN.1 object */
+       if ( total != image->len ) {
+               DBGC ( image, "DER %s is not single ASN.1\n", image->name );
+               return -ENOEXEC;
+       }
+
+       return 0;
+}
+
+/** DER image type */
+struct image_type der_image_type __image_type ( PROBE_NORMAL ) = {
+       .name = "DER",
+       .probe = der_probe,
+       .asn1 = der_asn1,
+};
diff --git a/src/include/ipxe/der.h b/src/include/ipxe/der.h
new file mode 100644 (file)
index 0000000..c63bd97
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IPXE_DER_H
+#define _IPXE_DER_H
+
+/** @file
+ *
+ * DER image format
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/image.h>
+
+extern struct image_type der_image_type __image_type ( PROBE_NORMAL );
+
+#endif /* _IPXE_DER_H */
index f743dae6ff365e9cf700fad2510eb4dbfabc99ed..61e208a5270bc2655fc48dc3985576ce3fa5755b 100644 (file)
@@ -276,6 +276,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_embedded             ( ERRFILE_IMAGE | 0x00050000 )
 #define ERRFILE_pnm                  ( ERRFILE_IMAGE | 0x00060000 )
 #define ERRFILE_png                  ( ERRFILE_IMAGE | 0x00070000 )
+#define ERRFILE_der                  ( ERRFILE_IMAGE | 0x00080000 )
 
 #define ERRFILE_asn1                 ( ERRFILE_OTHER | 0x00000000 )
 #define ERRFILE_chap                 ( ERRFILE_OTHER | 0x00010000 )
diff --git a/src/tests/asn1_test.c b/src/tests/asn1_test.c
new file mode 100644 (file)
index 0000000..df3f01b
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 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
+ *
+ * ASN.1 self-tests
+ *
+ */
+
+/* Forcibly enable assertions */
+#undef NDEBUG
+
+#include <stdlib.h>
+#include <assert.h>
+#include <ipxe/image.h>
+#include <ipxe/asn1.h>
+#include <ipxe/test.h>
+#include "asn1_test.h"
+
+/**
+ * Report ASN.1 test result
+ *
+ * @v test             ASN.1 test
+ * @v file             Test code file
+ * @v line             Test code line
+ */
+void asn1_okx ( struct asn1_test *test, const char *file, unsigned int line ) {
+       struct digest_algorithm *digest = &asn1_test_digest_algorithm;
+       struct asn1_cursor *cursor;
+       uint8_t ctx[digest->ctxsize];
+       uint8_t out[ASN1_TEST_DIGEST_SIZE];
+       unsigned int i;
+       size_t offset;
+       int next;
+
+       /* Sanity check */
+       assert ( sizeof ( out ) == digest->digestsize );
+
+       /* Correct image data pointer */
+       test->image->data = virt_to_user ( ( void * ) test->image->data );
+
+       /* Check that image is detected as correct type */
+       okx ( register_image ( test->image ) == 0, file, line );
+       okx ( test->image->type == test->type, file, line );
+
+       /* Check that all ASN.1 objects can be extracted */
+       for ( offset = 0, i = 0 ; i < test->count ; offset = next, i++ ) {
+
+               /* Extract ASN.1 object */
+               next = image_asn1 ( test->image, offset, &cursor );
+               okx ( next >= 0, file, line );
+               okx ( ( ( size_t ) next ) > offset, file, line );
+               if ( next > 0 ) {
+
+                       /* Calculate digest of ASN.1 object */
+                       digest_init ( digest, ctx );
+                       digest_update ( digest, ctx, cursor->data,
+                                       cursor->len );
+                       digest_final ( digest, ctx, out );
+
+                       /* Compare against expected digest */
+                       okx ( memcmp ( out, test->expected[i].digest,
+                                      sizeof ( out ) ) == 0, file, line );
+
+                       /* Free ASN.1 object */
+                       free ( cursor );
+               }
+       }
+
+       /* Check that we have reached the end of the image */
+       okx ( offset == test->image->len, file, line );
+
+       /* Unregister image */
+       unregister_image ( test->image );
+}
diff --git a/src/tests/asn1_test.h b/src/tests/asn1_test.h
new file mode 100644 (file)
index 0000000..c8167ed
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef _ASN1_TEST_H
+#define _ASN1_TEST_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <ipxe/image.h>
+#include <ipxe/sha1.h>
+#include <ipxe/test.h>
+
+/** Digest algorithm used for ASN.1 tests */
+#define asn1_test_digest_algorithm sha1_algorithm
+
+/** Digest size used for ASN.1 tests */
+#define ASN1_TEST_DIGEST_SIZE SHA1_DIGEST_SIZE
+
+/** An ASN.1 test digest */
+struct asn1_test_digest {
+       /** Digest value */
+       uint8_t digest[ASN1_TEST_DIGEST_SIZE];
+};
+
+/** An ASN.1 test */
+struct asn1_test {
+       /** Image type */
+       struct image_type *type;
+       /** Source image */
+       struct image *image;
+       /** Expected digests of ASN.1 objects */
+       struct asn1_test_digest *expected;
+       /** Number of ASN.1 objects */
+       unsigned int count;
+};
+
+/**
+ * Define an ASN.1 test
+ *
+ * @v _name            Test name
+ * @v _type            Test image file type
+ * @v _file            Test image file data
+ * @v ...              Expected ASN.1 object digests
+ * @ret test           ASN.1 test
+ */
+#define ASN1( _name, _type, _file, ... )                               \
+       static const char _name ## __file[] = _file;                    \
+       static struct image _name ## __image = {                        \
+               .refcnt = REF_INIT ( ref_no_free ),                     \
+               .name = #_name,                                         \
+               .data = ( userptr_t ) ( _name ## __file ),              \
+               .len = sizeof ( _name ## __file ),                      \
+       };                                                              \
+       static struct asn1_test_digest _name ## _expected[] = {         \
+               __VA_ARGS__                                             \
+       };                                                              \
+       static struct asn1_test _name = {                               \
+               .type = _type,                                          \
+               .image = & _name ## __image,                            \
+               .expected = _name ## _expected,                         \
+               .count = ( sizeof ( _name ## _expected ) /              \
+                          sizeof ( _name ## _expected[0] ) ),          \
+       };
+
+extern void asn1_okx ( struct asn1_test *test, const char *file,
+                      unsigned int line );
+
+/**
+ * Report ASN.1 test result
+ *
+ * @v test             ASN.1 test
+ */
+#define asn1_ok( test ) asn1_okx ( test, __FILE__, __LINE__ )
+
+#endif /* _ASN1_TEST_H */
diff --git a/src/tests/der_test.c b/src/tests/der_test.c
new file mode 100644 (file)
index 0000000..00cc644
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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
+ *
+ * DER self-tests
+ *
+ */
+
+/* Forcibly enable assertions */
+#undef NDEBUG
+
+#include <string.h>
+#include <assert.h>
+#include <ipxe/test.h>
+#include <ipxe/der.h>
+#include "asn1_test.h"
+
+/** Define inline data */
+#define DATA(...) { __VA_ARGS__ }
+
+/** Define inline expected digest */
+#define DIGEST(...) { { __VA_ARGS__ } }
+
+/** 32-bit RSA private key */
+ASN1 ( rsa32, &der_image_type,
+       DATA ( 0x30, 0x2c, 0x02, 0x01, 0x00, 0x02, 0x05, 0x00, 0xb7, 0x56,
+             0x5c, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x04, 0x66,
+             0xa4, 0xc4, 0x35, 0x02, 0x03, 0x00, 0xda, 0x9f, 0x02, 0x03,
+             0x00, 0xd6, 0xaf, 0x02, 0x02, 0x01, 0x59, 0x02, 0x02, 0x4e,
+             0xe1, 0x02, 0x03, 0x00, 0xa6, 0x5a ),
+       DIGEST ( 0x82, 0x66, 0x24, 0xd9, 0xc3, 0x98, 0x1e, 0x5e, 0x56, 0xed,
+               0xd0, 0xd0, 0x2a, 0x5e, 0x9c, 0x3a, 0x58, 0xdf, 0x76, 0x0d ) );
+
+/** 64-bit RSA private key */
+ASN1 ( rsa64, &der_image_type,
+       DATA ( 0x30, 0x3e, 0x02, 0x01, 0x00, 0x02, 0x09, 0x00, 0xa1, 0xba,
+             0xb5, 0x70, 0x00, 0x89, 0xc0, 0x43, 0x02, 0x03, 0x01, 0x00,
+             0x01, 0x02, 0x08, 0x43, 0x98, 0xc6, 0x3c, 0x5f, 0xdc, 0x98,
+             0x01, 0x02, 0x05, 0x00, 0xcf, 0x91, 0x1c, 0x5d, 0x02, 0x05,
+             0x00, 0xc7, 0x77, 0x85, 0x1f, 0x02, 0x05, 0x00, 0xbc, 0xb3,
+             0x33, 0x91, 0x02, 0x04, 0x1b, 0xf9, 0x38, 0x13, 0x02, 0x04,
+             0x19, 0xf2, 0x58, 0x86 ),
+       DIGEST ( 0xee, 0x17, 0x32, 0x31, 0xf0, 0x3d, 0xfd, 0xaa, 0x9b, 0x47,
+               0xaf, 0x7b, 0x4b, 0x52, 0x0b, 0xb1, 0xab, 0x25, 0x3f, 0x11 ) );
+
+/**
+ * Perform DER self-test
+ *
+ */
+static void der_test_exec ( void ) {
+
+       /* Perform tests */
+       asn1_ok ( &rsa32 );
+       asn1_ok ( &rsa64 );
+}
+
+/** DER self-test */
+struct self_test der_test __self_test = {
+       .name = "der",
+       .exec = der_test_exec,
+};
index 0ec885f4f07ecf49c582c1c5ec65b17889569e32..b9679b49eceeb7a68795c638dd824dd319488a53 100644 (file)
@@ -69,3 +69,4 @@ REQUIRE_OBJECT ( pccrc_test );
 REQUIRE_OBJECT ( linebuf_test );
 REQUIRE_OBJECT ( iobuf_test );
 REQUIRE_OBJECT ( bitops_test );
+REQUIRE_OBJECT ( der_test );