/** * Finalise HMAC * * @v digest Digest algorithm to use * @v digest_ctx Digest context * @v key Key * @v key_len Length of key * @v hmac HMAC digest to fill in * * The length of the key should be less than the block size of the * digest algorithm being used. (If the key length is greater, it * will be replaced with its own digest, and key_len will be updated * accordingly). */ void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, void *key, size_t *key_len, void *hmac ) { unsigned char k_opad[digest->blocksize]; unsigned int i; /* Reduce key if necessary */ if ( *key_len > sizeof ( k_opad ) ) hmac_reduce_key ( digest, key, key_len ); /* Construct output pad */ memset ( k_opad, 0, sizeof ( k_opad ) ); memcpy ( k_opad, key, *key_len ); for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) { k_opad[i] ^= 0x5c; } /* Finish inner hash */ digest_final ( digest, digest_ctx, hmac ); /* Perform outer hash */ digest_init ( digest, digest_ctx ); digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) ); digest_update ( digest, digest_ctx, hmac, digest->digestsize ); digest_final ( digest, digest_ctx, hmac ); }
/** * Build OCSP request * * @v ocsp OCSP check * @ret rc Return status code */ static int ocsp_request ( struct ocsp_check *ocsp ) { struct digest_algorithm *digest = &ocsp_digest_algorithm; struct asn1_builder *builder = &ocsp->request.builder; struct asn1_cursor *cert_id_tail = &ocsp->request.cert_id_tail; uint8_t digest_ctx[digest->ctxsize]; uint8_t name_digest[digest->digestsize]; uint8_t pubkey_digest[digest->digestsize]; int rc; /* Generate digests */ digest_init ( digest, digest_ctx ); digest_update ( digest, digest_ctx, ocsp->cert->issuer.raw.data, ocsp->cert->issuer.raw.len ); 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 ); digest_final ( digest, digest_ctx, pubkey_digest ); /* Construct request */ if ( ( rc = ( asn1_prepend_raw ( builder, ocsp->cert->serial.raw.data, ocsp->cert->serial.raw.len ), asn1_prepend ( builder, ASN1_OCTET_STRING, pubkey_digest, sizeof ( pubkey_digest ) ), asn1_prepend ( builder, ASN1_OCTET_STRING, name_digest, sizeof ( name_digest ) ), asn1_prepend ( builder, ASN1_SEQUENCE, ocsp_algorithm_id, sizeof ( ocsp_algorithm_id ) ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ), asn1_wrap ( builder, ASN1_SEQUENCE ) ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" could not build request: %s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); return rc; } DBGC2 ( ocsp, "OCSP %p \"%s\" request is:\n", ocsp, x509_name ( ocsp->cert ) ); DBGC2_HDA ( ocsp, 0, builder->data, builder->len ); /* Parse certificate ID for comparison with response */ cert_id_tail->data = builder->data; cert_id_tail->len = builder->len; if ( ( rc = ( asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_enter ( cert_id_tail, ASN1_SEQUENCE ), asn1_skip ( cert_id_tail, ASN1_SEQUENCE ) ) ) != 0 ) { DBGC ( ocsp, "OCSP %p \"%s\" could not locate certID: %s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc ) ); return rc; } return 0; }
/** * Compare responder's certificate public key hash * * @v ocsp OCSP check * @v cert Certificate * @ret difference Difference as returned by memcmp() */ static int ocsp_compare_responder_key_hash ( struct ocsp_check *ocsp, struct x509_certificate *cert ) { struct ocsp_responder *responder = &ocsp->response.responder; struct asn1_cursor key_hash; uint8_t ctx[SHA1_CTX_SIZE]; uint8_t digest[SHA1_DIGEST_SIZE]; int difference; /* Enter responder key hash */ memcpy ( &key_hash, &responder->id, sizeof ( key_hash ) ); asn1_enter ( &key_hash, ASN1_OCTET_STRING ); /* Sanity check */ difference = ( sizeof ( digest ) - key_hash.len ); if ( difference ) return difference; /* 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 ); digest_final ( &sha1_algorithm, ctx, digest ); /* Compare responder key hash with hash of certificate's public key */ return memcmp ( digest, key_hash.data, sizeof ( digest ) ); }
/** * Calculate digest algorithm cost * * @v digest Digest algorithm * @ret cost Cost (in cycles per byte) */ unsigned long digest_cost ( struct digest_algorithm *digest ) { static uint8_t random[8192]; /* Too large for stack */ uint8_t ctx[digest->ctxsize]; uint8_t out[digest->digestsize]; struct profiler profiler; unsigned long cost; unsigned int i; /* Fill buffer with pseudo-random data */ srand ( 0x1234568 ); for ( i = 0 ; i < sizeof ( random ) ; i++ ) random[i] = rand(); /* Profile digest calculation */ memset ( &profiler, 0, sizeof ( profiler ) ); for ( i = 0 ; i < PROFILE_COUNT ; i++ ) { profile_start ( &profiler ); digest_init ( digest, ctx ); digest_update ( digest, ctx, random, sizeof ( random ) ); digest_final ( digest, ctx, out ); profile_stop ( &profiler ); } /* Round to nearest whole number of cycles per byte */ cost = ( ( profile_mean ( &profiler ) + ( sizeof ( random ) / 2 ) ) / sizeof ( random ) ); return cost; }
/** * Test digest algorithm * * @v digest Digest algorithm * @v fragments Digest test fragment list, or NULL * @v data Test data * @v len Length of test data * @v expected Expected digest value * @ret ok Digest value is as expected */ int digest_test ( struct digest_algorithm *digest, struct digest_test_fragments *fragments, void *data, size_t len, void *expected ) { uint8_t ctx[digest->ctxsize]; uint8_t out[digest->digestsize]; size_t frag_len = 0; unsigned int i; /* Initialise digest */ digest_init ( digest, ctx ); /* Update digest fragment-by-fragment */ for ( i = 0 ; len && ( i < ( sizeof ( fragments->len ) / sizeof ( fragments->len[0] ) ) ) ; i++ ) { if ( fragments ) frag_len = fragments->len[i]; if ( ( frag_len == 0 ) || ( frag_len < len ) ) frag_len = len; digest_update ( digest, ctx, data, frag_len ); data += frag_len; len -= frag_len; } /* Finalise digest */ digest_final ( digest, ctx, out ); /* Compare against expected output */ return ( memcmp ( expected, out, sizeof ( out ) ) == 0 ); }
/** * Report server passphrase test result * * @v test Content information segment test * @v info Content information * @v pass Server passphrase * @v pass_len Length of server passphrase * @v file Test code file * @v line Test code line */ static void peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test, const struct peerdist_info *info, uint8_t *pass, size_t pass_len, const char *file, unsigned int line ) { struct digest_algorithm *digest = info->digest; uint8_t ctx[digest->ctxsize]; uint8_t secret[digest->digestsize]; uint8_t expected[digest->digestsize]; size_t digestsize = info->digestsize; size_t secretsize = digestsize; /* Calculate server secret */ digest_init ( digest, ctx ); digest_update ( digest, ctx, pass, pass_len ); digest_final ( digest, ctx, secret ); /* Calculate expected segment secret */ hmac_init ( digest, ctx, secret, &secretsize ); assert ( secretsize == digestsize ); hmac_update ( digest, ctx, test->expected_hash, digestsize ); hmac_final ( digest, ctx, secret, &secretsize, expected ); assert ( secretsize == digestsize ); /* Verify segment secret */ okx ( memcmp ( test->expected_secret, expected, digestsize ) == 0, file, line ); }
/** * Finalise HTTP Digest * * @v ctx Digest context * @v out Buffer for digest output * @v len Buffer length */ static void http_digest_final ( struct md5_context *ctx, char *out, size_t len ) { uint8_t digest[MD5_DIGEST_SIZE]; /* Finalise and base16-encode MD5 digest */ digest_final ( &md5_algorithm, ctx, digest ); base16_encode ( digest, sizeof ( digest ), out, len ); }
/** * Reduce HMAC key length * * @v digest Digest algorithm to use * @v digest_ctx Digest context * @v key Key * @v key_len Length of key */ static void hmac_reduce_key ( struct digest_algorithm *digest, void *key, size_t *key_len ) { uint8_t digest_ctx[digest->ctxsize]; digest_init ( digest, digest_ctx ); digest_update ( digest, digest_ctx, key, *key_len ); digest_final ( digest, digest_ctx, key ); *key_len = digest->digestsize; }
/** * The "digest" command * * @v argc Argument count * @v argv Argument list * @v digest Digest algorithm * @ret rc Exit code */ static int digest_exec ( int argc, char **argv, struct digest_algorithm *digest ) { const char *image_name; struct image *image; uint8_t digest_ctx[digest->ctxsize]; uint8_t digest_out[digest->digestsize]; uint8_t buf[128]; size_t offset; size_t len; size_t frag_len; int i; unsigned j; if ( argc < 2 || !strcmp ( argv[1], "--help" ) || !strcmp ( argv[1], "-h" ) ) { digest_syntax ( argv ); return 1; } for ( i = 1 ; i < argc ; i++ ) { image_name = argv[i]; /* find image */ image = find_image ( image_name ); if ( ! image ) { printf ( "No such image: %s\n", image_name ); continue; } offset = 0; len = image->len; /* calculate digest */ digest_init ( digest, digest_ctx ); while ( len ) { frag_len = len; if ( frag_len > sizeof ( buf ) ) frag_len = sizeof ( buf ); copy_from_user ( buf, image->data, offset, frag_len ); digest_update ( digest, digest_ctx, buf, frag_len ); len -= frag_len; offset += frag_len; } digest_final ( digest, digest_ctx, digest_out ); for ( j = 0 ; j < sizeof ( digest_out ) ; j++ ) printf ( "%02x", digest_out[j] ); printf ( " %s\n", image->name ); } return 0; }
/** * Check OCSP response signature * * @v ocsp OCSP check * @v signer Signing certificate * @ret rc Return status code */ static int ocsp_check_signature ( struct ocsp_check *ocsp, struct x509_certificate *signer ) { 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; uint8_t digest_ctx[ digest->ctxsize ]; uint8_t digest_out[ digest->digestsize ]; uint8_t pubkey_ctx[ pubkey->ctxsize ]; int rc; /* Generate digest */ digest_init ( digest, digest_ctx ); digest_update ( digest, digest_ctx, response->tbs.data, 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 ) { DBGC ( ocsp, "OCSP %p \"%s\" signature verification failed: " "%s\n", ocsp, x509_name ( ocsp->cert ), strerror ( rc )); goto err_verify; } 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; }
/** * Distribute entropy throughout a buffer * * @v hash Underlying hash algorithm * @v input Input data * @v input_len Length of input data, in bytes * @v output Output buffer * @v output_len Length of output buffer, in bytes * * This is the Hash_df function defined in ANS X9.82 Part 3-2007 * Section 10.5.2 (NIST SP 800-90 Section 10.4.1). * * The number of bits requested is implicit in the length of the * output buffer. Requests must be for an integral number of bytes. * * The output buffer is filled incrementally with each iteration of * the central loop, rather than constructing an overall "temp" and * then taking the leftmost(no_of_bits_to_return) bits. * * There is no way for the Hash_df function to fail. The returned * status SUCCESS is implicit. */ void hash_df ( struct digest_algorithm *hash, const void *input, size_t input_len, void *output, size_t output_len ) { uint8_t context[hash->ctxsize]; uint8_t digest[hash->digestsize]; size_t frag_len; struct { uint8_t pad[3]; uint8_t counter; uint32_t no_of_bits_to_return; } __attribute__ (( packed )) prefix; void *temp; size_t remaining; DBGC ( &hash_df, "HASH_DF input:\n" ); DBGC_HDA ( &hash_df, 0, input, input_len ); /* Sanity checks */ assert ( input != NULL ); assert ( output != NULL ); /* 1. temp = the Null string * 2. len = ceil ( no_of_bits_to_return / outlen ) * * (Nothing to do. We fill the output buffer incrementally, * rather than constructing the complete "temp" in-memory. * "len" is implicit in the number of iterations required to * fill the output buffer, and so is not calculated * explicitly.) */ /* 3. counter = an 8-bit binary value representing the integer "1" */ prefix.counter = 1; /* 4. For i = 1 to len do */ for ( temp = output, remaining = output_len ; remaining > 0 ; ) { /* Comment: in step 5.1 (sic), no_of_bits_to_return is * used as a 32-bit string. * * 4.1 temp = temp || Hash ( counter || no_of_bits_to_return * || input_string ) */ prefix.no_of_bits_to_return = htonl ( output_len * 8 ); digest_init ( hash, context ); digest_update ( hash, context, &prefix.counter, ( sizeof ( prefix ) - offsetof ( typeof ( prefix ), counter ) ) ); digest_update ( hash, context, input, input_len ); digest_final ( hash, context, digest ); /* 4.2 counter = counter + 1 */ prefix.counter++; /* 5. requested_bits = Leftmost ( no_of_bits_to_return ) * of temp * * (We fill the output buffer incrementally.) */ frag_len = sizeof ( digest ); if ( frag_len > remaining ) frag_len = remaining; memcpy ( temp, digest, frag_len ); temp += frag_len; remaining -= frag_len; } /* 6. Return SUCCESS and requested_bits */ DBGC ( &hash_df, "HASH_DF output:\n" ); DBGC_HDA ( &hash_df, 0, output, output_len ); return; }