1use std::{array::TryFromSliceError, fmt::Debug};
14
15use aes::{Aes128, cipher::typenum::Unsigned};
16use base64ct::{Base64, Encoding as _};
17use ccm::{
18 Ccm,
19 Nonce,
20 aead::{Aead, KeyInit, rand_core::RngCore},
21 consts::{U13, U16},
22};
23use curve25519_dalek::Scalar;
24use ed25519_dalek::{SigningKey, hazmat::ExpandedSecretKey};
25use num_enum::{FromPrimitive, IntoPrimitive};
26use yubihsm::object::{Handle, Type};
27
28use crate::object::{Domains, ObjectId};
29
30#[derive(Debug, thiserror::Error)]
32pub enum Error {
33 #[error("Decoding Base64 failed: {0}")]
35 Base64Decode(#[from] base64ct::Error),
36
37 #[error("Decryption error: {0}")]
39 Decrypt(#[from] ccm::Error),
40
41 #[error("Incorrect slice length: {0}")]
43 SliceLength(#[from] TryFromSliceError),
44
45 #[error("Unexpected Ed25519 serialized form length: {actual}")]
49 UnexpectedEd25519SerializedLength {
50 actual: usize,
52 },
53
54 #[error("Cannot parse data of unknown type: {0:?}")]
56 UnknownObjectType(ObjectType),
57
58 #[error("YubiHSM2 object error: {0:?}")]
60 YubiHsmObject(#[from] yubihsm::object::Error),
61
62 #[error("Parsing buffer: not enough data.")]
64 InsufficientDataInBuffer,
65}
66
67pub struct PlainWrappedDataWithKey<'a, 'b> {
69 pub data: &'a [u8],
71
72 pub key: &'b [u8],
74}
75
76impl Debug for PlainWrappedDataWithKey<'_, '_> {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 f.debug_struct("PlainWrappedDataWithKey")
79 .field("data", &self.data)
80 .field("key", &"[REDACTED]")
81 .finish()
82 }
83}
84
85impl TryFrom<PlainWrappedDataWithKey<'_, '_>> for YubiHsm2Wrap {
86 type Error = Error;
87
88 fn try_from(value: PlainWrappedDataWithKey<'_, '_>) -> Result<Self, Self::Error> {
94 let cipher = Aes128Ccm::new(value.key.into());
95 let mut nonce = [0; 13];
96 let mut rng = aes::cipher::crypto_common::rand_core::OsRng;
97 rng.fill_bytes(&mut nonce);
98 let mut wrapped = cipher.encrypt(Nonce::from_slice(&nonce), value.data)?;
99 wrapped.splice(0..0, nonce);
100
101 Ok(Self { wrapped })
102 }
103}
104
105type Aes128Ccm = Ccm<Aes128, U16, U13>;
106
107#[derive(Debug)]
109pub struct YubiHsm2Wrap {
110 wrapped: Vec<u8>,
111}
112
113impl YubiHsm2Wrap {
114 pub fn new(wrapped: Vec<u8>) -> Self {
116 Self { wrapped }
117 }
118
119 pub fn from_yhw(wrapped: &str) -> Result<Self, Error> {
129 let wrapped = wrapped.trim_ascii();
130 let wrapped = Base64::decode_vec(wrapped)?;
131 Ok(Self { wrapped })
132 }
133
134 pub fn to_yhw(&self) -> String {
137 Base64::encode_string(&self.wrapped)
138 }
139
140 pub fn decrypt(&self, wrapping_key: &[u8]) -> Result<Vec<u8>, Error> {
146 let cipher = Aes128Ccm::new(wrapping_key.into());
147 let (nonce, ciphertext) = self.wrapped.split_at(U13::to_usize());
148 let plaintext = cipher.decrypt(nonce.into(), ciphertext)?;
149
150 Ok(plaintext)
151 }
152}
153
154impl AsRef<[u8]> for YubiHsm2Wrap {
155 fn as_ref(&self) -> &[u8] {
156 &self.wrapped
157 }
158}
159
160#[derive(Clone, Copy, Debug, Eq, FromPrimitive, IntoPrimitive, PartialEq)]
164#[repr(u8)]
165pub enum WrapAlgorithm {
166 Aes128Ccm = 29,
168
169 Aes192Ccm = 41,
171
172 Aes256Ccm = 42,
174
175 #[num_enum(catch_all)]
177 Unknown(u8),
178}
179
180#[derive(Clone, Copy, Debug, Eq, FromPrimitive, IntoPrimitive, PartialEq)]
185#[repr(u8)]
186pub enum ObjectType {
187 Ed25519 = 46,
191
192 Aes128Auth = 38,
196
197 Opaque = 30,
201
202 #[num_enum(catch_all)]
204 Unknown(u8),
205}
206
207#[derive(Clone, Debug, Eq, PartialEq)]
209pub struct ExpandedEd25519KeyData<'a> {
210 pub private_scalar: &'a [u8; 32],
212
213 pub private_hash_prefix: &'a [u8; 32],
215
216 pub public: &'a [u8; 32],
218}
219
220impl ExpandedEd25519KeyData<'_> {
221 pub const LEN: usize = 32 * 3;
223}
224
225impl<'a> From<ExpandedEd25519KeyData<'a>> for ExpandedSecretKey {
226 fn from(value: ExpandedEd25519KeyData<'a>) -> Self {
227 let mut private_scalar = *value.private_scalar;
228 private_scalar.reverse();
229 ExpandedSecretKey {
230 scalar: Scalar::from_bytes_mod_order(private_scalar),
231 hash_prefix: *value.private_hash_prefix,
232 }
233 }
234}
235
236impl<'a> TryFrom<&'a [u8]> for ExpandedEd25519KeyData<'a> {
237 type Error = TryFromSliceError;
238 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
239 Ok(Self {
240 private_scalar: value[0..32].try_into()?,
241 private_hash_prefix: value[32..64].try_into()?,
242 public: value[64..].try_into()?,
243 })
244 }
245}
246
247#[derive(Clone, Debug, Eq, PartialEq)]
253pub struct SeedEd25519KeyData<'a> {
254 pub private_scalar: &'a [u8; 32],
256
257 pub private_hash_prefix: &'a [u8; 32],
259
260 pub public: &'a [u8; 32],
262
263 pub private_seed: &'a [u8; 32],
265}
266
267impl SeedEd25519KeyData<'_> {
268 pub const LEN: usize = 32 * 4;
270}
271
272impl<'a> TryFrom<&'a [u8]> for SeedEd25519KeyData<'a> {
273 type Error = TryFromSliceError;
274 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
275 Ok(Self {
276 private_seed: value[0..32].try_into()?,
277 private_scalar: value[32..64].try_into()?,
278 private_hash_prefix: value[64..96].try_into()?,
279 public: value[96..].try_into()?,
280 })
281 }
282}
283
284impl<'a> From<SeedEd25519KeyData<'a>> for ExpandedSecretKey {
285 fn from(value: SeedEd25519KeyData<'a>) -> Self {
286 let mut private_scalar = *value.private_scalar;
287 private_scalar.reverse();
288
289 ExpandedSecretKey {
291 scalar: Scalar::from_bytes_mod_order(private_scalar),
292 hash_prefix: *value.private_hash_prefix,
293 }
294 }
295}
296
297impl<'a> From<&'a SeedEd25519KeyData<'a>> for SigningKey {
298 fn from(value: &'a SeedEd25519KeyData<'a>) -> Self {
299 SigningKey::from(value.private_seed)
300 }
301}
302
303#[derive(Debug)]
312pub struct SerializedEd25519([u8; 32 * 4]);
313
314impl AsRef<[u8]> for SerializedEd25519 {
315 fn as_ref(&self) -> &[u8] {
316 &self.0
317 }
318}
319
320impl From<&SigningKey> for SerializedEd25519 {
321 fn from(value: &SigningKey) -> Self {
322 let mut result = [0; _];
323 let expanded = ExpandedSecretKey::from(&value.to_bytes());
324 result[0..32].copy_from_slice(value.as_bytes());
325 result[32..64].copy_from_slice(expanded.scalar.as_bytes());
326 result[32..64].reverse();
327 result[64..96].copy_from_slice(&expanded.hash_prefix);
328 result[96..].copy_from_slice(value.verifying_key().as_bytes());
329 Self(result)
330 }
331}
332
333#[derive(Clone, Debug, Eq, PartialEq)]
335pub struct AuthAes128<'a> {
336 pub delegated_capabilities: &'a [u8; 8],
338
339 pub symmetric_keys: &'a [u8; 32],
341}
342
343impl AuthAes128<'_> {
344 const LEN: usize = 8 + 32;
346}
347
348#[derive(Clone, Debug, Eq, PartialEq)]
354pub enum WrappedPayload<'a> {
355 ExpandedEd25519(ExpandedEd25519KeyData<'a>),
357
358 SeedEd25519(SeedEd25519KeyData<'a>),
360
361 AuthAes128(AuthAes128<'a>),
363
364 Opaque(&'a [u8]),
366}
367
368impl<'a> WrappedPayload<'a> {
369 fn parse(object_type: ObjectType, bytes: &'a [u8]) -> Result<WrappedPayload<'a>, Error> {
383 Ok(match object_type {
384 ObjectType::Ed25519 => match bytes.len() {
385 ExpandedEd25519KeyData::LEN => Self::ExpandedEd25519(bytes.try_into()?),
386 SeedEd25519KeyData::LEN => Self::SeedEd25519(bytes.try_into()?),
387 len => return Err(Error::UnexpectedEd25519SerializedLength { actual: len }),
388 },
389 ObjectType::Aes128Auth => {
390 let (delegated_capabilities, symmetric_keys) = bytes.split_at(8);
391 Self::AuthAes128(AuthAes128 {
392 delegated_capabilities: delegated_capabilities.try_into()?,
393 symmetric_keys: symmetric_keys.try_into()?,
394 })
395 }
396 ObjectType::Opaque => Self::Opaque(bytes),
397 object_type => return Err(Error::UnknownObjectType(object_type)),
398 })
399 }
400
401 fn serialize_into(&self, buffer: &mut Vec<u8>) {
403 match self {
404 WrappedPayload::ExpandedEd25519(key_data) => {
405 buffer.extend_from_slice(key_data.private_scalar);
406 buffer.extend_from_slice(key_data.private_hash_prefix);
407 buffer.extend_from_slice(key_data.public);
408 }
409 WrappedPayload::SeedEd25519(key_data) => {
410 buffer.extend_from_slice(key_data.private_seed);
411 buffer.extend_from_slice(key_data.private_scalar);
412 buffer.extend_from_slice(key_data.private_hash_prefix);
413 buffer.extend_from_slice(key_data.public);
414 }
415 WrappedPayload::AuthAes128(key_data) => {
416 buffer.extend_from_slice(key_data.delegated_capabilities);
417 buffer.extend_from_slice(key_data.symmetric_keys);
418 }
419 WrappedPayload::Opaque(key_data) => buffer.extend_from_slice(key_data),
420 }
421 }
422
423 fn len(&self) -> usize {
425 match self {
426 WrappedPayload::ExpandedEd25519(_) => ExpandedEd25519KeyData::LEN,
427 WrappedPayload::SeedEd25519(_) => SeedEd25519KeyData::LEN,
428 WrappedPayload::AuthAes128(_) => AuthAes128::LEN,
429 WrappedPayload::Opaque(key_data) => key_data.len(),
430 }
431 }
432}
433
434struct BeReader<'a> {
436 pos: usize,
437 buf: &'a [u8],
438}
439
440impl<'a> BeReader<'a> {
441 fn new(buf: &'a [u8]) -> Self {
443 Self { buf, pos: 0 }
444 }
445
446 fn position(&self) -> usize {
448 self.pos
449 }
450
451 fn read_u8(&mut self) -> Result<u8, Error> {
457 if self.pos + 1 >= self.buf.len() {
458 return Err(Error::InsufficientDataInBuffer);
459 }
460 let byte = self.buf[self.pos];
461 self.pos += 1;
462 Ok(byte)
463 }
464
465 fn read_u16(&mut self) -> Result<u16, Error> {
472 Ok(u16::from_be_bytes([self.read_u8()?, self.read_u8()?]))
473 }
474
475 fn read<const N: usize>(&mut self) -> Result<&'a [u8; N], Error> {
482 if self.pos + N >= self.buf.len() {
483 return Err(Error::InsufficientDataInBuffer);
484 }
485 let bytes = &self.buf[self.pos..self.pos + N];
486 self.pos += N;
487 bytes
488 .try_into()
489 .map_err(|_| Error::InsufficientDataInBuffer)
490 }
491
492 fn read_to_end(&mut self) -> Result<&'a [u8], Error> {
499 if self.pos > self.buf.len() {
500 return Err(Error::InsufficientDataInBuffer);
501 }
502 let bytes = &self.buf[self.pos..];
503 self.pos = self.buf.len() + 1;
504 Ok(bytes)
505 }
506}
507
508#[derive(Debug)]
510pub struct InnerFormat<'a> {
511 pub wrap_algorithm: WrapAlgorithm,
513
514 pub capabilities: &'a [u8; 8],
516
517 pub object_id: ObjectId,
519
520 pub domains: Domains,
522
523 pub object_type: ObjectType,
525
526 pub sequence: u8,
528
529 pub origin: u8,
531
532 pub label: String,
534
535 pub key_data: WrappedPayload<'a>,
537}
538
539impl<'a> InnerFormat<'a> {
540 pub fn parse(raw: &'a [u8]) -> Result<Self, crate::Error> {
549 let mut reader = BeReader::new(raw);
550
551 let wrap_algorithm = WrapAlgorithm::from(reader.read_u8()?);
552 let capabilities = reader.read()?;
553 let id = reader.read_u16()?;
554 let datalen = reader.read_u16()?;
555 let domains = reader.read_u16()?.into();
556 let object_id = ObjectId::try_from(Handle::new(
557 id,
558 Type::from_u8(reader.read_u8()?).map_err(Error::YubiHsmObject)?,
559 ))?;
560 let object_type = ObjectType::from(reader.read_u8()?);
561 let sequence = reader.read_u8()?;
562 let origin = reader.read_u8()?;
563
564 let label = reader.read::<40>()?;
565 let len = label.iter().position(|&b| b == 0).unwrap_or(label.len());
566 let label = String::from_utf8_lossy(&label[..len]).into();
567
568 if reader.position() + datalen as usize != raw.len() {
570 return Err(Error::InsufficientDataInBuffer)?;
571 }
572
573 Ok(Self {
574 wrap_algorithm,
575 capabilities,
576 object_id,
577 domains,
578 object_type,
579 sequence,
580 origin,
581 label,
582 key_data: WrappedPayload::parse(object_type, reader.read_to_end()?)?,
583 })
584 }
585
586 pub fn serialize_into(&self, buffer: &mut Vec<u8>) {
588 buffer.push(self.wrap_algorithm.into());
589 buffer.extend_from_slice(self.capabilities);
590 buffer.extend_from_slice(&u16::from(self.object_id.id()).to_be_bytes());
591 buffer.extend_from_slice(&(self.key_data.len() as u16).to_be_bytes());
592 buffer.extend_from_slice(&self.domains.to_be_bytes());
593 buffer.push(self.object_id.object_type().to_u8());
594 buffer.push(self.object_type.into());
595 buffer.push(self.sequence);
596 buffer.push(self.origin);
597 let mut label: [u8; 40] = [0; 40];
598 let slice_len = self.label.len().min(label.len());
599 label[..slice_len].copy_from_slice(self.label.as_bytes());
600 buffer.extend_from_slice(&label);
601 self.key_data.serialize_into(buffer);
602 }
603}
604
605#[cfg(test)]
606mod tests {
607
608 use ed25519_dalek::VerifyingKey;
609 use testresult::TestResult;
610
611 use super::*;
612 use crate::object::Domain;
613
614 const WRAP_KEY: &[u8] = &[
615 0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
616 ];
617
618 #[test]
619 fn decrypt_ed25519() -> TestResult {
620 let wrap = YubiHsm2Wrap::from_yhw(include_str!("../tests/backup/private-ed25519.yhw"))?;
621 let decrypted = wrap.decrypt(WRAP_KEY)?;
622 assert!(!decrypted.is_empty());
623 let inner = InnerFormat::parse(&decrypted)?;
624 let mut buffer = vec![];
625 inner.serialize_into(&mut buffer);
626 assert_eq!(buffer, decrypted);
627 assert_eq!(inner.object_type, ObjectType::Ed25519);
628 assert_eq!(inner.wrap_algorithm, WrapAlgorithm::Aes128Ccm);
629 assert_eq!(u16::from(inner.object_id.id()), 0x1f_u16);
630 assert_eq!(inner.domains, Domain::One.into());
631 assert_eq!(inner.sequence, 0);
632 assert_eq!(inner.origin, 2);
633 assert_eq!(inner.label, "Ed25519_Key");
634 let WrappedPayload::ExpandedEd25519(key_data) = inner.key_data else {
635 panic!("Expected Ed25519 key data");
636 };
637 let ExpandedEd25519KeyData {
638 private_scalar,
639 private_hash_prefix,
640 public,
641 } = key_data;
642
643 assert_eq!(
644 private_scalar,
645 &[
646 117, 188, 78, 175, 249, 221, 207, 75, 177, 26, 92, 146, 43, 19, 156, 7, 87, 43,
647 173, 199, 232, 63, 249, 230, 100, 131, 86, 147, 80, 229, 193, 192
648 ]
649 );
650 assert_eq!(
651 private_hash_prefix,
652 &[
653 182, 113, 137, 6, 206, 62, 108, 30, 26, 138, 65, 215, 178, 10, 9, 215, 181, 55,
654 132, 37, 162, 172, 202, 169, 56, 150, 245, 195, 212, 232, 235, 183
655 ]
656 );
657 assert_eq!(
658 public,
659 &[
660 185, 235, 254, 46, 190, 171, 17, 45, 56, 27, 211, 240, 69, 46, 39, 226, 53, 109,
661 50, 78, 181, 96, 30, 177, 162, 240, 122, 187, 82, 30, 156, 242
662 ]
663 );
664 let signing_key: ExpandedSecretKey = key_data.into();
665 let verifying_key = VerifyingKey::from(&signing_key);
666 assert_eq!(public, &verifying_key.to_bytes());
667 Ok(())
668 }
669
670 #[test]
671 fn decrypt_ed25519_with_seed() -> TestResult {
672 let wrap =
673 YubiHsm2Wrap::from_yhw(include_str!("../tests/backup/private-ed25519-seed.yhw"))?;
674 let decrypted = wrap.decrypt(WRAP_KEY)?;
675 assert!(!decrypted.is_empty());
676 let inner = InnerFormat::parse(&decrypted)?;
677 let mut buffer = vec![];
678 inner.serialize_into(&mut buffer);
679 assert_eq!(buffer, decrypted);
680 assert_eq!(inner.object_type, ObjectType::Ed25519);
681 assert_eq!(inner.wrap_algorithm, WrapAlgorithm::Aes128Ccm);
682 assert_eq!(u16::from(inner.object_id.id()), 13);
683 assert_eq!(inner.domains, Domains::all());
684 assert_eq!(inner.sequence, 0);
685 assert_eq!(inner.origin, 1);
686 assert_eq!(inner.label, "Signature_Key_Ed_2");
687 let WrappedPayload::SeedEd25519(key_data) = inner.key_data.clone() else {
688 panic!("Expected Ed25519 key data");
689 };
690
691 let SeedEd25519KeyData {
692 private_scalar,
693 private_hash_prefix,
694 public,
695 private_seed,
696 } = key_data;
697
698 assert_eq!(
699 private_seed,
700 &[
701 73, 122, 141, 156, 79, 125, 147, 201, 97, 207, 112, 15, 133, 155, 17, 216, 4, 254,
702 88, 71, 207, 139, 63, 170, 229, 246, 54, 32, 206, 12, 84, 86
703 ]
704 );
705 assert_eq!(
706 private_scalar,
707 &[
708 7, 81, 112, 122, 75, 85, 173, 6, 20, 181, 199, 29, 147, 191, 157, 102, 147, 157,
709 133, 249, 149, 223, 14, 41, 17, 51, 179, 38, 146, 102, 210, 15
710 ]
711 );
712 assert_eq!(
713 private_hash_prefix,
714 &[
715 161, 55, 166, 21, 136, 215, 184, 182, 181, 62, 143, 223, 62, 159, 19, 228, 179, 87,
716 101, 158, 129, 137, 207, 186, 191, 206, 220, 148, 44, 83, 203, 115
717 ]
718 );
719 assert_eq!(
720 public,
721 &[
722 252, 157, 136, 36, 18, 36, 60, 188, 181, 153, 78, 169, 136, 74, 14, 210, 150, 203,
723 47, 42, 79, 2, 238, 0, 103, 237, 202, 100, 87, 40, 252, 44
724 ]
725 );
726 let signing_key = SigningKey::from(&key_data);
727 let serialized = SerializedEd25519::from(&signing_key);
728 assert_eq!(
729 inner.key_data,
730 WrappedPayload::parse(ObjectType::Ed25519, serialized.as_ref())?
731 );
732
733 assert_eq!(public, &signing_key.verifying_key().to_bytes());
734 let exp = ExpandedSecretKey::from(private_seed);
735
736 let mut private_scalar = *private_scalar;
737 private_scalar.reverse();
738
739 assert_eq!(exp.scalar.as_bytes(), &private_scalar);
740 assert_eq!(&exp.hash_prefix, private_hash_prefix);
741
742 let signing_key: ExpandedSecretKey = key_data.into();
743 assert_eq!(exp.scalar, signing_key.scalar);
744 assert_eq!(exp.hash_prefix, signing_key.hash_prefix);
745
746 let verifying_key = VerifyingKey::from(&signing_key);
747 assert_eq!(public, &verifying_key.to_bytes());
748 Ok(())
749 }
750
751 #[test]
752 fn auth_key() -> TestResult {
753 let wrap = YubiHsm2Wrap::from_yhw(include_str!("../tests/backup/auth.yhw"))?;
754 let decrypted = wrap.decrypt(WRAP_KEY)?;
755 assert!(!decrypted.is_empty());
756 let inner = InnerFormat::parse(&decrypted)?;
757 let mut buffer = vec![];
758 inner.serialize_into(&mut buffer);
759 assert_eq!(decrypted, buffer);
760 assert_eq!(inner.object_type, ObjectType::Aes128Auth);
761 assert_eq!(inner.capabilities, &[0, 0, 0, 0, 0, 1, 0, 0]);
762 assert_eq!(inner.domains, Domain::One.into());
763 assert_eq!(u16::from(inner.object_id.id()), 14);
764 assert_eq!(
765 inner.key_data,
766 WrappedPayload::AuthAes128(AuthAes128 {
767 delegated_capabilities: &[0; 8],
768 symmetric_keys: &[
769 152, 123, 73, 154, 181, 120, 84, 139, 48, 32, 41, 176, 213, 53, 39, 232, 122,
770 150, 131, 153, 10, 233, 98, 202, 67, 12, 27, 245, 184, 198, 41, 93
771 ]
772 })
773 );
774 assert_eq!(inner.object_id.object_type(), Type::AuthenticationKey);
775 assert_eq!(inner.label, "");
776 assert_eq!(inner.origin, 2);
777 assert_eq!(inner.sequence, 0);
778 Ok(())
779 }
780
781 #[test]
782 fn opaque_data() -> TestResult {
783 let wrap = YubiHsm2Wrap::from_yhw(include_str!("../tests/backup/opaque.yhw"))?;
784 let decrypted = wrap.decrypt(WRAP_KEY)?;
785 assert!(!decrypted.is_empty());
786 let inner = InnerFormat::parse(&decrypted)?;
787 let mut buffer = vec![];
788 inner.serialize_into(&mut buffer);
789 assert_eq!(decrypted, buffer);
790 assert_eq!(inner.object_type, ObjectType::Opaque);
791 assert_eq!(inner.capabilities, &[0, 0, 0, 0, 0, 1, 0, 0]);
792 assert_eq!(inner.domains, Domain::One.into());
793 assert_eq!(u16::from(inner.object_id.id()), 13);
794 assert_eq!(inner.key_data, WrappedPayload::Opaque(&[1, 2, 3]));
795 assert_eq!(inner.object_id.object_type(), Type::Opaque);
796 assert_eq!(inner.label, "random");
797 assert_eq!(inner.origin, 2);
798 assert_eq!(inner.sequence, 0);
799 Ok(())
800 }
801
802 #[test]
803 fn roundtrip_yhw() -> TestResult {
804 let input = include_str!("../tests/backup/private-ed25519-seed.yhw");
805 let wrap = YubiHsm2Wrap::from_yhw(input)?;
806 assert_eq!(input, wrap.to_yhw());
807 Ok(())
808 }
809
810 #[test]
811 fn encrypt_decrypt() -> TestResult {
812 let input = include_str!("../tests/backup/opaque.yhw");
813 let wrap = YubiHsm2Wrap::from_yhw(input)?;
814 let decrypted_original = wrap.decrypt(WRAP_KEY)?;
815 let plain = PlainWrappedDataWithKey {
816 data: &decrypted_original,
817 key: WRAP_KEY,
818 };
819 let wrap: YubiHsm2Wrap = plain.try_into()?;
820 let decrypted_from_plain = wrap.decrypt(WRAP_KEY)?;
821 assert_eq!(decrypted_original, decrypted_from_plain);
822 Ok(())
823 }
824}