rc_crypto/keys/public.rs
1// Copyright 2026-Present Datadog, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use aws_lc_rs::signature::ECDSA_P256_SHA256_ASN1;
16
17use crate::{Signature, keys::KeyId};
18
19/// Verifying a signature failed.
20///
21/// This error SHOULD be treated as a security concern and SHOULD be logged.
22#[derive(Debug, thiserror::Error)]
23#[error("signature verification failed")]
24pub struct SignatureVerifyErr;
25
26/// The public portion of a [`PrivateKey`].
27///
28/// # Internal Encoding
29///
30/// This key holds the raw DER key material bytes (without algorithm
31/// identifier).
32///
33/// Specifically this holds the `subjectPublicKey` bit string field of the
34/// `SubjectPublicKeyInfo` ASN.1 message as defined in [RFC 5280 § 4.1].
35///
36/// [RFC 5280 § 4.1]: https://tools.ietf.org/html/rfc5280#section-4.1
37/// [`PrivateKey`]: crate::keys::PrivateKey
38#[derive(Debug)]
39pub struct PublicKey<'a>(&'a [u8]);
40
41impl<'a> PublicKey<'a> {
42 /// Construct this wrapper over opaque bytes.
43 ///
44 /// NOTE: this func is private to the crate to prevent constructing public
45 /// keys incorrectly.
46 pub(crate) fn new(v: &'a [u8]) -> Self {
47 Self(v)
48 }
49
50 /// Verify that this key pair generated `sig` over `data`.
51 pub fn verify(&self, data: &[u8], sig: &Signature) -> Result<(), SignatureVerifyErr> {
52 aws_lc_rs::signature::UnparsedPublicKey::new(&ECDSA_P256_SHA256_ASN1, self.0)
53 .verify(data, sig.as_ref())
54 .map_err(|_| SignatureVerifyErr)
55 }
56
57 /// Generate a [`KeyId`] that uniquely identifies this key, suitable for use
58 /// as an X509 Subject Key Identifier.
59 pub fn key_id(&self) -> KeyId {
60 KeyId::from(self)
61 }
62}
63
64impl<'a> rcgen::PublicKeyData for PublicKey<'a> {
65 fn der_bytes(&self) -> &[u8] {
66 self.0
67 }
68
69 fn algorithm(&self) -> &'static rcgen::SignatureAlgorithm {
70 &rcgen::PKCS_ECDSA_P256_SHA256
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use crate::{
77 Signature, Signer,
78 keys::{PrivateKey, tests::fixture_key},
79 };
80
81 /// Ensure a fixed key & signature can be used to verify a payload.
82 ///
83 /// It is unlikely the underlying library would break this, but changing the
84 /// parameters used might.
85 #[test]
86 fn test_verify_fixture() {
87 const SIG: &[u8] = &[
88 48, 70, 2, 33, 0, 159, 76, 25, 247, 14, 167, 0, 24, 61, 234, 149, 155, 10, 245, 27,
89 172, 116, 5, 107, 196, 201, 234, 169, 89, 6, 10, 214, 0, 134, 101, 141, 210, 2, 33, 0,
90 208, 252, 87, 7, 41, 104, 204, 68, 230, 200, 114, 145, 230, 146, 74, 188, 121, 72, 16,
91 186, 227, 169, 81, 231, 126, 133, 63, 65, 174, 55, 181, 207,
92 ];
93
94 let key = fixture_key();
95 let sig = Signature::try_from(SIG).expect("valid signature");
96
97 assert!(key.public_key().verify("bananas".as_bytes(), &sig).is_ok());
98 }
99
100 #[test]
101 fn test_non_deterministic_signatures() {
102 const DATA: [u8; 4] = [0x00, 0xCA, 0xFE, 0x42];
103
104 let key = PrivateKey::new();
105
106 let a = key.sign(&DATA);
107 let b = key.sign(&DATA);
108 assert_ne!(a, b);
109 }
110}