-
Notifications
You must be signed in to change notification settings - Fork 544
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce Public_Key::raw_public_key_bits() #3985
Introduce Public_Key::raw_public_key_bits() #3985
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems fine to me. We should document somewhere the anticipated change to public_key_bits
once an asn1 encoding for the pqc algorithms is defined.
90ec5a4
to
a49fafe
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM so far :)
a49fafe
to
1c9ed51
Compare
I added some details in pubkey.rst. |
8443b28
to
7f77a39
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another pass
doc/api_ref/pubkey.rst
Outdated
Returns a binary representation of the public key's canonical structure. | ||
Typically, this does not include any metadata like an algorithm identifier | ||
or parameter set. Decoding this might require knowledge of the algorithm | ||
and parameters used to generate the key. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also mention that some algorithms like RSA do not have such a representation and will throw Not_Implemented
std::vector<uint8_t> Dilithium_PublicKey::public_key_bits() const { | ||
// Technically, that's not really correct. The docstring for public_key_bits() | ||
// states that it should return a BER-encoding of the public key. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the doc for public_key_bits
is not really correct/precise. It should return the bytes that, if we stuff it into an X.509 SPKI, result in a valid public key DER encoding. For example for X25519 we just return the 32 byte encoded point with no outer encoding, because that's what RFC 8410 specifies we should do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the effect of the PQC algorithms returning raw_public_key_bits
here is that we just stuff the "native" representation of the key as defined by the various reference implementations into the BIT STRING
of the ASN.1 structure. Which is IMO the correct thing to do, but who knows what will end up getting standardized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this (and the other similar) comments to:
// Currently, there isn't a finalized definition of an ASN.1 structure for
// Dilithium aka ML-DSA public keys. Therefore, we return the raw public key bits.
Regarding API stability and "the principle of least surprise": I'm hoping that a finalized implementation of ML-DSA (and the other to-be-standardized algorithms) would return a standardized ASN.1 structure here. While the pre-standard implementations stay with raw_public_key_bits()
until they are deprecated and eventually phased out. (I'm open for better suggestions.)
src/tests/test_pubkey.cpp
Outdated
@@ -630,6 +630,31 @@ std::vector<Test::Result> PK_Key_Generation_Test::run() { | |||
!public_key->supports_operation(Botan::PublicKeyOperation::KeyAgreement)); | |||
} | |||
|
|||
// Test that the raw public key can be encoded. RSA is excused, as it | |||
// does not have an obvious raw encoding. | |||
if(public_key->algo_name() != "RSA") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rephrase this test slightly.
-
Always invoke
raw_public_key_bits
(for example, here we never call it on RSA, so if it simply crashed or threw some unexpected exception, we wouldn't notice). -
The test may throw an exception. If the exception is not
Not_Implemented
, failure. If the exception isNot_Implemented
, failure unlessalgo_name() == "RSA"
. -
Rest of test as is.
src/lib/pubkey/pk_keys.h
Outdated
* | ||
* Note: some algorithms (for example RSA) do not have an obvious encoding | ||
* for this value due to having many different values, and thus not implement | ||
* this function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also mention that this function (should) always throw Not_Implemented
in this case.
std::vector<uint8_t> Dilithium_PublicKey::public_key_bits() const { | ||
// Technically, that's not really correct. The docstring for public_key_bits() | ||
// states that it should return a BER-encoding of the public key. | ||
return m_public->raw_pk(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For cases like this where we are currently having an equivalence between raw_public_key_bits
and public_key_bits
I'd prefer the latter invoke the former so this relation is clear.
7a22fb8
to
e516c86
Compare
Rebased to latest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. One minor test nit
For most algorithms this returns the canonical byte encoding of the public key's intrinsic object(s). This is particularly useful for the algorithms of the PQC NIST competition where the public key encoding is a simple byte-concatenation of the internal objects. Some algorithm may not have such a canonical byte serialization and may throw Not_Implemented in this case (e.g. RSA). For Key Exchange Schemes this is returning the bare 'public_value'.
The encodings returned by Public_Key::raw_public_key_bits() do not contain any identifiers of the algorithms' parameter sets. Instead, we have to infer them from the test data.
e516c86
to
db3cbe6
Compare
Rebased again after #3716 just got merged. LMS always contains the necessary algorithm information in its encoding, so the implementation of |
Context
As suggested in #3706, this introduces a new top-level method for all public keys:
Public_Key::raw_public_key_bits()
, additional to::public_key_bits()
. Similarly,Private_Key
already providesprivate_key_bits()
andraw_private_key_bits()
(#3437).For a reference of how this new method is useful see the changes in the TLS 1.3 hybrid key handling.
Pull Request Dependencies
With the old
concat
helper, there was an obscure compile error on S390. I'm assuming some sort of alignment issue.Generic Description
For most algorithms this returns the canonical byte encoding of the public key's intrinsic object(s). The byte buffer of the
raw_...()
methods typically does not include any algorithm or parameter specifiers.This is particularly useful for the algorithms of the PQC NIST competition where the public key encoding is a simple byte-concatenation of the internal objects.
Some algorithms may not have such a canonical byte serialization and may throw
Not_Implemented
in this case (e.g. RSA).Key Exchange Schemes
Note that key exchange schemes will return the canonical 'public_value' which wasn't easily accessible from a generic
Public_Key
orPrivate_Key
before. Until now, one had to downcast the private key into aPK_Key_Agreement_Key
and invokepublic_value()
. This was cumbersome (for instance in the implementation of hybrid PQ/T TLS 1.3 - #3609)Testing
I've somewhat abused the
PK_Key_Generation_Test
to exercise round-trips of encoding the raw key data and decoding it with algorithm-specific domain knowledge. Basically, there's a new method that subclasses of this test must implement and return a genericBotan::Public_Key
object constructed from the output ofraw_public_key_bits()
and some parameter context information.Future Work
For the new PQC algorithms we currently return the same bare byte concatenation of the key for both
public_key_bits()
andraw_public_key_bits()
. Technically this is incorrect. Earlier, we did opt to not implement any pre-mature ASN.1 specification for PQC keys.I suggest to depart from this behavior when implementing the final standards and let
public_key_bits()
either throwNot_Implemented
, or (eventually) return a standardized ASN.1 encoding.