Skip to content

Redesign the KEYMGMT libcrypto <-> provider interface#11006

Closed
levitte wants to merge 30 commits intoopenssl:masterfrom
levitte:refactor-keymgmt-20200130
Closed

Redesign the KEYMGMT libcrypto <-> provider interface#11006
levitte wants to merge 30 commits intoopenssl:masterfrom
levitte:refactor-keymgmt-20200130

Conversation

@levitte
Copy link
Member

@levitte levitte commented Feb 3, 2020

The KEYMGMT libcrypto <-> provider interface currently makes a few
assumptions:

  1. provider side domain parameters and key data isn't mutable. In
    other words, as soon as a key has been created in any (loaded,
    imported data, ...), it's set in stone.
  2. provider side domain parameters can be strictly separated from the
    key data.

This does work for the most part, but there are places where that's a
bit too rigid for the functionality that the EVP_PKEY API delivers.
Key data needs to be mutable to allow the flexibility that functions
like EVP_PKEY_copy_parameters promise, as well as to provide the
combinations of data that an EVP_PKEY is generally assumed to be able
to hold:

  • domain parameters only
  • public key only
  • public key + private key
  • domain parameters + public key
  • domain parameters + public key + private key

To remedy all this, we:

  1. let go of the distinction between domain parameters and key
    material proper in the libcrypto <-> provider interface (but only
    to a fashion, we still leave the possibility to choose a selection
    when importing and exporting).
  2. Rework the libcrypto <-> provider interface so provider side key
    objects are created and destructed with a separate function, and
    get their data filled and extracted in through import and export.

(future work will see other key object constructors and other
functions to fill them with data)

Fixes #10979

@levitte levitte added branch: master Applies to master branch approval: review pending This pull request needs review by a committer labels Feb 3, 2020
@levitte
Copy link
Member Author

levitte commented Feb 3, 2020

To be noted is that I removed the OP_keymgmt_load and OP_keymgmt_gen functions. They will re-appear in the PRs that implement them, such as #10289 and #9389.

@levitte
Copy link
Member Author

levitte commented Feb 3, 2020

I'm a bit amazed that "domainparams" didn't catch fire earlier, but hey, it's never too late...

So, I kinda like "properties" or "attributes"... PKCS#8 uses the latter, so that could be quite fitting.

Anyone else with better ideas, or just helping me choose?

@slontis
Copy link
Member

slontis commented Feb 4, 2020

Anyone else with better ideas, or just helping me choose?

Dont care too much , we can change that name if needed. I still want domainparams to exist. And flags would also be better just in case this expands to do other things in the future.

@levitte
Copy link
Member Author

levitte commented Feb 4, 2020

I still want domainparams to exist

If we end up having esssentially the same thing, but call it "domainparams" in some cases and "attributes" in another, we haven't done things right. I want one common name for these things, and then leave it to each implementation to treat that as they wish. It's still perfectly acceptable to document that in the case of DH and DSA, "whatever-name-we-choose" holds the domain parameters.

@levitte
Copy link
Member Author

levitte commented Feb 4, 2020

I think I'll let PKCS#8 lead the way, and rename DOMPARAMS to ATTRIBUTES

@slontis
Copy link
Member

slontis commented Feb 4, 2020

From a conceptual point of view.. There are
domain params
pub key
private key.

These 'other' parameters sound like a hack.
They also sound more like they are associated with a key?

So I guess you are trying to lump domain parameters into the key_properties?
Should they be lumped together - I dont know - but it sounds wrong.

@slontis
Copy link
Member

slontis commented Feb 4, 2020

If RSA returns true to hasdomparameters then I will not be pleased.

@slontis
Copy link
Member

slontis commented Feb 4, 2020

We could also end up with a key that 'can' have domain parameters, that returns true because it has some other key properties but no domain parameters.

@levitte
Copy link
Member Author

levitte commented Feb 4, 2020

Let me backtrack a little. My talk of PKCS#8 was la-la-land-talk.

What I was actually leaning on was this:

   AlgorithmIdentifier  ::=  SEQUENCE  {
        algorithm               OBJECT IDENTIFIER,
        parameters              ANY DEFINED BY algorithm OPTIONAL  }

Now, looking up the latest PKCS#1 RFC:

A.1. RSA Key Representation
...
The object identifier rsaEncryption identifies RSA public and private
keys as defined in Appendices A.1.1 and A.1.2. The parameters field
has associated with this OID in a value of type AlgorithmIdentifier
SHALL have a value of type NULL.

