Add from/to SubjectPublicKeyInfo conversion to ed25519.

And run `cargo fmt`.

Change-Id: Iff0ad50ffc8fb02454b22b82050c605620a201f8
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/76647
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
Cindy Lin
2025-02-21 15:56:07 -08:00
committed by Boringssl LUCI CQ
parent 0c59e9e55b
commit daa03aac8d
3 changed files with 93 additions and 9 deletions
+87 -1
View File
@@ -34,7 +34,10 @@
//! assert!(public_key.verify(signed_message, &sig).is_ok());
//! ```
use crate::{FfiMutSlice, FfiSlice, InvalidSignatureError};
use crate::{
cbb_to_buffer, parse_with_cbs, scoped, with_output_array, Buffer, FfiMutSlice, FfiSlice,
InvalidSignatureError,
};
/// The length in bytes of an Ed25519 public key.
pub const PUBLIC_KEY_LEN: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize;
@@ -149,6 +152,66 @@ impl PublicKey {
&self.0
}
/// Parse a public key in SubjectPublicKeyInfo format.
pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
let mut pkey = scoped::EvpPkey::from_ptr(parse_with_cbs(
spki,
// Safety: `pkey` is a non-null result from `EVP_parse_public_key` here.
|pkey| unsafe { bssl_sys::EVP_PKEY_free(pkey) },
// Safety: cbs is valid per `parse_with_cbs`.
|cbs| unsafe { bssl_sys::EVP_parse_public_key(cbs) },
)?);
let mut out_len = 0;
// When the out buffer is null, `out_len` is set to the size of the raw public key.
// Safety: the arguments are valid.
let result = unsafe {
bssl_sys::EVP_PKEY_get_raw_public_key(
pkey.as_ffi_ptr(),
core::ptr::null_mut(),
&mut out_len,
)
};
if result != 1 {
return None;
}
if out_len != PUBLIC_KEY_LEN {
return None;
}
// When the out buffer is not null, the raw public key is written into it.
// Safety: the arguments are valid.
let raw_pkey: [u8; PUBLIC_KEY_LEN] = unsafe {
with_output_array(|out, _| {
assert_eq!(
1,
bssl_sys::EVP_PKEY_get_raw_public_key(pkey.as_ffi_ptr(), out, &mut out_len)
);
})
};
Some(PublicKey(raw_pkey))
}
/// Serialize this key in SubjectPublicKeyInfo format.
pub fn to_der_subject_public_key_info(&self) -> Buffer {
// Safety: this only copies from the `self.0` buffer.
let mut pkey = scoped::EvpPkey::from_ptr(unsafe {
bssl_sys::EVP_PKEY_new_raw_public_key(
bssl_sys::EVP_PKEY_ED25519,
/*unused=*/ core::ptr::null_mut(),
self.0.as_ffi_ptr(),
PUBLIC_KEY_LEN,
)
});
assert!(!pkey.as_ffi_ptr().is_null());
cbb_to_buffer(PUBLIC_KEY_LEN + 32, |cbb| unsafe {
// The arguments are valid so this will only fail if out of memory,
// which this crate doesn't handle.
assert_eq!(1, bssl_sys::EVP_marshal_public_key(cbb, pkey.as_ffi_ptr()));
})
}
/// Verifies that `signature` is a valid signature, by this key, of `msg`.
pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), InvalidSignatureError> {
let ret = unsafe {
@@ -182,6 +245,29 @@ mod test {
assert_eq!(private_key.0, new_private_key.0);
}
#[test]
fn der_subject_public_key_info() {
let priv_key = PrivateKey::generate();
let msg = [0u8; 0];
let sig = priv_key.sign(&msg);
let pub_key = priv_key.to_public();
assert!(pub_key.verify(&msg, &sig).is_ok());
let pub_key_der = pub_key.to_der_subject_public_key_info();
let pub_key_from_der =
PublicKey::from_der_subject_public_key_info(pub_key_der.as_ref()).unwrap();
assert_eq!(pub_key.as_bytes(), pub_key_from_der.as_bytes());
assert!(pub_key_from_der.verify(&msg, &sig).is_ok());
assert!(PublicKey::from_der_subject_public_key_info(
&pub_key_from_der.as_bytes()[0..PUBLIC_KEY_LEN / 2]
)
.is_none());
assert!(PublicKey::from_der_subject_public_key_info(b"").is_none());
}
#[test]
fn empty_msg() {
// Test Case 1 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
+6 -7
View File
@@ -184,9 +184,7 @@ pub struct Prk {
evp_md: *const bssl_sys::EVP_MD,
}
#[allow(clippy::let_unit_value,
clippy::unwrap_used,
)]
#[allow(clippy::let_unit_value, clippy::unwrap_used)]
impl Prk {
/// Creates a Prk from bytes.
pub fn new<MD: digest::Algorithm>(prk_bytes: &[u8]) -> Option<Self> {
@@ -210,10 +208,11 @@ impl Prk {
/// Returns the bytes of the pseudorandom key.
pub fn as_bytes(&self) -> &[u8] {
self.prk.get(..self.len)
// unwrap:`self.len` must be less than the length of `self.prk` thus
// this is always in bounds.
.unwrap()
self.prk
.get(..self.len)
// unwrap:`self.len` must be less than the length of `self.prk` thus
// this is always in bounds.
.unwrap()
}
/// Derive key material for the given info parameter. Attempting
-1
View File
@@ -95,7 +95,6 @@ impl Kem {
}
}
fn from_rfc_id(n: u16) -> Option<Kem> {
match n {
n if n == Kem::P256HkdfSha256 as u16 => Some(Self::P256HkdfSha256),