diff --git a/src/crypto/drbg.c b/src/crypto/drbg.c new file mode 100644 index 00000000..68aa7857 --- /dev/null +++ b/src/crypto/drbg.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * DRBG mechanism + * + * This mechanism is designed to comply with ANS X9.82 Part 3-2007 + * Section 9. This standard is not freely available, but most of the + * text appears to be shared with NIST SP 800-90, which can be + * downloaded from + * + * http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf + * + * Where possible, references are given to both documents. In the + * case of any disagreement, ANS X9.82 takes priority over NIST SP + * 800-90. (In particular, note that some algorithms that are + * Approved by NIST SP 800-90 are not Approved by ANS X9.82.) + */ + +#include +#include +#include +#include +#include + +/** + * Instantiate DRBG + * + * @v state Algorithm state to be initialised + * @v personal Personalisation string + * @v personal_len Length of personalisation string + * @ret rc Return status code + * + * This is the Instantiate_function defined in ANS X9.82 Part 3-2007 + * Section 9.2 (NIST SP 800-90 Section 9.1). + * + * Only a single security strength is supported, and prediction + * resistance is always enabled. The nonce is accounted for by + * increasing the entropy input, as per ANS X9.82 Part 3-2007 Section + * 8.4.2 (NIST SP 800-90 Section 8.6.7). + */ +int drbg_instantiate ( struct drbg_state *state, const void *personal, + size_t personal_len ) { + unsigned int entropy_bits = ( ( 3 * DRBG_SECURITY_STRENGTH + 1 ) / 2 ); + size_t min_len = DRBG_MIN_ENTROPY_LEN_BYTES; + size_t max_len = DRBG_MAX_ENTROPY_LEN_BYTES; + uint8_t data[ entropy_bufsize ( entropy_bits, min_len, max_len ) ]; + int len; + int rc; + + DBGC ( state, "DRBG %p instantiate\n", state ); + + /* Sanity checks */ + assert ( state != NULL ); + + /* 1. If requested_instantiation_security_strength > + * highest_supported_security_strength, then return an + * ERROR_FLAG + */ + if ( DRBG_SECURITY_STRENGTH > DRBG_MAX_SECURITY_STRENGTH ) { + DBGC ( state, "DRBG %p cannot support security strength %d\n", + state, DRBG_SECURITY_STRENGTH ); + return -ENOTSUP; + } + + /* 2. If prediction_resistance_flag is set, and prediction + * resistance is not supported, then return an ERROR_FLAG + * + * (Nothing to do since prediction resistance is always + * supported.) + */ + + /* 3. If the length of the personalization_string > + * max_personalization_string_length, return an ERROR_FLAG + */ + if ( personal_len > DRBG_MAX_PERSONAL_LEN_BYTES ) { + DBGC ( state, "DRBG %p personalisation string too long (%zd " + "bytes)\n", state, personal_len ); + return -ERANGE; + } + + /* 4. Set security_strength to the nearest security strength + * greater than or equal to + * requested_instantiation_security_strength. + * + * (Nothing to do since we support only a single security + * strength.) + */ + + /* 5. Using the security_strength, select appropriate DRBG + * mechanism parameters. + * + * (Nothing to do since we support only a single security + * strength.) + */ + + /* 6. ( status, entropy_input ) = Get_entropy_input ( + * security_strength, min_length, max_length, + * prediction_resistance_request ) + * 7. If an ERROR is returned in step 6, return a + * CATASTROPHIC_ERROR_FLAG. + * 8. Obtain a nonce. + */ + len = get_entropy_input ( entropy_bits, data, min_len, + sizeof ( data ) ); + if ( len < 0 ) { + rc = len; + DBGC ( state, "DRBG %p could not get entropy input: %s\n", + state, strerror ( rc ) ); + return rc; + } + assert ( len >= min_len ); + assert ( len <= sizeof ( data ) ); + + /* 9. initial_working_state = Instantiate_algorithm ( + * entropy_input, nonce, personalization_string ). + */ + drbg_instantiate_algorithm ( state, data, len, personal, personal_len ); + + /* 10. Get a state_handle for a currently empty state. If an + * empty internal state cannot be found, return an + * ERROR_FLAG. + * 11. Set the internal state indicated by state_handle to + * the initial values for the internal state (i.e. set + * the working_state to the values returned as + * initial_working_state in step 9 and any other values + * required for the working_state, and set the + * administrative information to the appropriate values. + * + * (Almost nothing to do since the memory to hold the state + * was passed in by the caller and has already been updated + * in-situ.) + */ + state->reseed_required = 0; + + /* 12. Return SUCCESS and state_handle. */ + return 0; +} + +/** + * Reseed DRBG + * + * @v state Algorithm state + * @v additional Additional input + * @v additional_len Length of additional input + * @ret rc Return status code + * + * This is the Reseed_function defined in ANS X9.82 Part 3-2007 + * Section 9.3 (NIST SP 800-90 Section 9.2). + * + * Prediction resistance is always enabled. + */ +int drbg_reseed ( struct drbg_state *state, const void *additional, + size_t additional_len ) { + unsigned int entropy_bits = DRBG_SECURITY_STRENGTH; + size_t min_len = DRBG_MIN_ENTROPY_LEN_BYTES; + size_t max_len = DRBG_MAX_ENTROPY_LEN_BYTES; + uint8_t data[ entropy_bufsize ( entropy_bits, min_len, max_len ) ]; + int len; + int rc; + + DBGC ( state, "DRBG %p reseed\n", state ); + + /* Sanity checks */ + assert ( state != NULL ); + + /* 1. Using state_handle, obtain the current internal state. + * If state_handle indicates an invalid or empty internal + * state, return an ERROR_FLAG. + * + * (Nothing to do since the memory holding the internal state + * was passed in by the caller.) + */ + + /* 2. If prediction_resistance_request is set, and + * prediction_resistance_flag is not set, then return an + * ERROR_FLAG. + * + * (Nothing to do since prediction resistance is always + * supported.) + */ + + /* 3. If the length of the additional_input > + * max_additional_input_length, return an ERROR_FLAG. + */ + if ( additional_len > DRBG_MAX_ADDITIONAL_LEN_BYTES ) { + DBGC ( state, "DRBG %p additional input too long (%zd bytes)\n", + state, additional_len ); + return -ERANGE; + } + + /* 4. ( status, entropy_input ) = Get_entropy_input ( + * security_strength, min_length, max_length, + * prediction_resistance_request ). + * + * 5. If an ERROR is returned in step 4, return a + * CATASTROPHIC_ERROR_FLAG. + */ + len = get_entropy_input ( entropy_bits, data, min_len, + sizeof ( data ) ); + if ( len < 0 ) { + rc = len; + DBGC ( state, "DRBG %p could not get entropy input: %s\n", + state, strerror ( rc ) ); + return rc; + } + + /* 6. new_working_state = Reseed_algorithm ( working_state, + * entropy_input, additional_input ). + */ + drbg_reseed_algorithm ( state, data, len, additional, additional_len ); + + /* 7. Replace the working_state in the internal state + * indicated by state_handle with the values of + * new_working_state obtained in step 6. + * + * (Nothing to do since the state has already been updated in-situ.) + */ + + /* 8. Return SUCCESS. */ + return 0; +} + +/** + * Generate pseudorandom bits using DRBG + * + * @v state Algorithm state + * @v additional Additional input + * @v additional_len Length of additional input + * @v prediction_resist Prediction resistance is required + * @v data Output buffer + * @v len Length of output buffer + * @ret rc Return status code + * + * This is the Generate_function defined in ANS X9.82 Part 3-2007 + * Section 9.4 (NIST SP 800-90 Section 9.3). + * + * Requests must be for an integral number of bytes. Only a single + * security strength is supported. Prediction resistance is supported + * if requested. + */ +int drbg_generate ( struct drbg_state *state, const void *additional, + size_t additional_len, int prediction_resist, + void *data, size_t len ) { + int rc; + + DBGC ( state, "DRBG %p generate\n", state ); + + /* Sanity checks */ + assert ( state != NULL ); + assert ( data != NULL ); + + /* 1. Using state_handle, obtain the current internal state + * for the instantiation. If state_handle indicates an + * invalid or empty internal state, then return an ERROR_FLAG. + * + * (Nothing to do since the memory holding the internal state + * was passed in by the caller.) + */ + + /* 2. If requested_number_of_bits > + * max_number_of_bits_per_request, then return an + * ERROR_FLAG. + */ + if ( len > DRBG_MAX_GENERATED_LEN_BYTES ) { + DBGC ( state, "DRBG %p request too long (%zd bytes)\n", + state, len ); + return -ERANGE; + } + + /* 3. If requested_security_strength > the security_strength + * indicated in the internal state, then return an + * ERROR_FLAG. + * + * (Nothing to do since only a single security strength is + * supported.) + */ + + /* 4. If the length of the additional_input > + * max_additional_input_length, then return an ERROR_FLAG. + */ + if ( additional_len > DRBG_MAX_ADDITIONAL_LEN_BYTES ) { + DBGC ( state, "DRBG %p additional input too long (%zd bytes)\n", + state, additional_len ); + return -ERANGE; + } + + /* 5. If prediction_resistance_request is set, and + * prediction_resistance_flag is not set, then return an + * ERROR_FLAG. + * + * (Nothing to do since prediction resistance is always + * supported.) + */ + + /* 6. Clear the reseed_required_flag. */ + state->reseed_required = 0; + + step_7: + /* 7. If reseed_required_flag is set, or if + * prediction_resistance_request is set, then + */ + if ( state->reseed_required || prediction_resist ) { + + /* 7.1 status = Reseed_function ( state_handle, + * prediction_resistance_request, + * additional_input ) + * 7.2 If status indicates an ERROR, then return + * status. + */ + if ( ( rc = drbg_reseed ( state, additional, + additional_len ) ) != 0 ) { + DBGC ( state, "DRBG %p could not reseed: %s\n", + state, strerror ( rc ) ); + return rc; + } + + /* 7.3 Using state_handle, obtain the new internal + * state. + * + * (Nothing to do since the internal state has been + * updated in-situ.) + */ + + /* 7.4 additional_input = the Null string. */ + additional = NULL; + additional_len = 0; + + /* 7.5 Clear the reseed_required_flag. */ + state->reseed_required = 0; + } + + /* 8. ( status, pseudorandom_bits, new_working_state ) = + * Generate_algorithm ( working_state, + * requested_number_of_bits, additional_input ). + */ + rc = drbg_generate_algorithm ( state, additional, additional_len, + data, len ); + + /* 9. If status indicates that a reseed is required before + * the requested bits can be generated, then + */ + if ( rc != 0 ) { + + /* 9.1 Set the reseed_required_flag. */ + state->reseed_required = 1; + + /* 9.2 If the prediction_resistance_flag is set, then + * set the prediction_resistance_request + * indication. + */ + prediction_resist = 1; + + /* 9.3 Go to step 7. */ + goto step_7; + } + + /* 10. Replace the old working_state in the internal state + * indicated by state_handle with the values of + * new_working_state. + * + * (Nothing to do since the working state has already been + * updated in-situ.) + */ + + /* 11. Return SUCCESS and pseudorandom_bits. */ + return 0; +} + +/** + * Uninstantiate DRBG + * + * @v state Algorithm state + * + * This is the Uninstantiate_function defined in ANS X9.82 Part 3-2007 + * Section 9.5 (NIST SP 800-90 Section 9.4). + */ +void drbg_uninstantiate ( struct drbg_state *state ) { + + DBGC ( state, "DRBG %p uninstantiate\n", state ); + + /* Sanity checks */ + assert ( state != NULL ); + + /* 1. If state_handle indicates an invalid state, then return + * an ERROR_FLAG. + * + * (Nothing to do since the memory holding the internal state + * was passed in by the caller.) + */ + + /* 2. Erase the contents of the internal state indicated by + * state_handle. + */ + memset ( state, 0, sizeof ( *state ) ); + + /* 3. Return SUCCESS. */ +} diff --git a/src/include/ipxe/drbg.h b/src/include/ipxe/drbg.h new file mode 100644 index 00000000..a09d136d --- /dev/null +++ b/src/include/ipxe/drbg.h @@ -0,0 +1,118 @@ +#ifndef _IPXE_DRBG_H +#define _IPXE_DRBG_H + +/** @file + * + * DRBG mechanism + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include + +/** Maximum security strength */ +#define DRBG_MAX_SECURITY_STRENGTH HMAC_DRBG_MAX_SECURITY_STRENGTH + +/** Security strength */ +#define DRBG_SECURITY_STRENGTH HMAC_DRBG_SECURITY_STRENGTH + +/** Minimum entropy input length */ +#define DRBG_MIN_ENTROPY_LEN_BYTES HMAC_DRBG_MIN_ENTROPY_LEN_BYTES + +/** Maximum entropy input length */ +#define DRBG_MAX_ENTROPY_LEN_BYTES HMAC_DRBG_MAX_ENTROPY_LEN_BYTES + +/** Maximum personalisation string length */ +#define DRBG_MAX_PERSONAL_LEN_BYTES HMAC_DRBG_MAX_PERSONAL_LEN_BYTES + +/** Maximum additional input length */ +#define DRBG_MAX_ADDITIONAL_LEN_BYTES HMAC_DRBG_MAX_ADDITIONAL_LEN_BYTES + +/** Maximum length of generated pseudorandom data per request */ +#define DRBG_MAX_GENERATED_LEN_BYTES HMAC_DRBG_MAX_GENERATED_LEN_BYTES + +/** A Deterministic Random Bit Generator */ +struct drbg_state { + /** Algorithm internal state */ + struct hmac_drbg_state internal; + /** Reseed required flag */ + int reseed_required; +}; + +/** + * Instantiate DRBG algorithm + * + * @v state Algorithm state + * @v entropy Entropy input + * @v entropy_len Length of entropy input + * @v personal Personalisation string + * @v personal_len Length of personalisation string + * + * This is the Instantiate_algorithm function defined in ANS X9.82 + * Part 3-2007 Section 9.2 (NIST SP 800-90 Section 9.1). + */ +static inline void drbg_instantiate_algorithm ( struct drbg_state *state, + const void *entropy, + size_t entropy_len, + const void *personal, + size_t personal_len ) { + hmac_drbg_instantiate ( &state->internal, entropy, entropy_len, + personal, personal_len ); +} + +/** + * Reseed DRBG algorithm + * + * @v state Algorithm state + * @v entropy Entropy input + * @v entropy_len Length of entropy input + * @v additional Additional input + * @v additional_len Length of additional input + * + * This is the Reseed_algorithm function defined in ANS X9.82 + * Part 3-2007 Section 9.3 (NIST SP 800-90 Section 9.2). + */ +static inline void drbg_reseed_algorithm ( struct drbg_state *state, + const void *entropy, + size_t entropy_len, + const void *additional, + size_t additional_len ) { + hmac_drbg_reseed ( &state->internal, entropy, entropy_len, + additional, additional_len ); +} + +/** + * Generate pseudorandom bits using DRBG algorithm + * + * @v state Algorithm state + * @v additional Additional input + * @v additional_len Length of additional input + * @v data Output buffer + * @v len Length of output buffer + * @ret rc Return status code + * + * This is the Generate_algorithm function defined in ANS X9.82 + * Part 3-2007 Section 9.4 (NIST SP 800-90 Section 9.3). + * + * Note that the only permitted error is "reseed required". + */ +static inline int drbg_generate_algorithm ( struct drbg_state *state, + const void *additional, + size_t additional_len, + void *data, size_t len ) { + return hmac_drbg_generate ( &state->internal, additional, + additional_len, data, len ); +} + +extern int drbg_instantiate ( struct drbg_state *state, const void *personal, + size_t personal_len ); +extern int drbg_reseed ( struct drbg_state *state, const void *additional, + size_t additional_len ); +extern int drbg_generate ( struct drbg_state *state, const void *additional, + size_t additional_len, int prediction_resist, + void *data, size_t len ); +extern void drbg_uninstantiate ( struct drbg_state *state ); + +#endif /* _IPXE_DRBG_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2094b78c..8f6c3641 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -243,6 +243,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_prompt ( ERRFILE_OTHER | 0x00220000 ) #define ERRFILE_nvo_cmd ( ERRFILE_OTHER | 0x00230000 ) #define ERRFILE_hmac_drbg ( ERRFILE_OTHER | 0x00240000 ) +#define ERRFILE_drbg ( ERRFILE_OTHER | 0x00250000 ) /** @} */