Skip to content

Commit

Permalink
[crypto] Add DER image format
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
mcb30 committed Jul 29, 2016
1 parent ef50608 commit eb7188d
Show file tree
Hide file tree
Showing 9 changed files with 396 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/config/config.c
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/config/general.h
Expand Up @@ -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
Expand Down
120 changes: 120 additions & 0 deletions src/image/der.c
@@ -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,
};
16 changes: 16 additions & 0 deletions src/include/ipxe/der.h
@@ -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 */
1 change: 1 addition & 0 deletions src/include/ipxe/errfile.h
Expand Up @@ -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 )
Expand Down
97 changes: 97 additions & 0 deletions src/tests/asn1_test.c
@@ -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 );
}
73 changes: 73 additions & 0 deletions src/tests/asn1_test.h
@@ -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 */

0 comments on commit eb7188d

Please sign in to comment.