nym_gateway_requests/
shared_key.rs

1// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use 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}