Skip to main content

rc_crypto/keys/
private.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::{
16    rand,
17    signature::{ECDSA_P256_SHA256_ASN1_SIGNING, EcdsaKeyPair, EcdsaSigningAlgorithm, KeyPair},
18};
19
20use crate::{Signature, keys::public::PublicKey, signer::Signer};
21
22pub(crate) const KEY_TYPE: &EcdsaSigningAlgorithm = &ECDSA_P256_SHA256_ASN1_SIGNING;
23
24/// A private ECDSA-P256 key.
25///
26/// # Encoding
27///
28/// These variable-length signatures use SHA256 internally, and are encoded
29/// using ASN.1 wrapped DER bytes as described in [RFC 3279 § 2.2.3].
30///
31/// [RFC 3279 § 2.2.3]: https://tools.ietf.org/html/rfc3279#section-2.2.3
32#[derive(Debug)] // Debug doesn't leak private key
33pub struct PrivateKey {
34    /// An ECDSA-P256 key pair, internally encoded as a PKCS#8 document.
35    key: EcdsaKeyPair,
36}
37
38impl Default for PrivateKey {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl PrivateKey {
45    /// Generate a new, ephemeral key.
46    pub fn new() -> Self {
47        #[cfg(not(feature = "dd-source"))] // Fake non-FIPS in dd-source
48        assert!(
49            aws_lc_rs::try_fips_mode().is_ok(),
50            "crypto module must be in FIPS mode"
51        );
52
53        let rand = rand::SystemRandom::new();
54        let raw =
55            EcdsaKeyPair::generate_pkcs8(KEY_TYPE, &rand).expect("ecdsa key generation failed");
56
57        let key = EcdsaKeyPair::from_pkcs8(KEY_TYPE, raw.as_ref()).expect("invalid pkcs8");
58
59        Self { key }
60    }
61
62    /// Return the [`PublicKey`] derived from this [`PrivateKey`].
63    pub fn public_key(&self) -> PublicKey<'_> {
64        PublicKey::new(self.key.public_key().as_ref())
65    }
66}
67
68impl Signer for PrivateKey {
69    fn sign(&self, data: &[u8]) -> Signature {
70        let rand = rand::SystemRandom::new();
71
72        Signature::from(
73            self.key
74                .sign(&rand, data)
75                .expect("signature generation failed"),
76        )
77    }
78
79    fn public_key(&self) -> PublicKey<'_> {
80        PrivateKey::public_key(self)
81    }
82}
83
84impl rcgen::SigningKey for PrivateKey {
85    fn sign(&self, msg: &[u8]) -> Result<Vec<u8>, rcgen::Error> {
86        Ok(<PrivateKey as Signer>::sign(self, msg).as_ref().to_vec())
87    }
88}
89
90impl rcgen::PublicKeyData for PrivateKey {
91    fn der_bytes(&self) -> &[u8] {
92        self.key.public_key().as_ref()
93    }
94
95    fn algorithm(&self) -> &'static rcgen::SignatureAlgorithm {
96        self.public_key().algorithm()
97    }
98}
99
100#[cfg(test)]
101pub(crate) mod tests {
102    use super::*;
103
104    /// Return a specific private key, deterministic across test runs.
105    pub(crate) fn fixture_key() -> PrivateKey {
106        const PKCS8_KEY: &[u8] = &[
107            48, 129, 135, 2, 1, 0, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72,
108            206, 61, 3, 1, 7, 4, 109, 48, 107, 2, 1, 1, 4, 32, 243, 225, 70, 195, 91, 136, 168,
109            153, 187, 153, 116, 229, 57, 167, 13, 216, 27, 239, 144, 198, 203, 121, 64, 198, 7,
110            111, 75, 160, 18, 140, 203, 253, 161, 68, 3, 66, 0, 4, 191, 188, 109, 191, 201, 131,
111            85, 74, 84, 241, 161, 173, 189, 81, 122, 100, 128, 86, 229, 222, 41, 122, 152, 53, 210,
112            162, 198, 133, 186, 162, 195, 21, 4, 213, 175, 88, 65, 194, 57, 232, 116, 80, 167, 165,
113            193, 161, 175, 12, 225, 178, 55, 131, 212, 251, 75, 94, 140, 105, 227, 223, 67, 234,
114            183, 132,
115        ];
116
117        let key = EcdsaKeyPair::from_pkcs8(KEY_TYPE, PKCS8_KEY).expect("ecdsa key load failed");
118
119        PrivateKey { key }
120    }
121
122    /// Assert a key can be generated without panicking.
123    #[test]
124    fn test_generation() {
125        let key = PrivateKey::default();
126        let _pub = key.public_key();
127    }
128
129    /// Fixture test on the key type + hash algorithm used.
130    ///
131    /// This parameter MUST be FIPS compliant.
132    #[test]
133    fn test_fips_fixture() {
134        assert_eq!(*KEY_TYPE, ECDSA_P256_SHA256_ASN1_SIGNING);
135    }
136}