A.2.1. RSAES-OAEP
...
The parameters field associated with this OID in a value of type
AlgorithmIdentifier SHALL have a value of type RSAES-OAEP-params
:

RSAES-OAEP-params ::= SEQUENCE {
    hashAlgorithm      [0] HashAlgorithm     DEFAULT sha1,
    maskGenAlgorithm   [1] MaskGenAlgorithm  DEFAULT mgf1SHA1,
    pSourceAlgorithm   [2] PSourceAlgorithm  DEFAULT pSpecifiedEmpty
}

A.2.3. RSASSA-PSS
...
The parameters field associated with this OID in a value of type
AlgorithmIdentifier SHALL have a value of type RSASSA-PSS-params:

RSASSA-PSS-params ::= SEQUENCE {
    hashAlgorithm      [0] HashAlgorithm      DEFAULT sha1,
    maskGenAlgorithm   [1] MaskGenAlgorithm   DEFAULT mgf1SHA1,
    saltLength         [2] INTEGER            DEFAULT 20,
    trailerField       [3] TrailerField       DEFAULT trailerFieldBC
}

Looking at RFC 3279 - Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile, I find:

2.3.2 DSA Signature Keys
...
If the DSA domain parameters are present in the subjectPublicKeyInfo
AlgorithmIdentifier, the parameters are included using the following
ASN.1 structure:

   Dss-Parms  ::=  SEQUENCE  {
       p             INTEGER,
       q             INTEGER,
       g             INTEGER  }

2.3.3 Diffie-Hellman Key Exchange Keys
...
The dhpublicnumber OID is intended to be used in the algorithm field
of a value of type AlgorithmIdentifier. The parameters field of that
type, which has the algorithm-specific syntax ANY DEFINED BY
algorithm, have the ASN.1 type DomainParameters for this algorithm.

   DomainParameters ::= SEQUENCE {
         p       INTEGER, -- odd prime, p=jq +1
         g       INTEGER, -- generator, g
         q       INTEGER, -- factor of p-1
         j       INTEGER OPTIONAL, -- subgroup factor
         validationParms  ValidationParms OPTIONAL }

   ValidationParms ::= SEQUENCE {
         seed             BIT STRING,
         pgenCounter      INTEGER }

See what I'm getting at? I believe the "parameters" that are mentioned all over the place comes directly from the AlgorithmIdentifier definition, and can hold whatever parameters that are associated with a key, be it the domain parameters for DSA and DH, OAEP or PSS parameters for RSA, etc etc etc.

So I guess this ends up being about removing "domain" everywhere I've mistakenly written "domain parameters". Fine, no problem at all, and that approaches already existing code anyway, so...

@t-j-h
Copy link
Member

t-j-h commented Feb 4, 2020

Those two groups of things are completely different concepts.
They aren't something to mix together conceptually IMHO.

@slontis
Copy link
Member

slontis commented Feb 4, 2020

fair enough - but you cant get rid of it 'everywhere'..
We still want to know if something has domain params.. (as opposed to these 'other' parameters).
And key validation is on the domain params..

@slontis
Copy link
Member

slontis commented Feb 4, 2020

I think I'll let PKCS#8 lead the way, and rename DOMPARAMS to ATTRIBUTES

If you had them as flags then it shouldnt be a big issue to do both at once without having to collapse them into one... i.e you could request _ATTRIBUTES | _DOMPARAMS

@levitte
Copy link
Member Author

levitte commented Feb 4, 2020

Having spoken in a vid call and thought about it a little bit more and sought some last minute feedback from @mattcaswell, I figured that the selector bit approach does make sense after all. It would allow us to codify the needs of the EVP layer in an extensible manner, and the broader view is simply a combination of bits.

This could also allow us to reduce the set of OP_keymgmt_has_something() functions to just OP_keymgmt_has() that takes a selector alongside the keydata, and probably also reduce the diverse OP_keymgmt_validate_something() into just one OP_keymgmt_validate().

A hard decision has to be made whether a combination of selector bits should mean that all of those subsets of keydata is considered, or just any (i.e. do we look on them with AND in mind or with OR in mind). Intuitively, it seems that all inclusive approach makes better sense. If everyone agrees with that approach (I already know that @mattcaswell does), we're good.

