nym_gateway_requests/
shared_key.rs1use nym_crypto::blake3;
5use nym_crypto::crypto_hash::compute_digest;
6use nym_crypto::generic_array::{typenum::Unsigned, GenericArray};
7use nym_crypto::symmetric::aead::{
8 self, nonce_size, random_nonce, AeadError, AeadKey, KeySizeUser, Nonce,
9};
10use nym_pemstore::traits::PemStorableKey;
11use nym_sphinx::params::GatewayEncryptionAlgorithm;
12use rand::thread_rng;
13use serde::{Deserialize, Serialize};
14use thiserror::Error;
15use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
16
17pub type SharedKeySize = <GatewayEncryptionAlgorithm as KeySizeUser>::KeySize;
18
19#[derive(Debug, Error)]
20pub enum SharedKeyUsageError {
21 #[error("the request is too short")]
22 TooShortRequest,
23
24 #[error("provided MAC is invalid")]
25 InvalidMac,
26
27 #[error("the provided nonce (or legacy IV) did not have the expected length")]
28 MalformedNonce,
29
30 #[error("did not provide a valid nonce for aead encryption")]
31 MissingAeadNonce,
32
33 #[error("failed to either encrypt or decrypt provided message")]
34 AeadFailure(#[from] AeadError),
35}
36
37#[derive(Debug, PartialEq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
38pub struct SharedSymmetricKey(AeadKey<GatewayEncryptionAlgorithm>);
39
40type KeySize = <GatewayEncryptionAlgorithm as KeySizeUser>::KeySize;
41
42#[derive(Debug, Clone, Copy, Error)]
43pub enum SharedKeyConversionError {
44 #[error("the string representation of the shared key was malformed: {0}")]
45 DecodeError(#[from] bs58::decode::Error),
46 #[error(
47 "the received shared keys had invalid size. Got: {received}, but expected: {expected}"
48 )]
49 InvalidSharedKeysSize { received: usize, expected: usize },
50}
51
52impl SharedSymmetricKey {
53 pub fn random_nonce(&self) -> Nonce<GatewayEncryptionAlgorithm> {
54 let mut rng = thread_rng();
55 random_nonce::<GatewayEncryptionAlgorithm, _>(&mut rng)
56 }
57
58 pub fn nonce_size(&self) -> usize {
59 nonce_size::<GatewayEncryptionAlgorithm>()
60 }
61
62 pub fn validate_aead_nonce(
63 raw: &[u8],
64 ) -> Result<Nonce<GatewayEncryptionAlgorithm>, SharedKeyUsageError> {
65 if raw.len() != nonce_size::<GatewayEncryptionAlgorithm>() {
66 return Err(SharedKeyUsageError::MalformedNonce);
67 }
68 Ok(Nonce::<GatewayEncryptionAlgorithm>::clone_from_slice(raw))
69 }
70 pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, SharedKeyConversionError> {
71 if bytes.len() != KeySize::to_usize() {
72 return Err(SharedKeyConversionError::InvalidSharedKeysSize {
73 received: bytes.len(),
74 expected: KeySize::to_usize(),
75 });
76 }
77
78 Ok(SharedSymmetricKey(GenericArray::clone_from_slice(bytes)))
79 }
80
81 pub fn zeroizing_clone(&self) -> Zeroizing<Self> {
82 Zeroizing::new(SharedSymmetricKey(self.0))
83 }
84
85 pub fn digest(&self) -> Vec<u8> {
86 compute_digest::<blake3::Hasher>(self.as_bytes()).to_vec()
87 }
88
89 pub fn as_bytes(&self) -> &[u8] {
90 self.0.as_slice()
91 }
92
93 pub fn to_bytes(&self) -> Vec<u8> {
94 self.0.iter().copied().collect()
95 }
96
97 pub fn try_from_base58_string<S: Into<String>>(
98 val: S,
99 ) -> Result<Self, SharedKeyConversionError> {
100 let bs58_str = Zeroizing::new(val.into());
101 let decoded = Zeroizing::new(bs58::decode(bs58_str).into_vec()?);
102 Self::try_from_bytes(&decoded)
103 }
104
105 pub fn to_base58_string(&self) -> String {
106 let bytes = Zeroizing::new(self.to_bytes());
107 bs58::encode(bytes).into_string()
108 }
109
110 pub fn encrypt(
111 &self,
112 plaintext: &[u8],
113 nonce: &Nonce<GatewayEncryptionAlgorithm>,
114 ) -> Result<Vec<u8>, SharedKeyUsageError> {
115 aead::encrypt::<GatewayEncryptionAlgorithm>(&self.0, nonce, plaintext).map_err(Into::into)
116 }
117
118 pub fn decrypt(
119 &self,
120 ciphertext: &[u8],
121 nonce: &Nonce<GatewayEncryptionAlgorithm>,
122 ) -> Result<Vec<u8>, SharedKeyUsageError> {
123 aead::decrypt::<GatewayEncryptionAlgorithm>(&self.0, nonce, ciphertext).map_err(Into::into)
124 }
125}
126
127impl PemStorableKey for SharedSymmetricKey {
128 type Error = SharedKeyConversionError;
129
130 fn pem_type() -> &'static str {
131 "AES-256-GCM-SIV GATEWAY SHARED KEY"
132 }
133
134 fn to_bytes(&self) -> Vec<u8> {
135 self.to_bytes()
136 }
137
138 fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
139 Self::try_from_bytes(bytes)
140 }
141}