Skip to main content

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}