1#![deny(unsafe_code)]
10#![deny(missing_docs)]
11
12use std::fmt;
13
14pub mod cert;
15mod ecdsa;
16pub mod jwk;
17
18use crate::ecdsa::EcdsaSigningKey;
19
20pub use crate::ecdsa::EcdsaAlgorithm;
21
22pub use p256;
23pub use pkcs8;
24pub use signature;
25
26pub struct Signature(Vec<u8>);
31
32impl fmt::Debug for Signature {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 f.debug_tuple("Signature").field(&self.0).finish()
35 }
36}
37
38impl AsRef<[u8]> for Signature {
39 fn as_ref(&self) -> &[u8] {
40 self.0.as_slice()
41 }
42}
43
44impl From<Vec<u8>> for Signature {
45 fn from(value: Vec<u8>) -> Self {
46 Signature(value)
47 }
48}
49
50pub struct SigningError;
52
53pub struct PublicKey(Box<dyn PublicKeyAlgorithm>);
55
56impl PublicKey {
57 pub fn to_jwk(&self) -> crate::jwk::Jwk {
61 self.0.as_jwk()
62 }
63
64 pub fn algorithm(&self) -> pkcs8::AlgorithmIdentifier {
66 self.0.algorithm()
67 }
68
69 pub fn as_bytes(&self) -> Vec<u8> {
71 self.0.as_bytes()
72 }
73}
74
75impl PartialEq for PublicKey {
76 fn eq(&self, other: &Self) -> bool {
77 self.to_jwk() == other.to_jwk()
78 }
79}
80
81impl Eq for PublicKey {}
82
83impl<T> From<Box<T>> for PublicKey
84where
85 T: PublicKeyAlgorithm + 'static,
86{
87 fn from(value: Box<T>) -> Self {
88 PublicKey(value as _)
89 }
90}
91
92impl fmt::Debug for PublicKey {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.debug_tuple("PublicKey").finish()
95 }
96}
97
98impl AsRef<[u8]> for PublicKey {
99 fn as_ref(&self) -> &[u8] {
100 todo!("PublicKey to bytes")
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum SignatureKind {
115 Ecdsa(EcdsaAlgorithm),
117}
118
119impl SignatureKind {
120 pub fn random(&self) -> SigningKey {
122 match self {
123 SignatureKind::Ecdsa(ecdsa) => SigningKey(ecdsa.random().into()),
124 }
125 }
126}
127
128#[derive(Debug, PartialEq, Eq)]
147pub struct SigningKey(InnerSigningKey);
148
149impl SigningKey {
150 pub fn from_pkcs8_pem(data: &str, signature: SignatureKind) -> Result<Self, pkcs8::Error> {
154 match signature {
155 SignatureKind::Ecdsa(algorithm) => Ok(SigningKey(InnerSigningKey::Ecdsa(Box::new(
156 EcdsaSigningKey::from_pkcs8_pem(data, algorithm)?,
157 )))),
158 }
159 }
160
161 pub fn kind(&self) -> SignatureKind {
163 match &self.0 {
164 InnerSigningKey::Ecdsa(key) => key.kind(),
165 }
166 }
167}
168
169impl SigningKey {
170 pub fn public_key(&self) -> PublicKey {
172 self.0.public_key()
173 }
174
175 pub fn as_jwk(&self) -> crate::jwk::Jwk {
179 self.0.as_jwk()
180 }
181
182 pub fn algorithm(&self) -> pkcs8::AlgorithmIdentifier {
184 self.0.algorithm()
185 }
186}
187
188impl pkcs8::EncodePrivateKey for SigningKey {
189 fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
190 self.0.to_pkcs8_der()
191 }
192}
193
194impl signature::Signer<Signature> for SigningKey {
195 fn try_sign(&self, msg: &[u8]) -> Result<Signature, ::ecdsa::Error> {
196 self.0.try_sign(msg)
197 }
198}
199
200impl signature::DigestSigner<sha2::Sha256, Signature> for SigningKey {
201 fn try_sign_digest(&self, digest: sha2::Sha256) -> Result<Signature, ::ecdsa::Error> {
202 self.0.try_sign_digest(digest)
203 }
204}
205
206pub(crate) enum InnerSigningKey {
207 Ecdsa(Box<dyn SigningKeyAlgorithm>),
208 }
210
211impl InnerSigningKey {
212 pub(crate) fn as_jwk(&self) -> crate::jwk::Jwk {
213 match self {
214 InnerSigningKey::Ecdsa(ecdsa) => ecdsa.as_jwk(),
215 }
216 }
217
218 pub(crate) fn public_key(&self) -> PublicKey {
219 match self {
220 InnerSigningKey::Ecdsa(ecdsa) => ecdsa.public_key(),
221 }
222 }
223
224 pub(crate) fn algorithm(&self) -> pkcs8::AlgorithmIdentifier {
225 match self {
226 InnerSigningKey::Ecdsa(ecdsa) => ecdsa.algorithm(),
227 }
228 }
229}
230
231impl From<EcdsaSigningKey> for InnerSigningKey {
232 fn from(value: EcdsaSigningKey) -> Self {
233 InnerSigningKey::Ecdsa(Box::new(value) as _)
234 }
235}
236
237impl PartialEq for InnerSigningKey {
238 fn eq(&self, other: &Self) -> bool {
239 self.public_key() == other.public_key()
240 }
241}
242
243impl Eq for InnerSigningKey {}
244
245impl pkcs8::EncodePrivateKey for InnerSigningKey {
246 fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
247 match self {
248 InnerSigningKey::Ecdsa(key) => key.to_pkcs8_der(),
249 }
250 }
251}
252
253impl signature::Signer<Signature> for InnerSigningKey {
254 fn try_sign(&self, msg: &[u8]) -> Result<Signature, ::ecdsa::Error> {
255 match self {
256 InnerSigningKey::Ecdsa(key) => key.try_sign(msg),
257 }
258 }
259}
260
261impl signature::DigestSigner<sha2::Sha256, Signature> for InnerSigningKey {
262 fn try_sign_digest(&self, digest: sha2::Sha256) -> Result<Signature, ::ecdsa::Error> {
263 match self {
264 InnerSigningKey::Ecdsa(key) => key.try_sign_digest(digest),
265 }
266 }
267}
268
269impl fmt::Debug for InnerSigningKey {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 match self {
272 InnerSigningKey::Ecdsa(_) => f.write_str("ECDSA-Key"),
273 }
274 }
275}
276
277pub(crate) trait SigningKeyAlgorithm:
278 signature::Signer<Signature> + pkcs8::EncodePrivateKey
279{
280 fn as_jwk(&self) -> crate::jwk::Jwk;
281 fn public_key(&self) -> PublicKey;
282 fn try_sign_digest(&self, digest: sha2::Sha256) -> Result<Signature, ::ecdsa::Error>;
283 fn algorithm(&self) -> pkcs8::AlgorithmIdentifier;
284 fn kind(&self) -> SignatureKind;
285}
286
287pub(crate) trait PublicKeyAlgorithm {
288 fn as_jwk(&self) -> crate::jwk::Jwk;
289 fn algorithm(&self) -> pkcs8::AlgorithmIdentifier;
290 fn as_bytes(&self) -> Vec<u8>;
291}
292
293#[cfg(test)]
294mod test {
295 use std::sync::Arc;
296
297 use base64ct::LineEnding;
298 use pkcs8::EncodePrivateKey;
299
300 #[macro_export]
301 macro_rules! key {
302 ($name:tt) => {
303 $crate::test::key(include_str!(concat!(
304 env!("CARGO_MANIFEST_DIR"),
305 "/../reference-keys/",
306 $name,
307 ".pem"
308 )))
309 };
310 }
311
312 pub(crate) fn key(private: &str) -> Arc<crate::SigningKey> {
313 let key = crate::SigningKey::from_pkcs8_pem(
314 private,
315 crate::SignatureKind::Ecdsa(crate::EcdsaAlgorithm::P256),
316 )
317 .unwrap();
318
319 Arc::new(key)
320 }
321
322 #[test]
323 fn roundtrip_key_through_pkcs8() {
324 let key = key!("ec-p255");
325 let pkcs8 = key.to_pkcs8_pem(LineEnding::default()).unwrap();
326 let key2 = crate::SigningKey::from_pkcs8_pem(&pkcs8, key.kind()).unwrap();
327
328 assert_eq!(key.as_ref(), &key2);
329 }
330}