hopr_crypto_types/
seal.rs

1use libp2p_identity::PeerId;
2
3use crate::errors::CryptoError;
4use crate::keypairs::OffchainKeypair;
5use crate::types::OffchainPublicKey;
6
7/// Performs randomized encryption of the given data, so that
8/// only the recipient with the given `peer_id` can [decrypt it](unseal_data).
9pub fn seal_data(data: &[u8], peer_id: PeerId) -> crate::errors::Result<Box<[u8]>> {
10    let recipient_pk: crypto_box::PublicKey =
11        curve25519_dalek::MontgomeryPoint::from(&OffchainPublicKey::try_from(peer_id)?).into();
12
13    recipient_pk
14        .seal(&mut hopr_crypto_random::rng(), data)
15        .map_err(|_| CryptoError::SealingError)
16        .map(|vec| vec.into_boxed_slice())
17}
18
19/// Decrypts a data previously encrypted with [`seal_data`].
20///
21/// The given `keypair` must correspond to the `peer_id` given during encryption.
22pub fn unseal_data(data: &[u8], keypair: &OffchainKeypair) -> crate::errors::Result<Box<[u8]>> {
23    let recipient_sk = crypto_box::SecretKey::from(curve25519_dalek::scalar::Scalar::from(keypair));
24
25    recipient_sk
26        .unseal(data)
27        .map(|vec| vec.into_boxed_slice())
28        .map_err(|_| CryptoError::SealingError)
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34    use crate::keypairs::Keypair;
35    use hex_literal::hex;
36    use std::ops::Not;
37
38    #[test]
39    fn seal_unseal_should_be_identity() -> anyhow::Result<()> {
40        let data = "some test data".to_string();
41
42        let keypair = OffchainKeypair::random();
43
44        let sealed = seal_data(data.as_bytes(), keypair.public().into())?;
45
46        let unsealed = String::from_utf8(unseal_data(&sealed, &keypair)?.into_vec())?;
47
48        assert_eq!(data, unsealed);
49
50        Ok(())
51    }
52
53    #[test]
54    fn unseal_should_fail_with_different_private_key() -> anyhow::Result<()> {
55        let data = "some test data".to_string();
56
57        let keypair_1 = OffchainKeypair::random();
58        let keypair_2 = OffchainKeypair::random();
59
60        let sealed = seal_data(data.as_bytes(), keypair_1.public().into())?;
61
62        assert_eq!(Err(CryptoError::SealingError), unseal_data(&sealed, &keypair_2));
63
64        Ok(())
65    }
66
67    #[test]
68    fn unseal_should_fail_when_ciphertext_has_been_tampered_with() -> anyhow::Result<()> {
69        let data = "some test data".to_string();
70
71        let keypair = OffchainKeypair::random();
72
73        let mut sealed = seal_data(data.as_bytes(), keypair.public().into())?;
74        sealed[1] = sealed[1].not();
75
76        assert_eq!(Err(CryptoError::SealingError), unseal_data(&sealed, &keypair));
77
78        Ok(())
79    }
80
81    #[test]
82    fn unseal_fixed_test() -> anyhow::Result<()> {
83        let data = hex!("d7538951e728a28c6381a481f9f33111b6d78211bd1d6a286bdf1b16ee1ad35837b5b0ffcd3b308a4fa9939af0a208150418629c7af31ad457d3fe51602dc9b5f0da253fb44ec0fb75cac9e0bcb9a3ef");
84        let peer_id: PeerId = "12D3KooWHcCWDKzMkypyLWvri5ioSVivCazU8jgbWzyerM5aMuf8".parse()?;
85
86        let keypair = OffchainKeypair::from_secret(&hex!(
87            "1142b6483e171aa577baea2290797023cd14e034d36f9febb975772ac2924c00"
88        ))?;
89        assert_eq!(PeerId::from(keypair.public()), peer_id);
90
91        let pt = String::from_utf8(unseal_data(&data, &keypair)?.into_vec())?;
92
93        assert_eq!("Hello, this is a secret message!", pt);
94
95        Ok(())
96    }
97}