Skip to main content

rns_core/link/
identify.rs

1use alloc::vec::Vec;
2
3use rns_crypto::ed25519::Ed25519PublicKey;
4use rns_crypto::identity::Identity;
5
6use super::types::{LinkError, LinkId};
7use crate::constants::{KEYSIZE, SIGLENGTH};
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().ok_or(LinkError::CryptoError)?;
14
15    let mut signed_data = Vec::with_capacity(16 + 64);
16    signed_data.extend_from_slice(link_id);
17    signed_data.extend_from_slice(&public_key);
18
19    let signature = identity
20        .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!(
97            validate_identify_data(&data, &wrong_id),
98            Err(LinkError::InvalidSignature)
99        );
100    }
101
102    #[test]
103    fn test_identify_tampered() {
104        let mut rng = FixedRng::new(&[0x42; 128]);
105        let identity = Identity::new(&mut rng);
106        let link_id: LinkId = [0xAA; 16];
107
108        let mut data = build_identify_data(&identity, &link_id).unwrap();
109        data[10] ^= 0xFF; // tamper with public key
110        assert_eq!(
111            validate_identify_data(&data, &link_id),
112            Err(LinkError::InvalidSignature)
113        );
114    }
115
116    #[test]
117    fn test_identify_invalid_length() {
118        let link_id: LinkId = [0xAA; 16];
119        assert_eq!(
120            validate_identify_data(&[0u8; 64], &link_id),
121            Err(LinkError::InvalidData)
122        );
123    }
124
125    #[test]
126    fn test_identify_public_only_fails() {
127        let mut rng = FixedRng::new(&[0x42; 128]);
128        let identity = Identity::new(&mut rng);
129        let pubkey = identity.get_public_key().unwrap();
130        let public_only = Identity::from_public_key(&pubkey);
131        let link_id: LinkId = [0xAA; 16];
132
133        // Should fail because public-only identity can't sign
134        assert!(build_identify_data(&public_only, &link_id).is_err());
135    }
136}