diff --git a/src/hci/commands/image_trust_cmd.c b/src/hci/commands/image_trust_cmd.c index b34378f93..5054f112b 100644 --- a/src/hci/commands/image_trust_cmd.c +++ b/src/hci/commands/image_trust_cmd.c @@ -161,6 +161,69 @@ static int imgverify_exec ( int argc, char **argv ) { return rc; } +/** "imgdigest" options */ +struct imgdigest_options { + /** Download timeout */ + unsigned long timeout; +}; + +/** "imgdigest" option list */ +static struct option_descriptor imgdigest_opts[] = { + OPTION_DESC ( "timeout", 't', required_argument, + struct imgdigest_options, timeout, parse_timeout), +}; + +/** "imgdigest" command descriptor */ +static struct command_descriptor imgdigest_cmd = + COMMAND_DESC ( struct imgdigest_options, imgdigest_opts, 3, 3, + " " ); + +/** + * The "imgdigest" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int imgdigest_exec ( int argc, char **argv ) { + struct imgdigest_options opts; + struct image *image; + const char *image_name_uri; + const char *digest_name; + const char *digest; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &imgdigest_cmd, &opts ) ) != 0 ) + return rc; + + /* Parse image name/URI string */ + image_name_uri = argv[optind]; + + /* Parse digest name */ + digest_name = argv[ optind + 1 ]; + + /* Parse digest string */ + digest = argv[ optind + 2 ]; + + /* Acquire the image */ + if ( ( rc = imgacquire ( image_name_uri, opts.timeout, &image ) ) != 0 ) + goto err_acquire_image; + + /* Verify image */ + if ( ( rc = imgverifydigest ( image, digest_name, digest ) ) != 0 ) { + printf ( "Could not verify: %s\n", strerror ( rc ) ); + goto err_verify; + } + + /* Success */ + rc = 0; + + err_verify: + err_acquire_image: + return rc; +} + /** Image trust management commands */ struct command image_trust_commands[] __command = { { @@ -171,4 +234,8 @@ struct command image_trust_commands[] __command = { .name = "imgverify", .exec = imgverify_exec, }, + { + .name = "imgdigest", + .exec = imgdigest_exec, + }, }; diff --git a/src/include/usr/imgtrust.h b/src/include/usr/imgtrust.h index 414e07a80..e3cb696aa 100644 --- a/src/include/usr/imgtrust.h +++ b/src/include/usr/imgtrust.h @@ -14,4 +14,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int imgverify ( struct image *image, struct image *signature, const char *name ); +extern int imgverifydigest ( struct image *image, const char *digest_name, + const char *digest ); + #endif /* _USR_IMGTRUST_H */ diff --git a/src/usr/imgtrust.c b/src/usr/imgtrust.c index 7f7e7ed14..d1dad23b8 100644 --- a/src/usr/imgtrust.c +++ b/src/usr/imgtrust.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -91,3 +92,109 @@ int imgverify ( struct image *image, struct image *signature, image->name, strerror ( rc ) ); return rc; } + + +/** + * Calculate digest of user data + * + * @v digest Digest alogorithm + * @v data Data to digest + * @v len Length of data + * @v out Digest output + */ +static void digest_user_data ( struct digest_algorithm *digest, userptr_t data, + size_t len, void *out ) { + uint8_t ctx[ digest->ctxsize ]; + uint8_t block[ digest->blocksize ]; + size_t offset = 0; + size_t frag_len; + + /* Initialise 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_final ( digest, ctx, out ); +} + + +/** + * Identify a digest algorithm by name + * + * @v name Digest name + + * @ret digest Digest algorithm, or NULL + */ +static struct digest_algorithm * +find_digest_algorithm ( const char *name ) { + struct asn1_algorithm *algorithm; + + for_each_table_entry ( algorithm, ASN1_ALGORITHMS ) { + if ( strcmp ( algorithm->name, name ) == 0 + && algorithm->digest ) + return algorithm->digest; + } + + return NULL; +} + +/** + * Verify image using the supplied digest + * + * @v image Image to verify + * @v digest_name Name of digest algorithm to use + * @v hex Hex encoded digest + * @ret rc Return status code + */ +int imgverifydigest ( struct image *image, const char *digest_name, + const char *hex ) { + struct digest_algorithm *digest; + uint8_t in[ base16_decoded_max_len ( hex ) ]; + uint8_t out[ base16_decoded_max_len ( hex ) ]; + int rc; + + /* Mark image as untrusted */ + image_untrust ( image ); + + /* Parse digest name */ + if ( ! ( digest = find_digest_algorithm ( digest_name ) ) ) { + syslog ( LOG_ERR, "Invalid digest name: %s\n", digest_name ); + rc = -EINVAL; + goto err_verify; + } + + /* Parse hex input digest */ + if ( base16_decode ( hex, in ) != (int) digest->digestsize ) { + syslog ( LOG_ERR, "Invalid digest: %s %s\n", digest_name, hex ); + rc = -EINVAL; + goto err_verify; + } + + /* Verify digest */ + digest_user_data ( digest, image->data, image->len, out ); + if ( memcmp ( in, out, digest->digestsize ) != 0 ) { + rc = -EINVAL; + goto err_verify; + } + + /* Mark image as trusted */ + image_trust ( image ); + syslog ( LOG_NOTICE, "Image \"%s\" digest OK\n", image->name ); + + return 0; + + err_verify: + syslog ( LOG_ERR, "Image \"%s\" digest bad: %s\n", + image->name, strerror ( rc ) ); + return rc; +}