dcrypt_symmetric/aead/gcm/
types.rs1use crate::error::{validate, validate_format, Result};
4use base64;
5use rand::{rngs::OsRng, RngCore};
6use std::fmt;
7
8#[derive(Clone, Debug)]
10pub struct GcmNonce([u8; 12]);
11
12impl GcmNonce {
13 pub fn new(bytes: [u8; 12]) -> Self {
15 Self(bytes)
16 }
17
18 pub fn generate() -> Self {
20 let mut nonce = [0u8; 12];
21 OsRng.fill_bytes(&mut nonce);
22 Self(nonce)
23 }
24
25 pub fn as_bytes(&self) -> &[u8; 12] {
27 &self.0
28 }
29
30 pub fn from_string(s: &str) -> Result<Self> {
32 let bytes =
33 base64::decode(s).map_err(|_| dcrypt_api::error::Error::SerializationError {
34 context: "nonce base64",
35 #[cfg(feature = "std")]
36 message: "invalid base64 encoding".to_string(),
37 })?;
38
39 validate::length("GCM nonce", bytes.len(), 12)?;
40
41 let mut nonce = [0u8; 12];
42 nonce.copy_from_slice(&bytes);
43
44 Ok(Self(nonce))
45 }
46}
47
48impl fmt::Display for GcmNonce {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(f, "{}", base64::encode(self.0))
51 }
52}
53
54#[derive(Clone, Debug)]
56pub struct AesCiphertextPackage {
57 pub nonce: GcmNonce,
59 pub ciphertext: Vec<u8>,
61}
62
63impl AesCiphertextPackage {
64 pub fn new(nonce: GcmNonce, ciphertext: Vec<u8>) -> Self {
66 Self { nonce, ciphertext }
67 }
68
69 pub fn from_string(s: &str) -> Result<Self> {
71 validate_format(
72 s.starts_with("dcrypt-AES-GCM:"),
73 "package deserialization",
74 "invalid package format",
75 )?;
76
77 let parts: Vec<&str> = s["dcrypt-AES-GCM:".len()..].split(':').collect();
78 validate_format(
79 parts.len() == 2,
80 "package deserialization",
81 "expected format: dcrypt-AES-GCM:<nonce>:<ciphertext>",
82 )?;
83
84 let nonce_bytes =
85 base64::decode(parts[0]).map_err(|_| dcrypt_api::error::Error::SerializationError {
86 context: "nonce base64",
87 #[cfg(feature = "std")]
88 message: "invalid base64 encoding".to_string(),
89 })?;
90
91 validate::length("GCM nonce", nonce_bytes.len(), 12)?;
92
93 let mut nonce = [0u8; 12];
94 nonce.copy_from_slice(&nonce_bytes);
95
96 let ciphertext =
97 base64::decode(parts[1]).map_err(|_| dcrypt_api::error::Error::SerializationError {
98 context: "ciphertext base64",
99 #[cfg(feature = "std")]
100 message: "invalid base64 encoding".to_string(),
101 })?;
102
103 Ok(Self {
104 nonce: GcmNonce(nonce),
105 ciphertext,
106 })
107 }
108}
109
110impl fmt::Display for AesCiphertextPackage {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 let nonce_b64 = base64::encode(self.nonce.as_bytes());
113 let ciphertext_b64 = base64::encode(&self.ciphertext);
114 write!(f, "dcrypt-AES-GCM:{}:{}", nonce_b64, ciphertext_b64)
115 }
116}