hopr_crypto_types/
seal.rs

1use libp2p_identity::PeerId;
2
3use crate::{errors::CryptoError, keypairs::OffchainKeypair, types::OffchainPublicKey};
4
5/// Performs randomized encryption of the given data, so that
6/// only the recipient with the given `peer_id` can [decrypt it](unseal_data).
7pub fn seal_data(data: &[u8], peer_id: PeerId) -> crate::errors::Result<Box<[u8]>> {
8    let recipient_pk: crypto_box::PublicKey =
9        curve25519_dalek::MontgomeryPoint::from(&OffchainPublicKey::try_from(peer_id)?).into();
10
11    recipient_pk
12        .seal(&mut hopr_crypto_random::rng(), data)
13        .map_err(|_| CryptoError::SealingError)
14        .map(|vec| vec.into_boxed_slice())
15}
16
17/// Decrypts a data previously encrypted with [`seal_data`].
18///
19/// The given `keypair` must correspond to the `peer_id` given during encryption.
20pub fn unseal_data(data: &[u8], keypair: &OffchainKeypair) -> crate::errors::Result<Box<[u8]>> {
21    let recipient_sk = crypto_box::SecretKey::from(curve25519_dalek::scalar::Scalar::from(keypair));
22
23    recipient_sk
24        .unseal(data)
25        .map(|vec| vec.into_boxed_slice())
26        .map_err(|_| CryptoError::SealingError)
27}
28
29#[cfg(test)]
30mod tests {
31    use std::ops::Not;
32
33    use hex_literal::hex;
34
35    use super::*;
36    use crate::keypairs::Keypair;
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}