diff --git a/aws-lc-rs/src/agreement.rs b/aws-lc-rs/src/agreement.rs index 32d6b5d3e98..5d5920555d2 100644 --- a/aws-lc-rs/src/agreement.rs +++ b/aws-lc-rs/src/agreement.rs @@ -53,27 +53,29 @@ mod ephemeral; pub use ephemeral::{agree_ephemeral, EphemeralPrivateKey}; -use crate::ec::{ - ec_group_from_nid, ec_point_from_bytes, evp_key_generate, evp_pkey_from_public_point, -}; +use crate::cbb::LcCBB; +use crate::ec::{ec_group_from_nid, evp_key_generate}; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; use crate::ptr::{ConstPointer, LcPtr, Pointer}; use crate::{ec, hex}; use aws_lc::{ - EVP_PKEY_CTX_new, EVP_PKEY_CTX_new_id, EVP_PKEY_derive, EVP_PKEY_derive_init, - EVP_PKEY_derive_set_peer, EVP_PKEY_get0_EC_KEY, EVP_PKEY_get_raw_private_key, - EVP_PKEY_get_raw_public_key, EVP_PKEY_keygen, EVP_PKEY_keygen_init, - EVP_PKEY_new_raw_private_key, EVP_PKEY_new_raw_public_key, NID_X9_62_prime256v1, NID_secp384r1, - NID_secp521r1, BIGNUM, EVP_PKEY, EVP_PKEY_X25519, NID_X25519, + CBS_init, EVP_PKEY_CTX_new, EVP_PKEY_CTX_new_id, EVP_PKEY_bits, EVP_PKEY_derive, + EVP_PKEY_derive_init, EVP_PKEY_derive_set_peer, EVP_PKEY_get0_EC_KEY, + EVP_PKEY_get_raw_private_key, EVP_PKEY_get_raw_public_key, EVP_PKEY_id, EVP_PKEY_keygen, + EVP_PKEY_keygen_init, EVP_PKEY_new_raw_private_key, EVP_PKEY_new_raw_public_key, + EVP_marshal_public_key, EVP_parse_public_key, NID_X9_62_prime256v1, NID_secp384r1, + NID_secp521r1, BIGNUM, CBS, EVP_PKEY, EVP_PKEY_X25519, NID_X25519, }; use crate::encoding::{ AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der, + EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der, }; use core::fmt; use core::fmt::{Debug, Formatter}; use core::ptr::null_mut; +use std::mem::MaybeUninit; #[allow(non_camel_case_types)] #[derive(PartialEq, Eq)] @@ -95,12 +97,24 @@ impl AlgorithmID { } } + // Uncompressed public key length in bytes #[inline] const fn pub_key_len(&self) -> usize { match self { - AlgorithmID::ECDH_P256 => 65, - AlgorithmID::ECDH_P384 => 97, - AlgorithmID::ECDH_P521 => 133, + AlgorithmID::ECDH_P256 => ec::uncompressed_public_key_size_bytes(256), + AlgorithmID::ECDH_P384 => ec::uncompressed_public_key_size_bytes(384), + AlgorithmID::ECDH_P521 => ec::uncompressed_public_key_size_bytes(521), + AlgorithmID::X25519 => 32, + } + } + + // Compressed public key length in bytes + #[inline] + const fn compressed_pub_key_len(&self) -> usize { + match self { + AlgorithmID::ECDH_P256 => ec::compressed_public_key_size_bytes(256), + AlgorithmID::ECDH_P384 => ec::compressed_public_key_size_bytes(384), + AlgorithmID::ECDH_P521 => ec::compressed_public_key_size_bytes(521), AlgorithmID::X25519 => 32, } } @@ -174,6 +188,17 @@ enum KeyInner { X25519(LcPtr), } +impl Clone for KeyInner { + fn clone(&self) -> KeyInner { + match self { + KeyInner::ECDH_P256(evp_pkey) => KeyInner::ECDH_P256(evp_pkey.clone()), + KeyInner::ECDH_P384(evp_pkey) => KeyInner::ECDH_P384(evp_pkey.clone()), + KeyInner::ECDH_P521(evp_pkey) => KeyInner::ECDH_P521(evp_pkey.clone()), + KeyInner::X25519(evp_pkey) => KeyInner::X25519(evp_pkey.clone()), + } + } +} + /// A private key for use (only) with `agree`. The /// signature of `agree` allows `PrivateKey` to be /// used for more than one key agreement. @@ -241,7 +266,6 @@ impl PrivateKey { #[inline] /// Generate a new private key for the given algorithm. - /// // # FIPS // Use this function with one of the following algorithms: // * `ECDH_P256` @@ -397,9 +421,9 @@ impl PrivateKey { | KeyInner::ECDH_P384(evp_pkey) | KeyInner::ECDH_P521(evp_pkey) => { let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN]; - let key_len = ec::marshal_public_key_to_buffer(&mut buffer, &evp_pkey.as_const())?; + let key_len = ec::marshal_public_key_to_buffer(&mut buffer, evp_pkey, false)?; Ok(PublicKey { - alg: self.algorithm(), + inner_key: self.inner_key.clone(), public_key: buffer, len: key_len, }) @@ -415,7 +439,7 @@ impl PrivateKey { } Ok(PublicKey { - alg: self.algorithm(), + inner_key: self.inner_key.clone(), public_key: buffer, len: out_len, }) @@ -533,7 +557,7 @@ const MAX_PUBLIC_KEY_LEN: usize = ec::PUBLIC_KEY_MAX_LEN; /// A public key for key agreement. pub struct PublicKey { - alg: &'static Algorithm, + inner_key: KeyInner, public_key: [u8; MAX_PUBLIC_KEY_LEN], len: usize, } @@ -542,15 +566,18 @@ impl PublicKey { /// The algorithm for the public key. #[must_use] pub fn algorithm(&self) -> &'static Algorithm { - self.alg + self.inner_key.algorithm() } } +unsafe impl Send for PublicKey {} +unsafe impl Sync for PublicKey {} + impl Debug for PublicKey { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str(&format!( "PublicKey {{ algorithm: {:?}, bytes: \"{}\" }}", - self.alg, + self.inner_key.algorithm(), hex::encode(&self.public_key[0..self.len]) )) } @@ -568,13 +595,84 @@ impl AsRef<[u8]> for PublicKey { impl Clone for PublicKey { fn clone(&self) -> Self { PublicKey { - alg: self.alg, + inner_key: self.inner_key.clone(), public_key: self.public_key, len: self.len, } } } +impl AsDer> for PublicKey { + /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure. + /// # Errors + /// Returns an error if the public key fails to marshal to X.509. + fn as_der(&self) -> Result, crate::error::Unspecified> { + match &self.inner_key { + KeyInner::ECDH_P256(evp_pkey) + | KeyInner::ECDH_P384(evp_pkey) + | KeyInner::ECDH_P521(evp_pkey) + | KeyInner::X25519(evp_pkey) => { + let key_size_bytes = + TryInto::::try_into(unsafe { EVP_PKEY_bits(evp_pkey.as_const_ptr()) }) + .expect("fit in usize") + * 8; + let mut der = LcCBB::new(key_size_bytes * 5); + if 1 != unsafe { EVP_marshal_public_key(der.as_mut_ptr(), evp_pkey.as_const_ptr()) } + { + return Err(Unspecified); + }; + Ok(PublicKeyX509Der::from(der.into_buffer()?)) + } + } + } +} + +impl AsBigEndian> for PublicKey { + /// Provides the public key elliptic curve point to a compressed point format. + /// # Errors + /// Returns an error if the underlying implementation is unable to marshal the public key to this format. + fn as_be_bytes(&self) -> Result, crate::error::Unspecified> { + let evp_pkey = match &self.inner_key { + KeyInner::ECDH_P256(evp_pkey) + | KeyInner::ECDH_P384(evp_pkey) + | KeyInner::ECDH_P521(evp_pkey) => evp_pkey, + KeyInner::X25519(_) => return Err(Unspecified), + }; + let ec_key = ConstPointer::new(unsafe { EVP_PKEY_get0_EC_KEY(evp_pkey.as_const_ptr()) })?; + + let mut buffer = vec![0u8; self.algorithm().id.compressed_pub_key_len()]; + + let out_len = ec::marshal_ec_public_key_to_buffer(&mut buffer, &ec_key, true)?; + + debug_assert_eq!(buffer.len(), out_len); + + buffer.truncate(out_len); + + Ok(EcPublicKeyCompressedBin::new(buffer)) + } +} + +impl AsBigEndian> for PublicKey { + /// Provides the public key elliptic curve point to a compressed point format. + /// + /// Equivalent to [`PublicKey::as_ref`] for ECDH key types, except that it provides you a copy instead of a reference. + /// + /// # Errors + /// Returns an error if the underlying implementation is unable to marshal the public key to this format. + fn as_be_bytes( + &self, + ) -> Result, crate::error::Unspecified> { + if self.algorithm().id == AlgorithmID::X25519 { + return Err(Unspecified); + } + + let mut buffer = vec![0u8; self.len]; + buffer.copy_from_slice(&self.public_key[0..self.len]); + + Ok(EcPublicKeyUncompressedBin::new(buffer)) + } +} + /// An unparsed, possibly malformed, public key for key agreement. #[derive(Clone)] pub struct UnparsedPublicKey> { @@ -633,7 +731,6 @@ impl> UnparsedPublicKey { /// After the key agreement is done, `agree` calls `kdf` with the raw /// key material from the key agreement operation and then returns what `kdf` /// returns. -/// // # FIPS // Use this function with one of the following key algorithms: // * `ECDH_P256` @@ -654,16 +751,13 @@ where F: FnOnce(&[u8]) -> Result, { let expected_alg = my_private_key.algorithm(); - let expected_pub_key_len = expected_alg.id.pub_key_len(); let expected_nid = expected_alg.id.nid(); if peer_public_key.alg != expected_alg { return Err(error_value); } + let peer_pub_bytes = peer_public_key.bytes.as_ref(); - if peer_pub_bytes.len() != expected_pub_key_len { - return Err(error_value); - } let mut buffer = [0u8; MAX_AGREEMENT_SECRET_LEN]; @@ -691,9 +785,7 @@ fn ec_key_ecdh<'a>( peer_pub_key_bytes: &[u8], nid: i32, ) -> Result<&'a [u8], ()> { - let ec_group = ec_group_from_nid(nid)?; - let pub_key_point = ec_point_from_bytes(&ec_group, peer_pub_key_bytes)?; - let pub_key = evp_pkey_from_public_point(&ec_group, &pub_key_point)?; + let pub_key = ec::try_parse_public_key_bytes(peer_pub_key_bytes, nid)?; let pkey_ctx = LcPtr::new(unsafe { EVP_PKEY_CTX_new(**priv_key, null_mut()) })?; @@ -732,14 +824,7 @@ fn x25519_diffie_hellman<'a>( return Err(()); }; - let pub_key = LcPtr::new(unsafe { - EVP_PKEY_new_raw_public_key( - EVP_PKEY_X25519, - null_mut(), - peer_pub_key.as_ptr(), - peer_pub_key.len(), - ) - })?; + let pub_key = try_parse_x25519_public_key_bytes(peer_pub_key)?; if 1 != unsafe { EVP_PKEY_derive_set_peer(*pkey_ctx, *pub_key) } { return Err(()); @@ -758,6 +843,47 @@ fn x25519_diffie_hellman<'a>( Ok(&buffer[0..AlgorithmID::X25519.pub_key_len()]) } +pub(crate) fn try_parse_x25519_public_key_bytes( + key_bytes: &[u8], +) -> Result, Unspecified> { + try_parse_x25519_subject_public_key_info_bytes(key_bytes) + .or(try_parse_x25519_public_key_raw_bytes(key_bytes)) +} + +fn try_parse_x25519_public_key_raw_bytes(key_bytes: &[u8]) -> Result, Unspecified> { + let expected_pub_key_len = X25519.id.pub_key_len(); + if key_bytes.len() != expected_pub_key_len { + return Err(Unspecified); + } + + Ok(LcPtr::new(unsafe { + EVP_PKEY_new_raw_public_key( + EVP_PKEY_X25519, + null_mut(), + key_bytes.as_ptr(), + key_bytes.len(), + ) + })?) +} + +fn try_parse_x25519_subject_public_key_info_bytes( + key_bytes: &[u8], +) -> Result, Unspecified> { + // Try to parse as SubjectPublicKeyInfo first + let mut cbs = { + let mut cbs = MaybeUninit::::uninit(); + unsafe { + CBS_init(cbs.as_mut_ptr(), key_bytes.as_ptr(), key_bytes.len()); + cbs.assume_init() + } + }; + let evp_pkey = LcPtr::new(unsafe { EVP_parse_public_key(&mut cbs) })?; + if EVP_PKEY_X25519 != unsafe { EVP_PKEY_id(*evp_pkey) } { + return Err(Unspecified); + } + Ok(evp_pkey) +} + #[cfg(test)] mod tests { use crate::agreement::{ @@ -766,6 +892,7 @@ mod tests { }; use crate::encoding::{ AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der, + EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der, }; use crate::{rand, test}; @@ -1165,19 +1292,15 @@ mod tests { let peer_private = PrivateKey::generate(alg).unwrap(); let my_private = PrivateKey::generate(alg).unwrap(); - let peer_public_keys = [UnparsedPublicKey::new( - alg, - peer_private.compute_public_key().unwrap(), - )]; + let peer_public_keys = + public_key_formats_helper(&peer_private.compute_public_key().unwrap()); - let my_public_keys = [UnparsedPublicKey::new( - alg, - my_private.compute_public_key().unwrap(), - )]; + let my_public_keys = public_key_formats_helper(&my_private.compute_public_key().unwrap()); let mut results: Vec> = Vec::new(); for peer_public in peer_public_keys { + let peer_public = UnparsedPublicKey::new(alg, peer_public); let result = agree(&my_private, &peer_public, (), |key_material| { results.push(key_material.to_vec()); Ok(()) @@ -1186,15 +1309,58 @@ mod tests { } for my_public in my_public_keys { + let my_public = UnparsedPublicKey::new(alg, my_public); let result = agree(&peer_private, &my_public, (), |key_material| { results.push(key_material.to_vec()); Ok(()) }); assert_eq!(result, Ok(())); } - assert_eq!(results.len(), 2); - for consecutive_results in results.windows(2) { - assert_eq!(consecutive_results[0], consecutive_results[1]); + + let key_types_tested = match alg.id { + crate::agreement::AlgorithmID::ECDH_P256 + | crate::agreement::AlgorithmID::ECDH_P384 + | crate::agreement::AlgorithmID::ECDH_P521 => 4, + crate::agreement::AlgorithmID::X25519 => 2, + }; + + assert_eq!(results.len(), key_types_tested * 2); // Multiplied by two because we tested the other direction + + assert_eq!(results[0..key_types_tested], results[key_types_tested..]); + } + + fn public_key_formats_helper(public_key: &PublicKey) -> Vec> { + let verify_ec_raw_traits = matches!( + public_key.algorithm().id, + crate::agreement::AlgorithmID::ECDH_P256 + | crate::agreement::AlgorithmID::ECDH_P384 + | crate::agreement::AlgorithmID::ECDH_P521 + ); + + let mut public_keys = Vec::>::new(); + public_keys.push(public_key.as_ref().into()); + + if verify_ec_raw_traits { + let raw = AsBigEndian::::as_be_bytes(public_key).unwrap(); + public_keys.push(raw.as_ref().into()); + let raw = AsBigEndian::::as_be_bytes(public_key).unwrap(); + public_keys.push(raw.as_ref().into()); } + + let peer_x509 = AsDer::::as_der(public_key).unwrap(); + public_keys.push(peer_x509.as_ref().into()); + + public_keys + } + + #[test] + fn private_key_drop() { + let private_key = PrivateKey::generate(&ECDH_P256).unwrap(); + let public_key = private_key.compute_public_key().unwrap(); + // PublicKey maintains a reference counted pointer to private keys EVP_PKEY so we test that with drop + drop(private_key); + let _ = AsBigEndian::::as_be_bytes(&public_key).unwrap(); + let _ = AsBigEndian::::as_be_bytes(&public_key).unwrap(); + let _ = AsDer::::as_der(&public_key).unwrap(); } } diff --git a/aws-lc-rs/src/agreement/data/agreement_tests.txt b/aws-lc-rs/src/agreement/data/agreement_tests.txt index 589c46a69fc..5d02bd0d896 100644 --- a/aws-lc-rs/src/agreement/data/agreement_tests.txt +++ b/aws-lc-rs/src/agreement/data/agreement_tests.txt @@ -17,6 +17,34 @@ D = 4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d MyQ = ff63fe57bfbf43fa3f563628b149af704d3db625369c49983650347a6a71e00e Output = 95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957 +# Same expected output as above but MyQ is now in SubjectPublicKeyInfo format + +Curve = X25519 +PeerQ = e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c +D = a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4 +MyQ = 302a300506032b656e0321001c9fd88f45606d932a80c71824ae151d15d73e77de38e8e000852e614fae7019 +MyQFormat = X509 +Output = c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552 + +Curve = X25519 +PeerQ = e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493 +D = 4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d +MyQ = 302a300506032b656e032100ff63fe57bfbf43fa3f563628b149af704d3db625369c49983650347a6a71e00e +MyQFormat = X509 +Output = 95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957 + +# Same test expectation but now PeerQ is SubjectPublicKeyInfo format +Curve = X25519 +PeerQ = 302a300506032b656e032100e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c +D = a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4 +MyQ = 1c9fd88f45606d932a80c71824ae151d15d73e77de38e8e000852e614fae7019 +Output = c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552 + +Curve = X25519 +PeerQ = 302a300506032b656e032100e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493 +D = 4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d +MyQ = ff63fe57bfbf43fa3f563628b149af704d3db625369c49983650347a6a71e00e +Output = 95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957 # Additional X25519 Test Vectors @@ -250,29 +278,95 @@ Curve = P-521 PeerQ = 0400D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F Error = Peer public key is missing the Y coordinate completely. +# RFC 5903 (IKE and IKEv2 ECDH) Test Vectors +# +# PeerQ is (grx, gry) in compressed encoding. +# D is i. +# MyQ is (gix, giy) in compressed encoding. +# Output is girx. + Curve = P-256 PeerQ = 02D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF63 -Error = Peer public key is in compressed form (0x02). +MyQFormat = COMPRESSED +D = C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433 +MyQ = 03dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c3772581180 +Output = D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE Curve = P-384 +MyQFormat = COMPRESSED PeerQ = 02E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571 -Error = Peer public key is in compressed form (0x02). +D = 099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194 +MyQ = 02667842d7d180ac2cde6f74f37551f55755c7645c20ef73e31634fe72b4c55ee6de3ac808acb4bdb4c88732aee95f41aa +Output = 11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746 Curve = P-521 +MyQFormat = COMPRESSED PeerQ = 0200D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F -Error = Peer public key is in compressed form (0x02). +D = 0037ADE9319A89F4DABDB3EF411AACCCA5123C61ACAB57B5393DCE47608172A095AA85A30FE1C2952C6771D937BA9777F5957B2639BAB072462F68C27A57382D4A52 +MyQ = 020015417e84dbf28c0ad3c278713349dc7df153c897a1891bd98bab4357c9ecbee1e3bf42e00b8e380aeae57c2d107564941885942af5a7f4601723c4195d176ced3e +Output = 01144C7D79AE6956BC8EDB8E7C787C4521CB086FA64407F97894E5E6B2D79B04D1427E73CA4BAA240A34786859810C06B3C715A3A8CC3151F2BEE417996D19F3DDEA Curve = P-256 +MyQFormat = COMPRESSED PeerQ = 03D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF63 -Error = Peer public key is in compressed form (0x03). +D = C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433 +MyQ = 03dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c3772581180 +Output = D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE Curve = P-384 +MyQFormat = COMPRESSED PeerQ = 03E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571 -Error = Peer public key is in compressed form (0x03). +D = 099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194 +MyQ = 02667842d7d180ac2cde6f74f37551f55755c7645c20ef73e31634fe72b4c55ee6de3ac808acb4bdb4c88732aee95f41aa +Output = 11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746 Curve = P-521 +MyQFormat = COMPRESSED PeerQ = 0300D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F -Error = Peer public key is in compressed form (0x03). +D = 0037ADE9319A89F4DABDB3EF411AACCCA5123C61ACAB57B5393DCE47608172A095AA85A30FE1C2952C6771D937BA9777F5957B2639BAB072462F68C27A57382D4A52 +MyQ = 020015417e84dbf28c0ad3c278713349dc7df153c897a1891bd98bab4357c9ecbee1e3bf42e00b8e380aeae57c2d107564941885942af5a7f4601723c4195d176ced3e +Output = 01144C7D79AE6956BC8EDB8E7C787C4521CB086FA64407F97894E5E6B2D79B04D1427E73CA4BAA240A34786859810C06B3C715A3A8CC3151F2BEE417996D19F3DDEA + +Curve = P-256 +PeerQ = 02D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF63 +MyQFormat = X509 +D = C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433 +MyQ = 3059301306072a8648ce3d020106082a8648ce3d03010703420004dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c37725811805271a0461cdb8252d61f1c456fa3e59ab1f45b33accf5f58389e0577b8990bb3 +Output = D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE + +Curve = P-384 +MyQFormat = X509 +PeerQ = 02E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571 +D = 099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194 +MyQ = 3076301006072a8648ce3d020106052b8104002203620004667842d7d180ac2cde6f74f37551f55755c7645c20ef73e31634fe72b4c55ee6de3ac808acb4bdb4c88732aee95f41aa9482ed1fc0eeb9cafc4984625ccfc23f65032149e0e144ada024181535a0f38eeb9fcff3c2c947dae69b4c634573a81c +Output = 11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746 + +Curve = P-521 +MyQFormat = X509 +PeerQ = 0200D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F +D = 0037ADE9319A89F4DABDB3EF411AACCCA5123C61ACAB57B5393DCE47608172A095AA85A30FE1C2952C6771D937BA9777F5957B2639BAB072462F68C27A57382D4A52 +MyQ = 30819b301006072a8648ce3d020106052b8104002303818600040015417e84dbf28c0ad3c278713349dc7df153c897a1891bd98bab4357c9ecbee1e3bf42e00b8e380aeae57c2d107564941885942af5a7f4601723c4195d176ced3e017cae20b6641d2eeb695786d8c946146239d099e18e1d5a514c739d7cb4a10ad8a788015ac405d7799dc75e7b7d5b6cf2261a6a7f1507438bf01beb6ca3926f9582 +Output = 01144C7D79AE6956BC8EDB8E7C787C4521CB086FA64407F97894E5E6B2D79B04D1427E73CA4BAA240A34786859810C06B3C715A3A8CC3151F2BEE417996D19F3DDEA + +# Same as above but PeerQ is X509 subject public key info format + +Curve = P-256 +PeerQ = 3059301306072A8648CE3D020106082A8648CE3D03010703420004D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF6356FBF3CA366CC23E8157854C13C58D6AAC23F046ADA30F8353E74F33039872AB +D = C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433 +MyQ = 04DAD0B65394221CF9B051E1FECA5787D098DFE637FC90B9EF945D0C37725811805271A0461CDB8252D61F1C456FA3E59AB1F45B33ACCF5F58389E0577B8990BB3 +Output = D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE + +Curve = P-384 +PeerQ = 3076301006072A8648CE3D020106052B8104002203620004E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571DCFBEC7AACF3196472169E838430367F66EEBE3C6E70C416DD5F0C68759DD1FFF83FA40142209DFF5EAAD96DB9E6386C +D = 099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194 +MyQ = 04667842D7D180AC2CDE6F74F37551F55755C7645C20EF73E31634FE72B4C55EE6DE3AC808ACB4BDB4C88732AEE95F41AA9482ED1FC0EEB9CAFC4984625CCFC23F65032149E0E144ADA024181535A0F38EEB9FCFF3C2C947DAE69B4C634573A81C +Output = 11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746 + +Curve = P-521 +PeerQ = 30819B301006072A8648CE3D020106052B81040023038186000400D0B3975AC4B799F5BEA16D5E13E9AF971D5E9B984C9F39728B5E5739735A219B97C356436ADC6E95BB0352F6BE64A6C2912D4EF2D0433CED2B6171640012D9460F015C68226383956E3BD066E797B623C27CE0EAC2F551A10C2C724D9852077B87220B6536C5C408A1D2AEBB8E86D678AE49CB57091F4732296579AB44FCD17F0FC56A +MyQ = 040015417E84DBF28C0AD3C278713349DC7DF153C897A1891BD98BAB4357C9ECBEE1E3BF42E00B8E380AEAE57C2D107564941885942AF5A7F4601723C4195D176CED3E017CAE20B6641D2EEB695786D8C946146239D099E18E1D5A514C739D7CB4A10AD8A788015AC405D7799DC75E7B7D5B6CF2261A6A7F1507438BF01BEB6CA3926F9582 +D = 0037ADE9319A89F4DABDB3EF411AACCCA5123C61ACAB57B5393DCE47608172A095AA85A30FE1C2952C6771D937BA9777F5957B2639BAB072462F68C27A57382D4A52 +Output = 01144C7D79AE6956BC8EDB8E7C787C4521CB086FA64407F97894E5E6B2D79B04D1427E73CA4BAA240A34786859810C06B3C715A3A8CC3151F2BEE417996D19F3DDEA # NIST vectors from diff --git a/aws-lc-rs/src/agreement/ephemeral.rs b/aws-lc-rs/src/agreement/ephemeral.rs index f15b0370594..0e3c4b00981 100644 --- a/aws-lc-rs/src/agreement/ephemeral.rs +++ b/aws-lc-rs/src/agreement/ephemeral.rs @@ -28,7 +28,6 @@ impl EphemeralPrivateKey { /// /// # *ring* Compatibility /// Our implementation ignores the `SecureRandom` parameter. - /// // # FIPS // Use this function with one of the following algorithms: // * `ECDH_P256` @@ -87,7 +86,6 @@ impl EphemeralPrivateKey { /// After the key agreement is done, `agree_ephemeral` calls `kdf` with the raw /// key material from the key agreement operation and then returns what `kdf` /// returns. -/// // # FIPS // Use this function with one of the following key algorithms: // * `ECDH_P256` @@ -114,6 +112,10 @@ where #[cfg(test)] mod tests { + use crate::agreement::{AlgorithmID, PublicKey}; + use crate::encoding::{ + AsBigEndian, AsDer, EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der, + }; use crate::error::Unspecified; use crate::{agreement, rand, test, test_file}; @@ -348,6 +350,49 @@ mod tests { ); } + fn check_computed_public_key( + algorithm: &AlgorithmID, + expected_format: &str, + expected_public_key_bytes: &[u8], + computed_public: &PublicKey, + ) { + match (algorithm, expected_format) { + (_, "X509") => { + let der = AsDer::::as_der(computed_public) + .expect("serialize to uncompressed format"); + assert_eq!( + expected_public_key_bytes, + der.as_ref(), + "hex: {:x?}", + der.as_ref() + ); + } + ( + AlgorithmID::ECDH_P256 | AlgorithmID::ECDH_P384 | AlgorithmID::ECDH_P521, + "COMPRESSED", + ) => { + let bin = AsBigEndian::::as_be_bytes(computed_public) + .expect("serialize to compressed format"); + assert_eq!(expected_public_key_bytes, bin.as_ref()); + } + ( + AlgorithmID::ECDH_P256 | AlgorithmID::ECDH_P384 | AlgorithmID::ECDH_P521, + "UNCOMPRESSED" | "", + ) => { + let bin = AsBigEndian::::as_be_bytes(computed_public) + .expect("serialize to uncompressed format"); + assert_eq!(expected_public_key_bytes, bin.as_ref()); + assert_eq!(expected_public_key_bytes, computed_public.as_ref()); + } + (AlgorithmID::X25519, "") => { + assert_eq!(expected_public_key_bytes, computed_public.as_ref()); + } + (ai, pf) => { + panic!("Unexpected PeerFormat={pf:?} for {ai:?}") + } + } + } + #[test] fn agreement_agree_ephemeral() { let rng = rand::SystemRandom::new(); @@ -362,6 +407,10 @@ mod tests { let peer_public = agreement::UnparsedPublicKey::new(alg, test_case.consume_bytes("PeerQ")); + let myq_format = test_case + .consume_optional_string("MyQFormat") + .unwrap_or_default(); + if test_case.consume_optional_string("Error").is_none() { let my_private_bytes = test_case.consume_bytes("D"); let my_private = { @@ -376,7 +425,8 @@ mod tests { assert_eq!(my_private.algorithm(), alg); let computed_public = my_private.compute_public_key().unwrap(); - assert_eq!(computed_public.as_ref(), &my_public[..]); + + check_computed_public_key(&alg.id, &myq_format, &my_public, &computed_public); assert_eq!(my_private.algorithm(), alg); diff --git a/aws-lc-rs/src/ec.rs b/aws-lc-rs/src/ec.rs index fc4e2ca8610..8832e1b9afe 100644 --- a/aws-lc-rs/src/ec.rs +++ b/aws-lc-rs/src/ec.rs @@ -21,20 +21,22 @@ use aws_lc::EC_KEY_check_fips; #[cfg(not(feature = "fips"))] use aws_lc::EC_KEY_check_key; use aws_lc::{ - d2i_PrivateKey, point_conversion_form_t, BN_bn2bin_padded, BN_num_bytes, ECDSA_SIG_from_bytes, - ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, ECDSA_SIG_new, ECDSA_SIG_set0, ECDSA_SIG_to_bytes, - EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_get0_group, + d2i_PrivateKey, point_conversion_form_t, BN_bn2bin_padded, BN_num_bytes, CBS_init, + ECDSA_SIG_from_bytes, ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, ECDSA_SIG_new, ECDSA_SIG_set0, + ECDSA_SIG_to_bytes, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_get0_group, EC_KEY_get0_private_key, EC_KEY_get0_public_key, EC_KEY_new, EC_KEY_set_group, EC_KEY_set_private_key, EC_KEY_set_public_key, EC_POINT_mul, EC_POINT_new, EC_POINT_oct2point, EC_POINT_point2oct, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_CTX_new_id, EVP_PKEY_CTX_set_ec_paramgen_curve_nid, EVP_PKEY_assign_EC_KEY, EVP_PKEY_get0_EC_KEY, - EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, NID_X9_62_prime256v1, NID_secp256k1, - NID_secp384r1, NID_secp521r1, BIGNUM, ECDSA_SIG, EC_GROUP, EC_KEY, EC_POINT, EVP_PKEY, - EVP_PKEY_EC, + EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, EVP_parse_public_key, + NID_X9_62_prime256v1, NID_secp256k1, NID_secp384r1, NID_secp521r1, BIGNUM, CBS, ECDSA_SIG, + EC_GROUP, EC_KEY, EC_POINT, EVP_PKEY, EVP_PKEY_EC, }; use crate::digest::digest_ctx::DigestContext; -use crate::encoding::{AsDer, PublicKeyX509Der}; +use crate::encoding::{ + AsBigEndian, AsDer, EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der, +}; use crate::error::{KeyRejected, Unspecified}; use crate::fips::indicator_check; use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr, Pointer}; @@ -117,19 +119,31 @@ impl AlgorithmID { AlgorithmID::ECDSA_P521 => 66, } } + // Compressed public key length in bytes + #[inline] + const fn compressed_pub_key_len(&self) -> usize { + match self { + AlgorithmID::ECDSA_P256 | AlgorithmID::ECDSA_P256K1 => { + compressed_public_key_size_bytes(256) + } + AlgorithmID::ECDSA_P384 => compressed_public_key_size_bytes(384), + AlgorithmID::ECDSA_P521 => compressed_public_key_size_bytes(521), + } + } } /// Elliptic curve public key. #[derive(Clone)] pub struct PublicKey { algorithm: &'static EcdsaSigningAlgorithm, + evp_pkey: LcPtr, octets: Box<[u8]>, } impl AsDer> for PublicKey { /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure. /// # Errors - /// Returns an error if the underlying implementation is unable to marshal the point. + /// Returns an error if the public key fails to marshal to X.509. fn as_der(&self) -> Result, Unspecified> { let ec_group = LcPtr::new(unsafe { EC_GROUP_new_by_curve_name(self.algorithm.id.nid()) })?; let ec_point = ec_point_from_bytes(&ec_group, self.as_ref())?; @@ -152,6 +166,39 @@ impl AsDer> for PublicKey { } } +impl AsBigEndian> for PublicKey { + /// Provides the public key elliptic curve point to a compressed point bytes format. + /// # Errors + /// Returns an error if the public key fails to marshal. + fn as_be_bytes(&self) -> Result, crate::error::Unspecified> { + let ec_key = + ConstPointer::new(unsafe { EVP_PKEY_get0_EC_KEY(self.evp_pkey.as_const_ptr()) })?; + + let mut buffer = vec![0u8; self.algorithm.0.id.compressed_pub_key_len()]; + + let out_len = marshal_ec_public_key_to_buffer(&mut buffer, &ec_key, true)?; + + debug_assert_eq!(buffer.len(), out_len); + + buffer.truncate(out_len); + + Ok(EcPublicKeyCompressedBin::new(buffer)) + } +} + +impl AsBigEndian> for PublicKey { + /// Provides the public key elliptic curve point to an uncompressed point bytes format. + /// # Errors + /// Returns an error if the public key fails to marshal. + fn as_be_bytes( + &self, + ) -> Result, crate::error::Unspecified> { + let mut uncompressed_bytes = vec![0u8; self.octets.len()]; + uncompressed_bytes.copy_from_slice(&self.octets); + Ok(EcPublicKeyUncompressedBin::new(uncompressed_bytes)) + } +} + impl Debug for PublicKey { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { f.write_str(&format!( @@ -234,7 +281,7 @@ fn verify_asn1_signature( msg: &[u8], signature: &[u8], ) -> Result<(), Unspecified> { - let pkey = evp_pkey_from_public_key(alg, public_key)?; + let pkey = try_parse_public_key_bytes(public_key, alg.nid())?; let mut md_ctx = DigestContext::new_uninit(); @@ -261,18 +308,6 @@ fn verify_asn1_signature( Ok(()) } -#[inline] -fn evp_pkey_from_public_key( - alg: &'static AlgorithmID, - public_key: &[u8], -) -> Result, Unspecified> { - let ec_group = ec_group_from_nid(alg.nid())?; - let ec_point = ec_point_from_bytes(&ec_group, public_key)?; - let pkey = evp_pkey_from_public_point(&ec_group, &ec_point)?; - - Ok(pkey) -} - fn verify_ec_key_nid( ec_key: &ConstPointer, expected_curve_nid: i32, @@ -364,38 +399,86 @@ pub(crate) fn unmarshal_der_to_private_key( } pub(crate) fn marshal_public_key_to_buffer( - buffer: &mut [u8; PUBLIC_KEY_MAX_LEN], - evp_pkey: &ConstPointer, + buffer: &mut [u8], + evp_pkey: &LcPtr, + compressed: bool, ) -> Result { - let ec_key = ConstPointer::new(unsafe { EVP_PKEY_get0_EC_KEY(**evp_pkey) })?; - marshal_ec_public_key_to_buffer(buffer, &ec_key) + let ec_key = ConstPointer::new(unsafe { EVP_PKEY_get0_EC_KEY(evp_pkey.as_const_ptr()) })?; + marshal_ec_public_key_to_buffer(buffer, &ec_key, compressed) } pub(crate) fn marshal_ec_public_key_to_buffer( - buffer: &mut [u8; PUBLIC_KEY_MAX_LEN], + buffer: &mut [u8], ec_key: &ConstPointer, + compressed: bool, ) -> Result { let ec_group = ConstPointer::new(unsafe { EC_KEY_get0_group(**ec_key) })?; let ec_point = ConstPointer::new(unsafe { EC_KEY_get0_public_key(**ec_key) })?; - let out_len = ec_point_to_bytes(&ec_group, &ec_point, buffer)?; + let point_conversion_form = if compressed { + point_conversion_form_t::POINT_CONVERSION_COMPRESSED + } else { + point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED + }; + + let out_len = ec_point_to_bytes(&ec_group, &ec_point, buffer, point_conversion_form)?; Ok(out_len) } -pub(crate) fn marshal_public_key( - evp_pkey: &ConstPointer, +pub(crate) fn public_key_from_evp_pkey( + evp_pkey: &LcPtr, algorithm: &'static EcdsaSigningAlgorithm, ) -> Result { let mut pub_key_bytes = [0u8; PUBLIC_KEY_MAX_LEN]; - let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_pkey)?; + let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_pkey, false)?; Ok(PublicKey { + evp_pkey: evp_pkey.clone(), algorithm, octets: pub_key_bytes[0..key_len].into(), }) } +pub(crate) fn try_parse_public_key_bytes( + key_bytes: &[u8], + expected_curve_nid: i32, +) -> Result, Unspecified> { + try_parse_subject_public_key_info_bytes(key_bytes) + .and_then(|key| { + validate_evp_key(&key.as_const(), expected_curve_nid) + .map(|()| key) + .map_err(|_| Unspecified) + }) + .or(try_parse_public_key_raw_bytes( + key_bytes, + expected_curve_nid, + )) +} + +fn try_parse_subject_public_key_info_bytes( + key_bytes: &[u8], +) -> Result, Unspecified> { + // Try to parse as SubjectPublicKeyInfo first + let mut cbs = { + let mut cbs = MaybeUninit::::uninit(); + unsafe { + CBS_init(cbs.as_mut_ptr(), key_bytes.as_ptr(), key_bytes.len()); + cbs.assume_init() + } + }; + Ok(LcPtr::new(unsafe { EVP_parse_public_key(&mut cbs) })?) +} + +fn try_parse_public_key_raw_bytes( + key_bytes: &[u8], + expected_curve_nid: i32, +) -> Result, Unspecified> { + let ec_group = ec_group_from_nid(expected_curve_nid)?; + let pub_key_point = ec_point_from_bytes(&ec_group, key_bytes)?; + evp_pkey_from_public_point(&ec_group, &pub_key_point) +} + #[inline] pub(crate) fn evp_pkey_from_public_point( ec_group: &LcPtr, @@ -551,17 +634,17 @@ pub(crate) fn ec_point_from_bytes( fn ec_point_to_bytes( ec_group: &ConstPointer, ec_point: &ConstPointer, - buf: &mut [u8; PUBLIC_KEY_MAX_LEN], + buf: &mut [u8], + pt_conv_form: point_conversion_form_t, ) -> Result { - let pt_conv_form = point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED; - + let buf_len = buf.len(); let out_len = unsafe { EC_POINT_point2oct( **ec_group, **ec_point, pt_conv_form, buf.as_mut_ptr(), - PUBLIC_KEY_MAX_LEN, + buf_len, null_mut(), ) }; @@ -620,10 +703,22 @@ unsafe fn ecdsa_sig_from_fixed( Ok(ecdsa_sig) } +#[inline] +pub(crate) const fn compressed_public_key_size_bytes(curve_field_bits: usize) -> usize { + 1 + (curve_field_bits + 7) / 8 +} + +#[inline] +pub(crate) const fn uncompressed_public_key_size_bytes(curve_field_bits: usize) -> usize { + 1 + 2 * ((curve_field_bits + 7) / 8) +} + #[cfg(test)] mod tests { - use crate::encoding::{AsDer, PublicKeyX509Der}; - use crate::signature::EcdsaKeyPair; + use crate::encoding::{ + AsBigEndian, AsDer, EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der, + }; + use crate::signature::{EcdsaKeyPair, UnparsedPublicKey, ECDSA_P256_SHA256_FIXED}; use crate::signature::{KeyPair, ECDSA_P256_SHA256_FIXED_SIGNING}; use crate::test::from_dirty_hex; use crate::{signature, test}; @@ -686,4 +781,34 @@ mod tests { let actual_result = unparsed_pub_key.verify(msg.as_bytes(), &sig); assert!(actual_result.is_ok(), "Key: {}", test::to_hex(public_key)); } + + #[test] + fn public_key_formats() { + const MESSAGE: &[u8] = b"message to be signed"; + + let key_pair = EcdsaKeyPair::generate(&ECDSA_P256_SHA256_FIXED_SIGNING).unwrap(); + let public_key = key_pair.public_key(); + let as_ref_bytes = public_key.as_ref(); + let compressed = AsBigEndian::::as_be_bytes(public_key).unwrap(); + let uncompressed = + AsBigEndian::::as_be_bytes(public_key).unwrap(); + let pub_x509 = AsDer::::as_der(public_key).unwrap(); + assert_eq!(as_ref_bytes, uncompressed.as_ref()); + assert_ne!(compressed.as_ref()[0], 0x04); + + let rng = crate::rand::SystemRandom::new(); + + let signature = key_pair.sign(&rng, MESSAGE).unwrap(); + + for pub_key_bytes in [ + as_ref_bytes, + compressed.as_ref(), + uncompressed.as_ref(), + pub_x509.as_ref(), + ] { + UnparsedPublicKey::new(&ECDSA_P256_SHA256_FIXED, pub_key_bytes) + .verify(MESSAGE, signature.as_ref()) + .unwrap(); + } + } } diff --git a/aws-lc-rs/src/ec/key_pair.rs b/aws-lc-rs/src/ec/key_pair.rs index db9b0552273..b890371e900 100644 --- a/aws-lc-rs/src/ec/key_pair.rs +++ b/aws-lc-rs/src/ec/key_pair.rs @@ -60,7 +60,7 @@ impl EcdsaKeyPair { algorithm: &'static EcdsaSigningAlgorithm, evp_pkey: LcPtr, ) -> Result { - let pubkey = ec::marshal_public_key(&evp_pkey.as_const(), algorithm)?; + let pubkey = ec::public_key_from_evp_pkey(&evp_pkey, algorithm)?; Ok(Self { algorithm, diff --git a/aws-lc-rs/src/encoding.rs b/aws-lc-rs/src/encoding.rs index b561118dbc4..278cfa063e2 100644 --- a/aws-lc-rs/src/encoding.rs +++ b/aws-lc-rs/src/encoding.rs @@ -58,6 +58,8 @@ pub(crate) use generated_encodings; generated_encodings!( EcPrivateKeyBin, EcPrivateKeyRfc5915Der, + EcPublicKeyUncompressedBin, + EcPublicKeyCompressedBin, PublicKeyX509Der, Curve25519SeedBin, Pkcs8V1Der