1use base64::Engine;
12use base64::engine::general_purpose::STANDARD as BASE64;
13use der::asn1::OctetString;
14use der::{Decode, Encode, Sequence};
15use ml_dsa::{EncodedVerifyingKey, MlDsa65};
16use pkcs8::PrivateKeyInfo;
17use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
18use sha2::Digest;
19use spki::der::AnyRef;
20use spki::der::asn1::BitStringRef;
21use spki::{AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo};
22use std::error::Error;
23use subtle::ConstantTimeEq;
24use zeroize::Zeroize;
25
26use crate::pem;
27
28pub const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.3.18");
30
31pub const SECRET_KEY_SIZE: usize = 32;
33
34pub const PUBLIC_KEY_SIZE: usize = 1952;
36
37pub const SIGNATURE_SIZE: usize = 3309;
39
40pub const FINGERPRINT_SIZE: usize = 32;
42
43#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
45struct MlDsa65PrivateKeyInner {
46 seed: OctetString,
47 expanded: OctetString,
48}
49
50#[derive(Clone)]
52pub struct SecretKey {
53 inner: ml_dsa::SigningKey<MlDsa65>,
54 seed: ml_dsa::Seed,
55}
56
57impl Drop for SecretKey {
58 fn drop(&mut self) {
59 self.seed.zeroize();
60 }
61}
62
63impl SecretKey {
64 pub fn generate() -> SecretKey {
66 let mut seed = ml_dsa::Seed::default();
67 getrandom::fill(&mut seed).unwrap();
68
69 let inner = ml_dsa::SigningKey::<MlDsa65>::from_seed(&seed);
70 Self { inner, seed }
71 }
72
73 pub fn from_bytes(seed: &[u8; SECRET_KEY_SIZE]) -> Self {
75 let array = ml_dsa::Seed::try_from(seed.as_slice()).unwrap();
76 let inner = ml_dsa::SigningKey::<MlDsa65>::from_seed(&array);
77 Self { inner, seed: array }
78 }
79
80 pub fn from_der(der: &[u8]) -> Result<Self, Box<dyn Error>> {
82 let info = PrivateKeyInfo::from_der(der)?;
84
85 if info.encoded_len()?.try_into() != Ok(der.len()) {
87 return Err("trailing data in private key".into());
88 }
89 if info.algorithm.oid != OID {
91 return Err("not an ML-DSA-65 private key".into());
92 }
93 let inner_key = MlDsa65PrivateKeyInner::from_der(info.private_key)?;
97
98 let seed: ml_dsa::Seed = inner_key
99 .seed
100 .as_bytes()
101 .try_into()
102 .map_err(|_| "seed not 32 bytes")?;
103 let expanded: [u8; 4032] = inner_key
104 .expanded
105 .as_bytes()
106 .try_into()
107 .map_err(|_| "expanded key not 4032 bytes")?;
108
109 let inner = ml_dsa::SigningKey::<MlDsa65>::from_seed(&seed);
111
112 #[allow(deprecated)] let enc = inner.to_expanded();
114 if enc.as_slice().ct_ne(&expanded).into() {
115 return Err("expanded key does not match seed".into());
116 }
117 Ok(Self { inner, seed })
118 }
119
120 pub fn from_pem(pem_str: &str) -> Result<Self, Box<dyn Error>> {
122 let (kind, data) = pem::decode(pem_str.as_bytes())?;
124 if kind != "PRIVATE KEY" {
125 return Err(format!("invalid PEM tag {}", kind).into());
126 }
127 Self::from_der(&data)
129 }
130
131 pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
133 let mut out = [0u8; 32];
134 out.copy_from_slice(self.seed.as_slice());
135 out
136 }
137
138 pub fn to_der(&self) -> Vec<u8> {
140 #[allow(deprecated)] let enc = self.inner.to_expanded();
142
143 let inner_key = MlDsa65PrivateKeyInner {
144 seed: OctetString::new(self.seed.as_slice()).unwrap(),
145 expanded: OctetString::new(enc.as_slice()).unwrap(),
146 };
147 let inner = inner_key.to_der().unwrap();
148
149 let alg = pkcs8::AlgorithmIdentifierRef {
150 oid: OID,
151 parameters: None::<AnyRef>,
152 };
153 let info = PrivateKeyInfo {
154 algorithm: alg,
155 private_key: &inner,
156 public_key: None,
157 };
158 info.to_der().unwrap()
159 }
160
161 pub fn to_pem(&self) -> String {
163 pem::encode("PRIVATE KEY", &self.to_der())
164 }
165
166 pub fn public_key(&self) -> PublicKey {
168 PublicKey {
169 inner: self.inner.verifying_key(),
170 }
171 }
172
173 pub fn fingerprint(&self) -> Fingerprint {
176 self.public_key().fingerprint()
177 }
178
179 pub fn sign(&self, message: &[u8], ctx: &[u8]) -> Signature {
181 let sig = self.inner.sign_deterministic(message, ctx).unwrap();
182 let encoded = sig.encode();
183 let slice: &[u8] = encoded.as_ref();
184 Signature(slice.try_into().unwrap())
185 }
186}
187
188#[derive(Debug, Clone)]
190pub struct PublicKey {
191 inner: ml_dsa::VerifyingKey<MlDsa65>,
192}
193
194impl PublicKey {
195 pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_SIZE]) -> Self {
197 let enc = EncodedVerifyingKey::<MlDsa65>::try_from(bytes.as_slice()).unwrap();
198 let inner = ml_dsa::VerifyingKey::<MlDsa65>::decode(&enc);
199 Self { inner }
200 }
201
202 pub fn from_der(der: &[u8]) -> Result<Self, Box<dyn Error>> {
204 let info: SubjectPublicKeyInfo<AlgorithmIdentifier<AnyRef>, BitStringRef> =
205 SubjectPublicKeyInfo::from_der(der)?;
206
207 if info.encoded_len()?.try_into() != Ok(der.len()) {
209 return Err("trailing data in public key".into());
210 }
211 if info.algorithm.oid != OID {
212 return Err("not an ML-DSA-65 public key".into());
213 }
214 let key = info.subject_public_key.as_bytes().unwrap();
215 if key.len() != 1952 {
216 return Err("public key not 1952 bytes".into());
217 }
218 let bytes: [u8; 1952] = key.try_into()?;
219 let enc = EncodedVerifyingKey::<MlDsa65>::try_from(bytes.as_slice()).unwrap();
220 let inner = ml_dsa::VerifyingKey::<MlDsa65>::decode(&enc);
221 Ok(Self { inner })
222 }
223
224 pub fn from_pem(pem_str: &str) -> Result<Self, Box<dyn Error>> {
226 let (kind, data) = pem::decode(pem_str.as_bytes())?;
227 if kind != "PUBLIC KEY" {
228 return Err(format!("invalid PEM tag {}", kind).into());
229 }
230 Self::from_der(&data)
231 }
232
233 pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_SIZE] {
235 let enc = self.inner.encode();
236 let mut out = [0u8; 1952];
237 out.copy_from_slice(enc.as_slice());
238 out
239 }
240
241 pub fn to_der(&self) -> Vec<u8> {
243 let enc = self.inner.encode();
244 let bytes = enc.as_slice();
245
246 let alg = AlgorithmIdentifier::<AnyRef> {
247 oid: OID,
248 parameters: None::<AnyRef>,
249 };
250 let info = SubjectPublicKeyInfo::<AnyRef, BitStringRef> {
251 algorithm: alg,
252 subject_public_key: BitStringRef::from_bytes(bytes).unwrap(),
253 };
254 info.to_der().unwrap()
255 }
256
257 pub fn to_pem(&self) -> String {
259 pem::encode("PUBLIC KEY", &self.to_der())
260 }
261
262 pub fn fingerprint(&self) -> Fingerprint {
265 let mut hasher = sha2::Sha256::new();
266 hasher.update(self.inner.encode().as_slice());
267 Fingerprint(hasher.finalize().into())
268 }
269
270 pub fn verify(
272 &self,
273 message: &[u8],
274 ctx: &[u8],
275 signature: &Signature,
276 ) -> Result<(), ml_dsa::Error> {
277 let sig = ml_dsa::Signature::<MlDsa65>::try_from(signature.to_bytes().as_slice())?;
278 if self.inner.verify_with_context(message, ctx, &sig) {
279 Ok(())
280 } else {
281 Err(ml_dsa::Error::default())
282 }
283 }
284}
285
286impl Serialize for PublicKey {
287 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
288 serializer.serialize_str(&BASE64.encode(self.to_bytes()))
289 }
290}
291
292impl<'de> Deserialize<'de> for PublicKey {
293 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
294 let s = String::deserialize(deserializer)?;
295 let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
296 let arr: [u8; PUBLIC_KEY_SIZE] = bytes
297 .try_into()
298 .map_err(|_| de::Error::custom("invalid public key length"))?;
299 Ok(PublicKey::from_bytes(&arr))
300 }
301}
302
303#[cfg(feature = "cbor")]
304impl crate::cbor::Encode for PublicKey {
305 fn encode_cbor(&self) -> Vec<u8> {
306 self.to_bytes().encode_cbor()
307 }
308}
309
310#[cfg(feature = "cbor")]
311impl crate::cbor::Decode for PublicKey {
312 fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
313 let bytes = <[u8; PUBLIC_KEY_SIZE]>::decode_cbor(data)?;
314 Ok(Self::from_bytes(&bytes))
315 }
316
317 fn decode_cbor_notrail(
318 decoder: &mut crate::cbor::Decoder<'_>,
319 ) -> Result<Self, crate::cbor::Error> {
320 let bytes = decoder.decode_bytes_fixed::<PUBLIC_KEY_SIZE>()?;
321 Ok(Self::from_bytes(&bytes))
322 }
323}
324
325#[derive(Debug, Clone, PartialEq, Eq)]
327pub struct Signature([u8; SIGNATURE_SIZE]);
328
329impl Signature {
330 pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Self {
332 Self(*bytes)
333 }
334
335 pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] {
337 self.0
338 }
339}
340
341impl Serialize for Signature {
342 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
343 serializer.serialize_str(&BASE64.encode(self.to_bytes()))
344 }
345}
346
347impl<'de> Deserialize<'de> for Signature {
348 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
349 let s = String::deserialize(deserializer)?;
350 let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
351 let arr: [u8; SIGNATURE_SIZE] = bytes
352 .try_into()
353 .map_err(|_| de::Error::custom("invalid signature length"))?;
354 Ok(Signature::from_bytes(&arr))
355 }
356}
357
358#[cfg(feature = "cbor")]
359impl crate::cbor::Encode for Signature {
360 fn encode_cbor(&self) -> Vec<u8> {
361 self.to_bytes().encode_cbor()
362 }
363}
364
365#[cfg(feature = "cbor")]
366impl crate::cbor::Decode for Signature {
367 fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
368 let bytes = <[u8; SIGNATURE_SIZE]>::decode_cbor(data)?;
369 Ok(Self::from_bytes(&bytes))
370 }
371
372 fn decode_cbor_notrail(
373 decoder: &mut crate::cbor::Decoder<'_>,
374 ) -> Result<Self, crate::cbor::Error> {
375 let bytes = decoder.decode_bytes_fixed::<SIGNATURE_SIZE>()?;
376 Ok(Self::from_bytes(&bytes))
377 }
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq)]
382pub struct Fingerprint([u8; FINGERPRINT_SIZE]);
383
384impl Fingerprint {
385 pub fn from_bytes(bytes: &[u8; FINGERPRINT_SIZE]) -> Self {
387 Self(*bytes)
388 }
389
390 pub fn to_bytes(&self) -> [u8; FINGERPRINT_SIZE] {
392 self.0
393 }
394}
395
396impl Serialize for Fingerprint {
397 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
398 serializer.serialize_str(&BASE64.encode(self.to_bytes()))
399 }
400}
401
402impl<'de> Deserialize<'de> for Fingerprint {
403 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
404 let s = String::deserialize(deserializer)?;
405 let bytes = BASE64.decode(&s).map_err(de::Error::custom)?;
406 let arr: [u8; FINGERPRINT_SIZE] = bytes
407 .try_into()
408 .map_err(|_| de::Error::custom("invalid fingerprint length"))?;
409 Ok(Fingerprint::from_bytes(&arr))
410 }
411}
412
413#[cfg(feature = "cbor")]
414impl crate::cbor::Encode for Fingerprint {
415 fn encode_cbor(&self) -> Vec<u8> {
416 self.to_bytes().encode_cbor()
417 }
418}
419
420#[cfg(feature = "cbor")]
421impl crate::cbor::Decode for Fingerprint {
422 fn decode_cbor(data: &[u8]) -> Result<Self, crate::cbor::Error> {
423 let bytes = <[u8; FINGERPRINT_SIZE]>::decode_cbor(data)?;
424 Ok(Self::from_bytes(&bytes))
425 }
426
427 fn decode_cbor_notrail(
428 decoder: &mut crate::cbor::Decoder<'_>,
429 ) -> Result<Self, crate::cbor::Error> {
430 let bytes = decoder.decode_bytes_fixed::<FINGERPRINT_SIZE>()?;
431 Ok(Self::from_bytes(&bytes))
432 }
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438
439 #[test]
444 fn test_secretkey_pem_codec() {
445 let input = "\
448-----BEGIN PRIVATE KEY-----
449MIIP/gIBADALBglghkgBZQMEAxIEgg/qMIIP5gQgHIyGEFLAqH3CNU6XhuKXm3S9
450kMz0ylk7Yhzc3VrDPPkEgg/AWMwxq+siDxIbH4H+lVy1REFr+KseX5CDOy+Uy0p7
4517E2ryX572rYV8/OIDSXG8nSmyTW8ucEHtTNjPBcFFDZbAS3JixztNB5DwB0HAWZj
452fy0BAhys/7zI3YacC26j7mzfFJXljY9Mrov0UQSyVlLXurOBZIXCPeBTxLTXkKJf
453QphwQHcSYBNAMAWECBA0MyBoYUQYElcoIjAxYicCFFUSFlEocxhWhkYlVIACOIUY
454BzSCBoQBJBdzFzeAVBVUImcoMTBwgGAEOHAwVoRDVxExRnhjYjJYiECCBIZVIXRQ
455YSE2EQQRcINEaACCdngII4ZBNhZTVzOIFnOBSHJWFyNwdGREJHUDIwZHRoUBM0FT
456dEFWiIdwdGRwFiZjUCJUGCIYNoUAIoVWEDQ0cVQWUHMWFWcVcAhlYAJ4VjcAQBQW
457dngzQkImIzQwKARBgRYSAiNTSDY3cBZTcRiIFhAmSBNTMkZ3CCEWZIBkFChwJncF
458hoY0ZCdjc2UGhAgYcDYnZzFBcGYUFARzAQNwQXIkBhaARQd2dyOCg0JgOBEGhnYW
459NkQSJhQkWGYGQ3gIZ4YzWDIXgIZoQig1cxhWNjAoRUF3QwZRIgdYKBZgcWBiOABk
460YiWERyZogocBYhIWhRFSNlMxYWMHQiZBRGNzcyUGFihBBBRWYFIzIUFWVIQSdhcY
461eCgnZHNEIAFHE1d3iHSDU0RxhjNHIwEoETF0Q4JFhTJFFXAREVKIhXQAdhAQYBKA
462YEYIFCeGJFQ3NlU4MyYFMHFkZDFzcCVVNwBFAAc0UxCCJGJXcjByEmQARoMzcUZR
463BCSIOFUGEQU0AwYSNmR3aBWHV3ACQSImRCFIiFVlNzEQhRBFQACDV2N4BiFwJSgF
464eBeFN4cEUXGFEDgERFJhYBhzRngUBihhAxU0ERIIczI4JYiGBiBiU4E2R1YFSFUm
465iDJGQCSARxB4JkMVSFI0ZyV2IjV1FIQyRYYkU3EDVHJnFzQzdQEiZlgwFSMGQ1Az
466ZAB1cjNgYoZAM2BUUjIlSDKCUwKHJnQkMigSZld3UTAxVgJQgxJSWFM3QwA0gYM3
467VShYdldQKAh2gRhlMiEDdxUxeDYSNkciVghlBlWDUQg4ZjdoVyNCRhNIMwRRgkUS
468JVA0N0OISCI4QkFAhFFBZYBWBxghNwg1FRUXUVV4FyAmA3EidXNyRVZRRFA2gjZC
469NjQzUFNERHOHdARHF4UwYFhGcoIFF0hkhXAQaCY4SEMXFiJkiGKCWFI3KEFjY4RE
470EwRlWIVEA0FmhoEXY4IlIRUICBcAVCNTZTdXdwMTN2EQcSEABjc2QSUnYWSEgTYX
471Y2MCJoZnhTMCJECBJ3eFdnhWAmJnV2SGd4RIRzQnAydEgkUEgWYyNnGIQQYYJXFC
472ZVFAgHRwRVImJDZSZlZlUxMIEiU4I2cWFmhkVRYGIyEVKANmGCVmgXAzQxMQgHdm
473GIAIOHhhgGKEcYCBAQRIEDdWFoZid1aCYTNiU2QWIyM4cyJHZBckhWKGJoAiKAdy
474cBdxN1YIYhJyEzQ3VAFnRAMHAYcAgERSJjMFEYYUcHdRJXUzAGVgUiJgUlIWAoGA
475ZzJUdyJUhkNIc2CFBGeGYlUiJ0glZkMXYISGNChDhUd2UYJYJ0A0c2gihYOGQoAE
476N4chcGFVhFSBMhQCAUgjIjECQHhyAyhlACNjdHAkBUeFU3EmEng1RxEBAiQRdQQo
477FCRmeIiFFwh0FCMEAlhAEYA1YGZEZldwdVgFaBQgQCU2ByRUOIMFBxh3YIaAFwN0
478M4NGEXJ3QzERgXSBMgJEVTEYIyMTWHRAE3JwEidRJxYUiHGGdVAgV4dAhDQDMyQi
479QnNRiBAYdBADVXY0BxeHYAVYgWcIJABkRgRhcyMWFxRQJ2NXUUJUeHRnIVJyNCh4
480cmEyBwQldIZGZ3ZYdxUEJXdYgINIMxgGhwNXF1JYYShGYQUkVkAlQHMmFThIc0FH
481BTQ3JCdFdyJAgkhBCAdnYnEmJQAlJiEiEVBDARNAI0iGE2QBg3FTIUITcSJAgkMx
482NodYOEGFgHFwdkKFUxMyU1NYP7UaHWZSiHjAkCzNiLvdMni1St9g4ezd7VjgQB8Q
4830IC5CABqXDWIYU47BVWxHg17oRrVqHdCDlsZey5vrwKsGdJ2A/RLOzcdgNKANAJr
484tV8ZuRPWWeQxeI86g010zAlNHBBGh+imntaiOcd4RTVEqKfe8Qvj3PqLv2vakWSq
485Lg1Egv2IK9ykZ/L70QeVviuQ3dICrVghNzA4p6t0SpI+KPH/FG5C1wZ5fuQdrjv5
486cuhaPI1sgjBji/W7uSEcpXNfJpjNx9sMQpHXLxyBZhS79M8ogRhE/Cg39LpPlB7e
487kP0ivbAiESsoWjAYOzH9TXfLT3K3WCdlI5TqM/6TroPGmVxkVU4h0Jnt3DwUPG2l
488MVMXSiTaIJKBJ6JIKYkCazMpr0eRBWtB8UlFbNWiDQG5W5stlbquUvIsx4zIJACr
489d6jFgEiR6NPC6r0beTZMYpchzcVhFX43O3PmVfj4AYQhU47RGzQmogO9FGeUwz4t
490A/XeVfAQT+GCtzch5lD2az9pYZh5jJIu+DE+GWw01Pj8nFCon9+bqZtRqzaR2/qa
491319dIZ5IrS0Go4fjtE7f/6v9U1N01XavvL7oS/MfiuTniOcB7F09RX6qlX36LyoF
492arV8RG5dPp0aOh1vAXlnVjlbxeOUCsgwxa82q/7qWPN78CQh93RXZJhHWVMIm0cQ
493MOZ5rLwcsRN4nxzx83uA4zOyiQ4VN8N76W1htVqwXfwz9Op/IaN2nJY6MKYpEEro
494yOAr/B5LbvNYVZvFAuvATne6Pw+M95sh/qzt/T+RDp3EUoMi8bDllcwACiSTWT+z
4952K7/vlGro5zX+JzPAiXPo8u8lZXLcazy2FtPZJPVY6rSJMnc0PPdq11xNqy3z4eX
496y+NnIZYdsoQF1cVKWkm1l772h5crJr6yaDCh8eHSopHhtxUyYxYRjhJdeZGvzevF
497+n57zRIROnAvZ3sW30unUGRPuQxqvSd52QoL4MFMyUrXSXLl7YBOhJNtzh7rIWhl
498W81GMlXd03A7ZdRCzGEB3BhVjqty44SY0pcmLAd95hZd/XsYo5geiFEl1Vjod2go
499Gc4RPTARnN7SVaTyOoYHUdsfDOTCz5oVndGzsLO5FSYjmRL092w9jPaDcMkqP25z
500sk89UWHRDt54R+7e0Vy/bHet5EmVBI2mGdkYQtUDn4UXeBQkwKCzjZIyQa2d9rP8
501/qbtSk57ju9i80hGkSsrQTqikPxmqJ8Who6xQCgxSZJqLjB+8hRJFRtjCl3c0dl5
502zB0Pv3vyqMREstSkD4EHAtTcLPssg3pkdcBch9Gxf6DUvgOrkjK8eQrBd2CZNq10
503EYXVPZJIx7x7gv52TasLxAUduJD5mWomtVoz8TWfvSoIwnNgmmUqDddcfqw7C2d6
504zP9R9aiJjkHDdPR4AQ+G5NWEfrA+2645er223rWIkQxn1tKh3wrPxt9/rkuLCbH4
505RPUi+8ClaYv31h2QY0MMtkxq+ZTkXMss0+IvGagNoQFBif9WWvFcP08PueOAYNvO
506eIhTSRRY+UtT3uN+NxLswmd53PB5+nncWu7VuVwBz0qGTMLlc5pSS2XaZ3hIcJ38
507hBvsTSltzVmAHJhNMOGJWAaHmCccUHY+5cmazbatZ5HweSo/0/7uXxILtRC39Mes
508R+XO5TdlXHvykq0tDoD/46+SLBrAqM5gOH5qD9jOEgtnwNvQ8CvNCkzQW57PnA9F
509fMY9/xRt8H+OvIVQgwiwSqtDbgtfGdco1VKeLxCngNFpsczve/l8dtrJCaeI3lDD
510C3+1ws1yZTDc49Q7rZMskPbIxhHvsDxnf71iE5iYma1VOnmUpKndKqgJ8RhSqbDm
511u6R507om0qBnc9f7QxBg5dfaYvEky9fRUOtnzoM9cO9cbyd58gxqP0SdyLR7u2Bs
512wRjX0+rHJtarc5kFQ/cPIhLDxVdObL25VlB5QBOFJelZF/zPA1ixzEeqtwmIvdiW
513S7DJjz+1cYGQwsCn8X9jQtMz8MecrnaYmv9bGssW5rB8souzBuA7N1/rLQIAX468
514PscKi6ZaUB5z/LWfUHo/vAL2D049iTiVzSI8/JDqwIF5oUQuH4CPjcPt0/oWN5vU
5150t0toTSlLhRfqBSQYhMcjZJzvI4ET/QZcmV2GqqDRoVAuRCs6Eds608Ao3wCPK58
516Mh9/dx+rGAiW8b8dfeEZ7Wa8B9WV8sXPZECOrTDgsRTE20cuh3+I18bVFI4GJrXl
517c30ex1NxwaEf91ehaZX+1esYcaYPqETaR6zuNoyQJ18jLlCRvd2D/PSTYD3XKW+X
518yhlYO0N3DP7wzVcN8mMMNUN26bOvaDHVHpsDFwkhZKRRKibykOsxOnKYtS0rjPV1
519F5ULdy3maQ/5RnhbBoiC+hhrXBZU8SZuk0AcPEtr82Wy3KQ4c5VnJnXk6u5aLD51
520jG7FaZ1gH+gXtNGqmBqYyXXyn93XuIPASgymrCTOo2g11vQdPdHz3wbTMNHMhJIV
521MLc97nexIDIeJqM+spcsXnU8ih5Q+rfNbG58o0V7xGc5Ad6Ge4y6E5z9nQwxoaVi
522aNtt6elVckuhpqyoZu2KuyVmpl7I80/jqx4p+5+YqNyBRg/TiKbs3SsFFlz8L/KR
523mK2SAk4es0pQGpNYbFY3iZO8mxN2fxCZ4Ni3Ma5w+W3RI3uYzgME3x98jcsR1IMZ
5245SES9BWInF6oWRIaeXyxlID7MXmsMIfz6JK1Hq4DqP01bZJSU+1oufKgd6DoS4rb
525FHfO81rhYW42gh/GDdZ8VN5Nk+aIPiVXO6GRdUKBsgwzNOnYmzE1+RNVI1GJ1YIG
5263O76ThBq8MmqS4O45XI95881MORMB5+l5O3vTSP+pxpfwMb7ZLvMwga81hXuI8FP
527IylA/RMde20OyCQGVzTS7XMTSivea+jeTTrcdRvpwRDzXdBa7hkjpWGdKYXdqOgd
528c6oKLcKUlXCiravzeBZi5Reqd3Krp6z7M3h9l0HaX2rL9RMC5i7FCvFwqB1ioJ/q
529EnMkkHEItUyVgXeHMmoCJxpZEaKnbzwCfEp1irJy4UymGZ+xGOnn2EfMbg067zLj
530JDeBIiG/XCppguLxSNRDgq3BLZwJpCdEjGCv+uk9g48zo2Ml9CUvz6W6al5BhP39
531/qjxtbPtvnEgSq/FMuMPr7M92IZ3FYhyMrP6bNgeI49IIrokN52V3A2i8Y6lqsMY
532QNanuvWmFjxUx1CQjiSeL3lHV/0jV8ORbK66TsL7u08NL0L6tNTd8D0/bvGP11Q8
533OVQJ+kOjdbjIYkfHMDJwzENV/471+KoSRdq90htxRfN+BrtujszxMvXKG04wpuoV
534sufdPOOp/GHJAqEQz7n/Owe/
535-----END PRIVATE KEY-----";
536
537 let key = SecretKey::from_pem(input).unwrap();
538 assert_eq!(key.to_pem().trim(), input.trim());
539 }
540
541 #[test]
546 fn test_publickey_pem_codec() {
547 let input = "\
550-----BEGIN PUBLIC KEY-----
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
5929w5RU0Pq
593-----END PUBLIC KEY-----";
594
595 let key = PublicKey::from_pem(input).unwrap();
596 assert_eq!(key.to_pem().trim(), input.trim());
597 }
598
599 #[test]
604 fn test_secretkey_der_codec() {
605 let input = "\
60930820ffe020100300b060960864801650304031204820fea30820fe60420
6101c8c861052c0a87dc2354e9786e2979b74bd90ccf4ca593b621cdcdd5ac3
6113cf904820fc058cc31abeb220f121b1f81fe955cb544416bf8ab1e5f9083
6123b2f94cb4a7bec4dabc97e7bdab615f3f3880d25c6f274a6c935bcb9c107
613b533633c170514365b012dc98b1ced341e43c01d070166637f2d01021cac
614ffbcc8dd869c0b6ea3ee6cdf1495e58d8f4cae8bf45104b25652d7bab381
6156485c23de053c4b4d790a25f429870407712601340300584081034332068
616614418125728223031622702145512165128731856864625548002388518
617073482068401241773173780541554226728313070806004387030568443
618571131467863623258884082048655217450612136110411708344680082
619767808238641361653573388167381487256172370746444247503230647
620468501334153744156888770746470162663502254182218368500228556
621103434715416507316156715700865600278563700401416767833424226
622233430280441811612022353483637701653711888161026481353324677
623082116648064142870267705868634642763736506840818703627673141
624706614140473010370417224061680450776772382834260381106867616
625364412261424586606437808678633583217808668422835731856363028
626454177430651220758281660716062380064622584472668828701621216
627851152365331616307422641446373732506162841041456605233214156
628548412761718782827647344200147135777887483534471863347230128
629113174438245853245157011115288857400761010601280604608142786
630245437365538332605307164643173702555370045000734531082246257
631723072126400468333714651042488385506110534030612366477681587
632577002412226442148885565373110851045400083576378062170252805
633781785378704517185103804445261601873467814062861031534111208
634733238258886062062538136475605485526883246402480471078264315
635485234672576223575148432458624537103547267173433750122665830
636152306435033640075723360628640336054523225483282530287267424
637322812665777513031560250831252585337430034818337552858765750
638280876811865322103771531783612364722560865065583510838663768
639572342461348330451824512255034374388482238424140845141658056
640071821370835151517515578172026037122757372455651445036823642
641363433505344447387740447178530605846728205174864857010682638
642484317162264886282585237284163638444130465588544034166868117
643638225211508081700542353653757770313376110712100063736412527
644616484813617636302268667853302244081277785767856026267576486
645778448473427032744824504816632367188410618257142655140807470
646455226243652665665531308122538236716166864551606232115280366
647182566817033431310807766188008387861806284718081010448103756
648168662775682613362536416232338732247641724856286268022280772
649701771375608621272133437540167440307018700804452263305118614
650707751257533006560522260525216028180673254772254864348736085
651046786625522274825664317608486342843854776518258274034736822
652858386428004378721706155845481321402014823223102407872032865
653002363747024054785537126127835471101022411750428142466788885
654170874142304025840118035606644665770755805681420402536072454
655388305071877608680170374338346117277433111817481320244553118
656232313587440137270122751271614887186755020578740843403332422
657427351881018741003557634071787600558816708240064460461732316
658171450276357514254787467215272342878726132070425748646677658
659771504257758808348331806870357175258612846610524564025407326
660153848734147053437242745772240824841080767627126250025262122
661115043011340234886136401837153214213712240824331368758384185
6628071707642855313325353583fb51a1d66528878c0902ccd88bbdd3278b5
6634adf60e1ecdded58e0401f10d080b908006a5c3588614e3b0555b11e0d7b
664a11ad5a877420e5b197b2e6faf02ac19d27603f44b3b371d80d28034026b
665b55f19b913d659e431788f3a834d74cc094d1c104687e8a69ed6a239c778
666453544a8a7def10be3dcfa8bbf6bda9164aa2e0d4482fd882bdca467f2fb
667d10795be2b90ddd202ad5821373038a7ab744a923e28f1ff146e42d70679
6687ee41dae3bf972e85a3c8d6c8230638bf5bbb9211ca5735f2698cdc7db0c
6694291d72f1c816614bbf4cf28811844fc2837f4ba4f941ede90fd22bdb022
670112b285a30183b31fd4d77cb4f72b75827652394ea33fe93ae83c6995c64
671554e21d099eddc3c143c6da53153174a24da20928127a2482989026b3329
672af4791056b41f149456cd5a20d01b95b9b2d95baae52f22cc78cc82400ab
67377a8c5804891e8d3c2eabd1b79364c629721cdc561157e373b73e655f8f8
674018421538ed11b3426a203bd146794c33e2d03f5de55f0104fe182b73721
675e650f66b3f696198798c922ef8313e196c34d4f8fc9c50a89fdf9ba99b51
676ab3691dbfa9adf5f5d219e48ad2d06a387e3b44edfffabfd535374d576af
677bcbee84bf31f8ae4e788e701ec5d3d457eaa957dfa2f2a056ab57c446e5d
6783e9d1a3a1d6f01796756395bc5e3940ac830c5af36abfeea58f37bf02421
679f774576498475953089b471030e679acbc1cb113789f1cf1f37b80e333b2
680890e1537c37be96d61b55ab05dfc33f4ea7f21a3769c963a30a629104ae8
681c8e02bfc1e4b6ef358559bc502ebc04e77ba3f0f8cf79b21feacedfd3f91
6820e9dc4528322f1b0e595cc000a2493593fb3d8aeffbe51aba39cd7f89ccf
6830225cfa3cbbc9595cb71acf2d85b4f6493d563aad224c9dcd0f3ddab5d71
68436acb7cf8797cbe36721961db28405d5c54a5a49b597bef687972b26beb2
6856830a1f1e1d2a291e1b715326316118e125d7991afcdebc5fa7e7bcd1211
6863a702f677b16df4ba750644fb90c6abd2779d90a0be0c14cc94ad74972e5
687ed804e84936dce1eeb2168655bcd463255ddd3703b65d442cc6101dc1855
6888eab72e38498d297262c077de6165dfd7b18a3981e885125d558e8776828
68919ce113d30119cded255a4f23a860751db1f0ce4c2cf9a159dd1b3b0b3b9
6901526239912f4f76c3d8cf68370c92a3f6e73b24f3d5161d10ede7847eede
691d15cbf6c77ade44995048da619d91842d5039f8517781424c0a0b38d9232
69241ad9df6b3fcfea6ed4a4e7b8eef62f34846912b2b413aa290fc66a89f16
693868eb140283149926a2e307ef21449151b630a5ddcd1d979cc1d0fbf7bf2
694a8c444b2d4a40f810702d4dc2cfb2c837a6475c05c87d1b17fa0d4be03ab
6959232bc790ac177609936ad741185d53d9248c7bc7b82fe764dab0bc4051d
696b890f9996a26b55a33f1359fbd2a08c273609a652a0dd75c7eac3b0b677a
697ccff51f5a8898e41c374f478010f86e4d5847eb03edbae397abdb6deb588
698910c67d6d2a1df0acfc6df7fae4b8b09b1f844f522fbc0a5698bf7d61d90
69963430cb64c6af994e45ccb2cd3e22f19a80da1014189ff565af15c3f4f0f
700b9e38060dbce788853491458f94b53dee37e3712ecc26779dcf079fa79dc
7015aeed5b95c01cf4a864cc2e5739a524b65da677848709dfc841bec4d296d
702cd59801c984d30e18958068798271c50763ee5c99acdb6ad6791f0792a3f
703d3feee5f120bb510b7f4c7ac47e5cee537655c7bf292ad2d0e80ffe3af92
7042c1ac0a8ce60387e6a0fd8ce120b67c0dbd0f02bcd0a4cd05b9ecf9c0f45
7057cc63dff146df07f8ebc85508308b04aab436e0b5f19d728d5529e2f10a7
70680d169b1ccef7bf97c76dac909a788de50c30b7fb5c2cd726530dce3d43b
707ad932c90f6c8c611efb03c677fbd6213989899ad553a7994a4a9dd2aa809
708f11852a9b0e6bba479d3ba26d2a06773d7fb431060e5d7da62f124cbd7d1
70950eb67ce833d70ef5c6f2779f20c6a3f449dc8b47bbb606cc118d7d3eac7
71026d6ab73990543f70f2212c3c5574e6cbdb956507940138525e95917fccf
7110358b1cc47aab70988bdd8964bb0c98f3fb5718190c2c0a7f17f6342d333
712f0c79cae76989aff5b1acb16e6b07cb28bb306e03b375feb2d02005f8ebc
7133ec70a8ba65a501e73fcb59f507a3fbc02f60f4e3d893895cd223cfc90ea
714c08179a1442e1f808f8dc3edd3fa16379bd4d2dd2da134a52e145fa81490
71562131c8d9273bc8e044ff4197265761aaa83468540b910ace8476ceb4f00
716a37c023cae7c321f7f771fab180896f1bf1d7de119ed66bc07d595f2c5cf
71764408ead30e0b114c4db472e877f88d7c6d5148e0626b5e5737d1ec75371
718c1a11ff757a16995fed5eb1871a60fa844da47acee368c90275f232e5091
719bddd83fcf493603dd7296f97ca19583b43770cfef0cd570df2630c354376
720e9b3af6831d51e9b0317092164a4512a26f290eb313a7298b52d2b8cf575
72117950b772de6690ff946785b068882fa186b5c1654f1266e93401c3c4b6b
722f365b2dca4387395672675e4eaee5a2c3e758c6ec5699d601fe817b4d1aa
723981a98c975f29fddd7b883c04a0ca6ac24cea36835d6f41d3dd1f3df06d3
72430d1cc84921530b73dee77b120321e26a33eb2972c5e753c8a1e50fab7cd
7256c6e7ca3457bc4673901de867b8cba139cfd9d0c31a1a56268db6de9e955
726724ba1a6aca866ed8abb2566a65ec8f34fe3ab1e29fb9f98a8dc81460fd3
72788a6ecdd2b05165cfc2ff29198ad92024e1eb34a501a93586c56378993bc
7289b13767f1099e0d8b731ae70f96dd1237b98ce0304df1f7c8dcb11d48319
729e52112f415889c5ea859121a797cb19480fb3179ac3087f3e892b51eae03
730a8fd356d925253ed68b9f2a077a0e84b8adb1477cef35ae1616e36821fc6
7310dd67c54de4d93e6883e25573ba191754281b20c3334e9d89b3135f91355
732235189d58206dceefa4e106af0c9aa4b83b8e5723de7cf3530e44c079fa5
733e4edef4d23fea71a5fc0c6fb64bbccc206bcd615ee23c14f232940fd131d
7347b6d0ec824065734d2ed73134a2bde6be8de4d3adc751be9c110f35dd05a
735ee1923a5619d2985dda8e81d73aa0a2dc2949570a2adabf3781662e517aa
7367772aba7acfb33787d9741da5f6acbf51302e62ec50af170a81d62a09fea
737127324907108b54c95817787326a02271a5911a2a76f3c027c4a758ab272
738e14ca6199fb118e9e7d847cc6e0d3aef32e32437812221bf5c2a6982e2f1
73948d44382adc12d9c09a427448c60affae93d838f33a36325f4252fcfa5ba
7406a5e4184fdfdfea8f1b5b3edbe71204aafc532e30fafb33dd88677158872
74132b3fa6cd81e238f4822ba24379d95dc0da2f18ea5aac31840d6a7baf5a6
742163c54c750908e249e2f794757fd2357c3916caeba4ec2fbbb4f0d2f42fa
743b4d4ddf03d3f6ef18fd7543c395409fa43a375b8c86247c7303270cc4355
744ff8ef5f8aa1245dabdd21b7145f37e06bb6e8eccf132f5ca1b4e30a6ea15
745b2e7dd3ce3a9fc61c902a110cfb9ff3b07bf"
746 .trim()
747 .replace("\n", "");
748
749 let der = hex::decode(&input).unwrap();
750 let key = SecretKey::from_der(&der).unwrap();
751 assert_eq!(hex::encode(key.to_der()), input);
752 }
753
754 #[test]
759 fn test_publickey_der_codec() {
760 let input = "\
764308207b2300b0609608648016503040312038207a10058cc31abeb220f12
7651b1f81fe955cb544416bf8ab1e5f90833b2f94cb4a7bec4d233a14ca86b6
7665061675fb6d1d63453a0b83a3b38db3d96737d7c36573538209609b8b430
767613624bf08de2d70b5ff1542fc4ae0ee3f99b18febecd97a47277c35ce35
76872c96250c7f8b3984a1b65ce5f5fc3a541b418a6e37a59b3454b3c8ebc94
769fb450160d404c10a2afd721f8ee29c90194919bf852d445adee0635ff4e1
7700ae3d9d2419bcf99903112b3c3f897c0f918b8a3879d1cd88a89daee61d2
77177f311e57abde344530236d0a784aba1c8c0f12d9a756256a392fc782463
7728549f2b6f4ca979b6f055a97332df266cd57c4f1f0eec7ec57b30049b1e5
7731bd925679debe71271041a1bff25044bc2ccdd01c0c4930683cd7f4887ad
774e6aea3841f80da75f01d090aa073a67be8c825138d6e2b1cf0cdb19fbc83
77591dbc45fff69f4ff4c2e60f110a24f5c1e118e8f9369b3d2562194e6747c
77666154ba398134bdda177aca7637236e754b3089ddaf9edbc59abcb52be5e
777f34aa5dc2ef86e9f6c168a1a34f1d6efc4f02c4061af5baba25eb3d801ba
778cd2d40d05e377dc2e9dde162e4f38043c0ee4be9307da863bc7a069707eb
7799363675d84548b77b9a00b6e18be146b0e1ea46d3e130f5bf6d8454b7541
7800f4c334a475e66745877a80f5d8f99d2d81a72b00edea72519dfeff774d4
7819793b40af1767ac0c8b25ab021fa9a9256d4ef8c0dc8c115a651f669475f
7820b98c1e71cda8b253cd765f1280b3506d7b30e7f9bf85afcd7ec8f06e649
7832a48ff59f92e357801d5a3ca359efede736caffb5b79a09db82c9f8c6636
784f67163833e288530692ce0a74a1754206275cde79a1dbcc798e2bc873831
7857d4411e7a4290be027a72210abb906ee18f0ed575a7e321196406f959c39
786685d6c4c8de7bcf0589823690ca87b0baeea09e57cb0e4bdebbfe1e7cdc5
787105977b608d1dd0ed4c27d7c4c51da09c0239adc5cda1a93157e8039f6ab
788c55b9d9d083c54a3da2f58b806e85e715d54c126481be8cf9dd39590f707
78943ab12da8a186a0e1a7198c66e48e56c984a99dc055e66b85b324cc02707
7902e382b0dae7d540c8ae721f099ce70d0710d2b7f1e7f7e97b4d0645ea4d1
7919531166c71ed5f3e373808213581f19c645e765807419f82e7ce7a9c1f67
7925000ea96df79d20c0c401f861ae30c6770330e480a56149fcdec8a764d4c
7937f246e48c1a482c28aebcc6ff305e3e1fdf644a8a675737b118a93b1e77f
794bfc800ad67d342dc2a141fccb9793878e73b93301b4658231e4593c488ee
7957482503e7438a913fd9a297d7604ae1e990cf8a2c79347ae455d704fc526
796a5efc9a2c50dec9037866e9b63f46ac84aa985cdc28e99f223a3c034ac05
797ce8b4d229a24dd5649a1d69e7abf0d89240e8428c248811b38f42d5d447d
7981add4348009c3b687d27d8c1f13652d378385bb3c026e14cf93b4e9b2d1f
7998f7f0aee76dd151b447c70fa58290cb24ea7b97c6036b6f0d2a1b30376de
80044d304d7c81680d4c5a671e60e19892408e755d90bbaf958e2e79d3b2c88
801f4dc2381183f3f30eb02a5a756061a98d2313542aba8c775b111ddf5f50d
802830e5c5077abaede047abb114abf8442422b6cbe852b5dd289d5f0a331af
8032caa725c4a0f8565eb2eb330cb7b4e972ebc3c585e24a28ffcde7fdea7fb
804854d5bace20cd3e5403f162347b1b24217a5716217067fe367b15c2e6ba7
8053e773df977a0ce2b13097c31fcd16526ef41341d5ec38aa8abaef8051df7
8066fb00d4a8b5748036b2e1622b9736a6bcada3a01e18f9dbd989c48d806f1
8073d622be3c373818cf33b7916b54dd7876ee6bea8080e35e76d64c4aa672d
808b9c037d42f7ba8bc168923bf9a1dd187093a4acfda33eab5f70d303158e9
80920bff261a3f34c6a261683013256ca8b03f0c7670810a10b9bdb60d8e8e6
8104f2d63e9f9d456f0679efc1237b08a5ee915de30f92106562c083fbc5df3
8117116e3a9b03a9716691270548e040e22988e622a80a1310994779f6578c1
8123102688150d0c3a7bf7850b98995a02bc013f799e8d35d08941167e533a6
81360a9472e4acdb606051b83921ab086fb3d613c1f57094c593dfb8fb713ac
814740d321807d6c1c5e62bf7d3afcf930c941b50870eb26e18eaf4c63f3184
81501ce196e10994c4502ed8b7dcc58c4e086c7bd18324ddc193f7551ffeb30
8169fa28ad2168269ad9692e53c9940540162891e9f854ba4145d5102f40d81
817f81c8744ee8fd97983163784c1c43016daa0de55a2e16c5949fe3737ee84
818d04f881f20fd50d1f8bbc58dd5db3eb4357393e4859183f62db69cff2eb3
819706ad5c9e3dea63441c39d175d97f659d3dc0c2e0d206991e4fad0b861e8
820cc8f898658bb0ab8eac53e3add5d1b0eeff2497b259db6cde0f6dcda6a7a
821272f19ffe749e14ab8b0bf208a5b45fd67b3d0e3723f6f94b1196b0771d8
82294a4a1607ea66d79e0409867a66d4c255523c8bd3ae1efe7189d6c1bf1ce
82362eee5cc73e247e9a07b25a4a3cbd51aad22c31d87f11bd452a8d7131fe3
824a080d67bec8528778cf48d001dd81b5358adb6c40c11527685e5dc135fdd
82524fab0b25e746ce1a7c826234a14606ed6d021f160974dfe68e98eb98019
826dbdfd1dd95f38d72218ceeae2461974019c705ef7d2d16a56e7b50a0cd51
8278aa9f4772c47a5209f7033bd3191b017eb083f94615abcb3c777d661383c
828891ce8c5a6e2e35cd9b025ae41b1909dbe79bb3bb2da7675cd3b96b629e5
82998005f4479c0a9be004a433553f69a081c15f70e515343ea"
830 .trim()
831 .replace("\n", "");
832
833 let der = hex::decode(&input).unwrap();
834 let key = PublicKey::from_der(&der).unwrap();
835 assert_eq!(hex::encode(key.to_der()), input);
836 }
837
838 #[test]
844 fn test_sign_verify() {
845 let secret = SecretKey::generate();
847 let public = secret.public_key();
848
849 struct TestCase<'a> {
851 message: &'a [u8],
852 ctx: &'a [u8],
853 }
854 let tests = [
855 TestCase {
856 message: b"message to authenticate",
857 ctx: b"",
858 },
859 TestCase {
860 message: b"message to authenticate",
861 ctx: b"application context",
862 },
863 ];
864
865 for tt in &tests {
866 let signature = secret.sign(tt.message, tt.ctx);
868 public.verify(tt.message, tt.ctx, &signature).unwrap();
869
870 assert!(
872 public
873 .verify(tt.message, b"wrong context", &signature)
874 .is_err()
875 );
876 }
877 }
878
879 #[test]
880 fn test_bad_pubkey_does_not_panic() {
881 let zeros = [0u8; 1952];
883 let _ = PublicKey::from_bytes(&zeros);
884
885 let ones = [0xFFu8; 1952];
886 let _ = PublicKey::from_bytes(&ones);
887 }
888}