mirror of https://github.com/openssl/openssl.git
				
				
				
			
		
			
	
	
		
			188 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
		
		
			
		
	
	
			188 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Markdown
		
	
	
	
|  | Fetching composite algorithms and using them - adding the bits still missing | ||
|  | ============================================================================ | ||
|  | 
 | ||
|  | Quick background | ||
|  | ---------------- | ||
|  | 
 | ||
|  | We currently support - at least in the public libcrypto API - explicitly | ||
|  | fetching composite algorithms (such as AES-128-CBC or HMAC-SHA256), and | ||
|  | using them in most cases.  In some cases (symmetric ciphers), our providers | ||
|  | also provide them. | ||
|  | 
 | ||
|  | However, there is one class of algorithms where the support for *using* | ||
|  | explicitly fetched algorithms is lacking: asymmetric algorithms. | ||
|  | 
 | ||
|  | For a longer background and explanation, see | ||
|  | [Background / tl;dr](#background-tldr) at the end of this design. | ||
|  | 
 | ||
|  | Public API - Add variants of `EVP_PKEY_CTX` initializers | ||
|  | -------------------------------------------------------- | ||
|  | 
 | ||
|  | As far as this design is concerned, these API sets are affected: | ||
|  | 
 | ||
|  | - SIGNATURE (DigestSign and DigestVerify) | ||
|  | - ASYM_CIPHER | ||
|  | - KEYEXCH | ||
|  | 
 | ||
|  | The proposal is to add these functions: | ||
|  | 
 | ||
|  | ``` C | ||
|  | EVP_DigestSignInit_ex2(EVP_PKEY_CTX **pctx, | ||
|  |                        EVP_SIGNATURE *sig, EVP_PKEY *pkey, | ||
|  |                        OSSL_LIB_CTX *libctx, const OSSL_PARAM params[]); | ||
|  | EVP_DigestVerifyInit_ex2(EVP_PKEY_CTX **pctx, | ||
|  |                          EVP_SIGNATURE *sig, EVP_PKEY *pkey, | ||
|  |                          OSSL_LIB_CTX *libctx, const OSSL_PARAM params[]); | ||
|  | 
 | ||
|  | int EVP_PKEY_encrypt_init_ex2(EVP_PKEY_CTX *ctx, EVP_ASYM_CIPHER *asymciph, | ||
|  |                               const OSSL_PARAM params[]); | ||
|  | int EVP_PKEY_decrypt_init_ex2(EVP_PKEY_CTX *ctx, EVP_ASYM_CIPHER *asymciph, | ||
|  |                               const OSSL_PARAM params[]); | ||
|  | 
 | ||
|  | int EVP_PKEY_derive_init_ex2(EVP_PKEY_CTX *ctx, EVP_KEYEXCH *exchange, | ||
|  |                              const OSSL_PARAM params[]); | ||
|  | ``` | ||
|  | 
 | ||
|  | Because `EVP_SIGNATURE`, `EVP_ASYM_CIPHER` and `EVP_KEYEXCH` aren't limited | ||
|  | to composite algorithms, these functions can be used just as well with | ||
|  | explicit fetches of simple algorithms, say "RSA".  In that case, the caller | ||
|  | will need to pass necessary auxiliary parameters through the `OSSL_PARAM` or | ||
|  | a call to a corresponding `set_params` function. | ||
|  | 
 | ||
|  | Requirements on the providers | ||
|  | ----------------------------- | ||
|  | 
 | ||
|  | Because it's not immediately obvious from a composite algorithm name what | ||
|  | key type it requires / supports, at least in code, allowing the use of an | ||
|  | explicitly fetched implementation of a composite algorithm requires that | ||
|  | providers cooperate by declaring what key type is required / supported by | ||
|  | each algorithm. | ||
|  | 
 | ||
|  | For non-composite operation algorithms (like "RSA"), this is not necessary, | ||
|  | see the fallback strategies below. | ||
|  | 
 | ||
|  | There are two ways this could be implemented: | ||
|  | 
 | ||
|  | 1.  through an added provider function that would work like keymgmt's | ||
|  |     `query_operation_name` function, but would return a key type name | ||
|  |     instead: | ||
|  | 
 | ||
|  |     ``` C | ||
|  |     # define OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPE         26 | ||
|  |     OSSL_CORE_MAKE_FUNC(const char *, signature_query_key_type, (void)) | ||
|  | 
 | ||
|  |     # define OSSL_FUNC ASYM_CIPHER_QUERY_KEY_TYPE       12 | ||
|  |     OSSL_CORE_MAKE_FUNC(const char *, asym_cipher_query_key_type, (void)) | ||
|  | 
 | ||
|  |     # define OSSL_FUNC_KEYEXCH_QUERY_KEY_TYPE           11 | ||
|  |     OSSL_CORE_MAKE_FUNC(const char *, keyexch_query_key_type, (void)) | ||
|  |     ``` | ||
|  | 
 | ||
|  | 2.  through a gettable `OSSL_PARAM`, using the param identity "keytype" | ||
|  | 
 | ||
|  | Fallback strategies | ||
|  | ------------------- | ||
|  | 
 | ||
|  | Because existing providers haven't been updated to declare composite | ||
|  | algorithms, or to respond to the key type query, some fallback strategies | ||
|  | will be needed to find out if the `EVP_PKEY` key type is possible to use | ||
|  | with the fetched algorithm: | ||
|  | 
 | ||
|  | -   Check if the fetched operation name matches the key type (keymgmt name) | ||
|  |     of the `EVP_PKEY` that's involved in the operation.  For example, this | ||
|  |     is useful when someone fetched the `EVP_SIGNATURE` "RSA". | ||
|  | -   Check if the fetched algorithm name matches the name returned by the | ||
|  |     keymgmt's `query_operation_name` function.  For example, this is useful | ||
|  |     when someone fetched the `EVP_SIGNATURE` "ECDSA", for which the key type | ||
|  |     to use is "EC". | ||
|  | -   libcrypto currently has knowledge of some composite algorithm names and | ||
|  |     what they are composed of, accessible with `OBJ_find_sigid_algs` and | ||
|  |     similar functionality.  This knowledge is regarded legacy, but can be | ||
|  |     used to figure out the key type. | ||
|  | 
 | ||
|  | If none of these strategies work out, the operation initialization should | ||
|  | fail. | ||
|  | 
 | ||
|  | These strategies have their limitations, but the built-in legacy knowledge | ||
|  | we currently have in libcrypto should be enough to cover most bases. | ||
|  | 
 | ||
|  | ----- | ||
|  | 
 | ||
|  | ----- | ||
|  | 
 | ||
|  | Background / tl;dr | ||
|  | ------------------ | ||
|  | 
 | ||
|  | ### What is a composite algorithm?
 | ||
|  | 
 | ||
|  | A composite algorithm is an algorithm that's composed of more than one other | ||
|  | algorithm.  In OpenSSL parlance with a focus on signatures, they have been | ||
|  | known as "sigalgs", but this is really broader than just signature algorithms. | ||
|  | Examples are: | ||
|  | 
 | ||
|  | -   AES-128-CBC | ||
|  | -   hmacWithSHA256 | ||
|  | -   sha256WithRSAEncryption | ||
|  | 
 | ||
|  | ### The connection with AlgorithmIdentifiers
 | ||
|  | 
 | ||
|  | AlgorithmIdentifier is an ASN.1 structure that defines an algorithm as an | ||
|  | OID, along with parameters that should be passed to that algorithm. | ||
|  | 
 | ||
|  | It is expected that an application should be able to take that OID and | ||
|  | fetch it directly, after conversion to string form (either a name if the | ||
|  | application or libcrypto happens to know it, or the OID itself in canonical | ||
|  | numerical form).  To enable this, explicit fetching is necessary. | ||
|  | 
 | ||
|  | ### What we have today
 | ||
|  | 
 | ||
|  | As a matter of fact, we already have built-in support for fetching | ||
|  | composite algorithms, although our providers do not fully participate in | ||
|  | that support, and *most of the time*, we also have public APIs to use the | ||
|  | fetched result, commonly known as support for explicit fetching. | ||
|  | 
 | ||
|  | The idea is that providers can declare the different compositions of a base | ||
|  | algorithm in the `OSSL_ALGORITHM` array, each pointing to different | ||
|  | `OSSL_DISPATCH` tables, which would in turn refer to pretty much the same | ||
|  | functions, apart from the constructor function. | ||
|  | 
 | ||
|  | For example, we already do this with symmetric ciphers. | ||
|  | 
 | ||
|  | Another example, which we could implement in our providers today, would be | ||
|  | compositions of HMAC: | ||
|  | 
 | ||
|  | ``` C | ||
|  | static const OSSL_ALGORITHM deflt_macs[] = { | ||
|  |     /* ... */ | ||
|  |     { "HMAC-SHA1:hmacWithSHA1:1.2.840.113549.2.7", | ||
|  |       "provider=default", ossl_hmac_sha1_functions }, | ||
|  |     { "HMAC-SHA224:hmacWithSHA224:1.2.840.113549.2.8", | ||
|  |       "provider=default", ossl_hmac_sha224_functions }, | ||
|  |     { "HMAC-SHA256:hmacWithSHA256:1.2.840.113549.2.9", | ||
|  |       "provider=default", ossl_hmac_sha256_functions }, | ||
|  |     { "HMAC-SHA384:hmacWithSHA384:1.2.840.113549.2.10", | ||
|  |       "provider=default", ossl_hmac_sha384_functions }, | ||
|  |     { "HMAC-SHA512:hmacWithSHA512:1.2.840.113549.2.11", | ||
|  |       "provider=default", ossl_hmac_sha512_functions }, | ||
|  |     /* ... */ | ||
|  | ``` | ||
|  | 
 | ||
|  | ### What we don't have today
 | ||
|  | 
 | ||
|  | There are some classes of algorithms for which we have no support for using | ||
|  | the result of explicit fetching.  So for example, while it's possible for a | ||
|  | provider to declare composite algorithms through the `OSSL_ALGORITHM` array, | ||
|  | there's currently no way for an application to use them. | ||
|  | 
 | ||
|  | This all revolves around asymmetric algorithms, where we currently only | ||
|  | support implicit fetching. | ||
|  | 
 | ||
|  | This is hurtful in multiple ways: | ||
|  | 
 | ||
|  | -   It fails the provider authors in terms being able to consistently | ||
|  |     declare all algorithms through `OSSL_ALGORITHM` arrays. | ||
|  | -   It fails the applications in terms of being able to fetch algorithms and | ||
|  |     use the result. | ||
|  | -   It fails discoverability, for example through the `openssl list` | ||
|  |     command. |