Skip to main content

rns_core/link/
identify.rs

1use alloc::vec::Vec;
2
3use rns_crypto::identity::Identity;
4use rns_crypto::ed25519::Ed25519PublicKey;
5
6use crate::constants::{KEYSIZE, SIGLENGTH};
7use super::types::{LinkError, LinkId};
8
9/// Build LINKIDENTIFY plaintext: `[public_key:64][signature:64]`.
10///
11/// `signed_data = link_id + public_key`
12pub fn build_identify_data(identity: &Identity, link_id: &LinkId) -> Result<Vec<u8>, LinkError> {
13    let public_key = identity.get_public_key()
14        .ok_or(LinkError::CryptoError)?;
15
16    let mut signed_data = Vec::with_capacity(16 + 64);
17    signed_data.extend_from_slice(link_id);
18    signed_data.extend_from_slice(&public_key);
19
20    let signature = identity.sign(&signed_data)
21        .map_err(|_| LinkError::CryptoError)?;
22
23    let mut data = Vec::with_capacity(128);
24    data.extend_from_slice(&public_key);
25    data.extend_from_slice(&signature);
26
27    Ok(data)
28}
29
30/// Validate LINKIDENTIFY plaintext.
31///
32/// Returns `(identity_hash, public_key)` on success.
33pub fn validate_identify_data(
34    plaintext: &[u8],
35    link_id: &LinkId,
36) -> Result<([u8; 16], [u8; 64]), LinkError> {
37    let expected_len = KEYSIZE / 8 + SIGLENGTH / 8; // 64 + 64 = 128
38    if plaintext.len() != expected_len {
39        return Err(LinkError::InvalidData);
40    }
41
42    let mut public_key = [0u8; 64];
43    public_key.copy_from_slice(&plaintext[..64]);
44
45    let mut signature = [0u8; 64];
46    signature.copy_from_slice(&plaintext[64..128]);
47
48    let mut signed_data = Vec::with_capacity(16 + 64);
49    signed_data.extend_from_slice(link_id);
50    signed_data.extend_from_slice(&public_key);
51
52    // Verify using Ed25519 signing key (second half of public key)
53    let sig_pub = Ed25519PublicKey::from_bytes(&{
54        let mut b = [0u8; 32];
55        b.copy_from_slice(&public_key[32..64]);
56        b
57    });
58
59    if sig_pub.verify(&signature, &signed_data) {
60        // Compute identity hash
61        let identity = Identity::from_public_key(&public_key);
62        let identity_hash = *identity.hash();
63        Ok((identity_hash, public_key))
64    } else {
65        Err(LinkError::InvalidSignature)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use rns_crypto::FixedRng;
73
74    #[test]
75    fn test_identify_roundtrip() {
76        let mut rng = FixedRng::new(&[0x42; 128]);
77        let identity = Identity::new(&mut rng);
78        let link_id: LinkId = [0xAA; 16];
79
80        let data = build_identify_data(&identity, &link_id).unwrap();
81        assert_eq!(data.len(), 128);
82
83        let (hash, pubkey) = validate_identify_data(&data, &link_id).unwrap();
84        assert_eq!(hash, *identity.hash());
85        assert_eq!(pubkey, identity.get_public_key().unwrap());
86    }
87
88    #[test]
89    fn test_identify_wrong_link_id() {
90        let mut rng = FixedRng::new(&[0x42; 128]);
91        let identity = Identity::new(&mut rng);
92        let link_id: LinkId = [0xAA; 16];
93        let wrong_id: LinkId = [0xBB; 16];
94
95        let data = build_identify_data(&identity, &link_id).unwrap();
96        assert_eq!(validate_identify_data(&data, &wrong_id), Err(LinkError::InvalidSignature));
97    }
98
99    #[test]
100    fn test_identify_tampered() {
101        let mut rng = FixedRng::new(&[0x42; 128]);
102        let identity = Identity::new(&mut rng);
103        let link_id: LinkId = [0xAA; 16];
104
105        let mut data = build_identify_data(&identity, &link_id).unwrap();
106        data[10] ^= 0xFF; // tamper with public key
107        assert_eq!(validate_identify_data(&data, &link_id), Err(LinkError::InvalidSignature));
108    }
109
110    #[test]
111    fn test_identify_invalid_length() {
112        let link_id: LinkId = [0xAA; 16];
113        assert_eq!(validate_identify_data(&[0u8; 64], &link_id), Err(LinkError::InvalidData));
114    }
115
116    #[test]
117    fn test_identify_public_only_fails() {
118        let mut rng = FixedRng::new(&[0x42; 128]);
119        let identity = Identity::new(&mut rng);
120        let pubkey = identity.get_public_key().unwrap();
121        let public_only = Identity::from_public_key(&pubkey);
122        let link_id: LinkId = [0xAA; 16];
123
124        // Should fail because public-only identity can't sign
125        assert!(build_identify_data(&public_only, &link_id).is_err());
126    }
127}