@iamamoose iamamoose added the approval: ready to merge The 24 hour grace period has passed, ready to merge label Feb 7, 2020
openssl-machine pushed a commit that referenced this pull request Feb 7, 2020
Some of the evp_keymgmt_ functions are just wrappers around the
EVP_KEYMGMT function pointers.  We move those from keymgmt_lib.c to
keymgmt_meth.c.

Other evp_keymgmt_ functions are utility functions to help the rest of
the EVP functions.  Since their names are easily confused with the
functions that were moved to keymgmt_meth.c, we rename them so they
all start with evp_keymgmt_util_.

Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Shane Lontis <[email protected]>
Reviewed-by: Paul Dale <[email protected]>
(Merged from #11006)
openssl-machine pushed a commit that referenced this pull request Feb 7, 2020
The KEYMGMT libcrypto <-> provider interface currently makes a few
assumptions:

1.  provider side domain parameters and key data isn't mutable. In
    other words, as soon as a key has been created in any (loaded,
    imported data, ...), it's set in stone.
2.  provider side domain parameters can be strictly separated from the
    key data.

This does work for the most part, but there are places where that's a
bit too rigid for the functionality that the EVP_PKEY API delivers.
Key data needs to be mutable to allow the flexibility that functions
like EVP_PKEY_copy_parameters promise, as well as to provide the
combinations of data that an EVP_PKEY is generally assumed to be able
to hold:

- domain parameters only
- public key only
- public key + private key
- domain parameters + public key
- domain parameters + public key + private key

To remedy all this, we:

1.  let go of the distinction between domain parameters and key
    material proper in the libcrypto <-> provider interface.

    As a consequence, functions that still need it gain a selection
    argument, which is a set of bits that indicate what parts of the
    key object are to be considered in a specific call.  This allows
    a reduction of very similar functions into one.

2.  Rework the libcrypto <-> provider interface so provider side key
    objects are created and destructed with a separate function, and
    get their data filled and extracted in through import and export.

(future work will see other key object constructors and other
functions to fill them with data)

Fixes #10979

squash! Redesign the KEYMGMT libcrypto <-> provider interface - the basics

Remedy 1 needs a rewrite:

Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Shane Lontis <[email protected]>
Reviewed-by: Paul Dale <[email protected]>
(Merged from #11006)
openssl-machine pushed a commit that referenced this pull request Feb 7, 2020
Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Shane Lontis <[email protected]>
Reviewed-by: Paul Dale <[email protected]>
(Merged from #11006)
openssl-machine pushed a commit that referenced this pull request Feb 7, 2020
Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Shane Lontis <[email protected]>
Reviewed-by: Paul Dale <[email protected]>
(Merged from #11006)
openssl-machine pushed a commit that referenced this pull request Feb 7, 2020
Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Shane Lontis <[email protected]>
Reviewed-by: Paul Dale <[email protected]>
(Merged from #11006)
openssl-machine pushed a commit that referenced this pull request Feb 7, 2020
The same go for the pairs import + import_types and export + export_types.

This required some additional changes in our KEYMGMT implementations.

Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Shane Lontis <[email protected]>
Reviewed-by: Paul Dale <[email protected]>
(Merged from #11006)
@levitte
Copy link
Member Author

levitte commented Feb 7, 2020

Merged!!!!!

68552cd Reorganize the internal evp_keymgmt functions
b305452 Redesign the KEYMGMT libcrypto <-> provider interface - the basics
8dd5c60 Adapt existing KEYMGMT implementations to the redesigned interface
72ec964 Adapt test/keymgmt_internal_test.c to the redesigned interface
32b0645 Adapt existing SERIALIZER implementations to the redesigned interface
273a67e KEYMGMT: Require both get_params and gettable_params, or none

@levitte levitte closed this Feb 7, 2020
@levitte
Copy link
Member Author

levitte commented Feb 7, 2020

I'm very happy this went so well, this was a big stone lifting for me. Thank you all for the feedback!

@slontis
Copy link
Member

slontis commented Feb 7, 2020

good work richard..

@paulidale
Copy link
Contributor

Agreed. This is a significant clean up.

@levitte
Copy link
Member Author

levitte commented Feb 7, 2020

good work richard..

Thanks, but I actually view it as good work on all of us.

@romen
Copy link
Member

romen commented Feb 9, 2020

@levitte

If OP_keymgmt_import() is called with selection = OSSL_KEYMGMT_SELECT_PRIVATE_KEY according to the documentation and intuition, shouldn't the provider only import the private key from the passed params? Same goes for any other atomic selector.

I believe that in general inside the provider implementation the use of the aggregated selectors should be close to zero (the only one that seems useful at the moment is the KEYPAIR selector for validate() as we document that in that specific case we should validate that private and public keys of the keypair match.

@slontis
Copy link
Member

slontis commented Feb 9, 2020

Currently the private key implies the public key must also exist currently. I dont think this is right either, but there are other places that are assuming this also at the moment. This may bite us in the future (say with p11) and then be fairly hard to fix properly.

@romen
Copy link
Member

romen commented Feb 9, 2020

Then again, can we import a private key without importing the public key AND the domain parameters?
It would violate the convention that an EVP_PKEY object MUST either contain domain parameters only (for the key types that have domain parameters) or at least the public key part (which in key types that have domain parameters is defined only in the context of associated domain parameters, that MUST thus be present).
This means that it is not possible to complete the import if we abide the selector when the selector is set to OSSL_KEYMGMT_SELECT_PRIVATE_KEY as we shouldn't (and most of the time cannot anyway) create keydata containing only a private key as we need also the public part which cannot be defined without the associated domain parameters..
And for export the situation is similar, we cannot really export only the private key into what is presumably going to be an EVP_PKEY object, without exporting also the public part and the associated domain parameters.

I feel that for import/export the selectors are creating more confusion than they should, and the current documentation does not clarify enough what the provider implementer should do about the selection bits for import and export.

@levitte
Copy link
Member Author

levitte commented Feb 9, 2020

If there are bits that seem vague, it can certainly be changed for better clarity. Generally speaking, I agree that there should be precision. PR welcome (if I don't get to it first)!

That being said, the interpretation of selection is very much up to the provider. The EVP code can only express a desire, then it's up to the provider code to respond to that desire in what way it deems appropriate. Basically, the EVP code has no control of what stuff the provider side key actually contains. However, a provider must also be smart, in that it needs to ensure that the provider side keys are actually usable for operations it's supposed to be usable with (signature, key exchange, whatever...)

Comment on lines +231 to +247
=head2 Key Object Import and Export Functions

OP_keymgmt_import() should import data indicated by I<selection> into
I<keydata> with values taken from the B<OSSL_PARAM> array I<params>.

OP_keymgmt_export() should extract values indicated by I<selection>
from I<keydata>, create an B<OSSL_PARAM> array with them and call
I<param_cb> with that array as well as the given I<cbarg>.

OP_keymgmt_import_types() should return a constant array of descriptor
B<OSSL_PARAM> for data indicated by I<selection>, for parameters that
OP_keymgmt_import() can handle.

OP_keymgmt_export_types() should return a constant array of descriptor
B<OSSL_PARAM> for data indicated by I<selection>, that the
OP_keymgmt_export() callback can expect to receive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So looking at current dh_kmgmt.c, the implementation does not match the documentation.

Theoretically libcrypto can ask the provider to import only the private key in keydata, but that is not possible and no check is done to prevent it.

If the rule of thumb is that the OSSL_PARAM array resulting from OP_keymgmt_export() with selection = FOO must be at least capable of importing a valid keydata object when the same OSSL_PARAM array is passed to OP_keymgmt_import() with the same value for selection, this should be stated clearly.

We should also state what should the provider do if libcrypto sets selection to some combination that makes it impossible to do what stated above.

  • should OP_keymgmt_export() ignore the restrictions set in selection and export whatever it wants?
  • should OP_keymgmt_export() alternatively return an error as soon as an unsupported selection is requested?
  • should OP_keymgmt_import() return an error as soon as an unsupported selection is requested?

The previous separation between OP_keymgmt_importkey()+OP_keymgmt_exportkey() and OP_keymgmt_importdomparams()+OP_keymgmt_exportdomparams() was IMHO less ambiguous and easier for provider implementers.

At this point I am not sure anymore when import and export can be triggered, but I see the potential risk for a key being generated in the provider, exported to libcrypto only partially because of selection, some dirty_cnt change is triggered in libcrypto which then triggers an import in the provider again resulting in loss of information or error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhmmm... have you read what's written in provider-keymgmt(7) / Key Object Import and Export Functions. Doesn't "indicated by selection" tell you that selection is not supposed to be ignored?

Of course, the provider code might ignore selection, but that seems rather dumb, doesn't it? But most of all, how are we going to stop that?

@romen
Copy link
Member

romen commented Feb 9, 2020

Also, in contrast with DH and DSA, for EC we have separate objects to represent a domain parameters object (EC_GROUP) and a key object (EC_KEY), and while it would be possible to pass around an EC_KEY object not fully initialized, we shouldn't really do that.

The previous separation between importkey and importdomparams (and the equivalent exports) supported cleanly this model, but unifying them makes things quite ugly on the provider side!
Also this is another place where we can see the clear distinction between domain parameters and key parameters.

  • ECDH_COFACTOR_MODE is a key parameter and can be set only on an EC_KEY object
  • EC_GROUP is a standalone representation of the domain parameters under which an EC_KEY can be instantiated

@levitte
Copy link
Member Author

levitte commented Feb 9, 2020

So the weird part is that, for EC keys, the assumptions done at the EVP_PKEY level were bad as well? After all, exactly the same thing applies there, that the underlying backend key that an EVP_PKEY points at may contain all sorts of data combinations. These are the combinations I have detected so far:

  • parameters only
  • public key only
  • public key + private key
  • parameters + public key
  • parameters + public key + private key

So yeah, at the EVP level, *we already assume that the underlying key data is in all sorts of shapes.

But I hear you, the possible interpretations of selection needs a little bit more write-up.
EDIT: looking again at the manual, I would like you to tell me what's actually missing

@romen
Copy link
Member

romen commented Feb 9, 2020

So the weird part is that, for EC keys, the assumptions done at the EVP_PKEY level were bad as well? After all, exactly the same thing applies there, that the underlying backend key that an EVP_PKEY points at may contain all sorts of data combinations. These are the combinations I have detected so far:

  • parameters only

(DH, DSA, EC support this, but in EC parameters only is represented as an EC_GROUP (from which an EC_KEY can be instantiated), while DH and DSA have a single type for a proper key and only the domain parameters to instantiate a proper key)

  • public key only
  • parameters + public key

(only RSA, X/Ed25519 and X/Ed448 can have public key only, as there are no "domain parameters" or the "domain parameters" are embedded in the key type; but technically for the very same reason, public key only and public key + domain parameters for these key types is the same thing, so I don't really think public key only really exists)

  • public key + private key
  • parameters + public key + private key

(as above, arguably public key does not exist without domain parameters, although domain parameters could be an empty set, so these 2 items are conceptually one and the same)

I don't know how to factor the "other parameters" in this discussion, as they are still a bit alien to me.
In EC an "other parameter" would be the "cofactor_mode" that, in terms of legacy behavior, can be only set on an EC_KEY and not on an EC_GROUP so it requires different handling than "domain parameters".
From the previous conversations I understand that the "other parameters" for the fancy RSA keys (OEAP, PSS) are instead more akin to "domain parameters"?

So yeah, at the EVP level, *we already assume that the underlying key data is in all sorts of shapes.

But I hear you, the possible interpretations of selection needs a little bit more write-up.

@levitte
Copy link
Member Author

levitte commented Feb 9, 2020

arguably public key does not exist without domain parameters

I think you should study this code:

openssl/apps/ca.c

Lines 1922 to 1925 in 87d3bb8

pktmp = X509_get0_pubkey(ret);
if (EVP_PKEY_missing_parameters(pktmp) &&
!EVP_PKEY_missing_parameters(pkey))
EVP_PKEY_copy_parameters(pktmp, pkey);

@levitte
Copy link
Member Author

levitte commented Feb 9, 2020

From the previous conversations I understand that the "other parameters" for the fancy RSA keys (OEAP, PSS) are instead more akin to "domain parameters"?

Please don't ask me to explain the difference between domain parameters and "other" parameters, I find this part utterly confusing, and considering what has been said in diverse discussion, no one agrees on an absolutely clear definition. All I know is that at least one person tells me that the FFC parameters associated with DH and DSA keys are domain parameters, and that the "fancy" RSA parameters - PSS and OAEP - are not domain parameters.
Me, I kinda go "ok", shrug a bit, and move on. From a coding point of view, the distinction seems quite useless.

@levitte
Copy link
Member Author

levitte commented Feb 9, 2020

As for EC specific stuff, maybe we should discuss that where it belongs, i.e. in #10631. I'll get there, just not now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: ready to merge The 24 hour grace period has passed, ready to merge branch: master Applies to master branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EVP_KEYMGMT needs a redesign

10 participants