verse_session_id/
session_id_pair.rs

1use crate::errors;
2use crate::SessionId;
3use anyhow::Result;
4use ed25519_dalek::Digest;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use std::fmt;
7
8/// Session ID and private key pair (ED25519).
9pub type SessionIdPair = ed25519_dalek::Keypair;
10
11/// Signature Salt Size
12pub const SIGNATURE_SALT_SIZE: usize = 8;
13/// Signature Size
14pub const SIGNATURE_SIZE: usize = ed25519_dalek::Signature::BYTE_SIZE;
15
16/// Session ID as public key
17pub trait SessionIdPublic {
18    /// Verify signature
19    fn verify(&self, payload: Vec<&[u8]>, sigset: &SignatureSet) -> Result<()>;
20}
21
22impl SessionIdPublic for SessionId {
23    fn verify(&self, payload: Vec<&[u8]>, sigset: &SignatureSet) -> Result<()> {
24        let pk =
25            ed25519_dalek::PublicKey::from_bytes(self.as_ref()).map_err(errors::signature!())?;
26        let mut hasher = ed25519_dalek::Sha512::new();
27        hasher.update(sigset.salt);
28        for p in payload {
29            hasher.update(p);
30        }
31        let signature = ed25519_dalek::Signature::from_bytes(&sigset.signature)
32            .map_err(errors::signature!())?;
33        Ok(pk
34            .verify_prehashed(hasher, None, &signature)
35            .map_err(errors::signature!())?)
36    }
37}
38
39pub trait ISessionIdPair {
40    /// Get session ID
41    fn get_id(&self) -> SessionId;
42    /// Create a signature for input data
43    fn sign(&self, payload: Vec<&[u8]>) -> Result<SignatureSet>;
44}
45
46/// Generate SessionIdPair
47pub fn new_session_id_pair() -> Result<SessionIdPair> {
48    let sk = &mut [0u8; ed25519_dalek::SECRET_KEY_LENGTH];
49    getrandom::getrandom(sk)?;
50    let sk = ed25519_dalek::SecretKey::from_bytes(sk).map_err(errors::signature!())?;
51
52    Ok(ed25519_dalek::Keypair {
53        public: ed25519_dalek::PublicKey::from(&sk),
54        secret: sk,
55    })
56}
57
58impl ISessionIdPair for SessionIdPair {
59    fn get_id(&self) -> SessionId {
60        self.public.to_bytes().into()
61    }
62    fn sign(&self, payload: Vec<&[u8]>) -> Result<SignatureSet> {
63        let mut salt = [0u8; SIGNATURE_SALT_SIZE];
64        getrandom::getrandom(&mut salt)?;
65        let mut hasher = ed25519_dalek::Sha512::new();
66        hasher.update(salt);
67        for p in payload {
68            hasher.update(p);
69        }
70        let signature = self
71            .sign_prehashed(hasher, None)
72            .map_err(errors::signature!())?;
73
74        Ok(SignatureSet {
75            signature: signature.to_bytes(),
76            salt,
77        })
78    }
79}
80
81/// Signature
82#[derive(Deserialize, Serialize, Eq, PartialEq, Debug)]
83pub struct SignatureSet {
84    #[serde(serialize_with = "as_base64", deserialize_with = "from_base64")]
85    pub signature: [u8; SIGNATURE_SIZE],
86    #[serde(serialize_with = "as_base64", deserialize_with = "from_base64")]
87    pub salt: [u8; SIGNATURE_SALT_SIZE],
88}
89
90impl fmt::Display for SignatureSet {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        let mut buf = Vec::<u8>::with_capacity(SIGNATURE_SIZE + SIGNATURE_SALT_SIZE);
93        buf.extend_from_slice(&self.signature);
94        buf.extend_from_slice(&self.salt);
95        write!(f, "{}", base64::encode(buf))
96    }
97}
98impl std::str::FromStr for SignatureSet {
99    type Err = anyhow::Error;
100    fn from_str(s: &str) -> Result<Self, Self::Err> {
101        base64::decode(s)?.try_into()
102    }
103}
104impl TryFrom<Vec<u8>> for SignatureSet {
105    type Error = anyhow::Error;
106    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
107        if value.len() != SIGNATURE_SIZE + SIGNATURE_SALT_SIZE {
108            return Err(errors::convert!(format!(
109                "{:?} != {:?}",
110                value.len(),
111                SIGNATURE_SIZE + SIGNATURE_SALT_SIZE
112            )));
113        }
114        let ss = SignatureSet {
115            signature: value[0..SIGNATURE_SIZE]
116                .try_into()
117                .map_err(|_v| errors::convert!())?,
118            salt: value[SIGNATURE_SIZE..]
119                .try_into()
120                .map_err(|_v| errors::convert!())?,
121        };
122        Ok(ss)
123    }
124}
125
126fn as_base64<const N: usize, S: Serializer>(
127    val: &[u8; N],
128    serializer: S,
129) -> Result<S::Ok, S::Error> {
130    serializer.serialize_str(&base64::encode(val))
131}
132
133fn from_base64<'de, const N: usize, D: Deserializer<'de>>(
134    deserializer: D,
135) -> Result<[u8; N], D::Error> {
136    use serde::de;
137
138    let res = <&str>::deserialize(deserializer).and_then(|s| {
139        base64::decode(s)
140            .map_err(|e| de::Error::custom(format!("invalid base64 string: {}, {}", s, e)))
141    })?;
142    res.try_into()
143        .map_err(|_| de::Error::custom(format!("invalid array size: {}", N)))
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    #[test]
150    fn test_keypair() {
151        let kp = new_session_id_pair();
152        // console_log!("{0:?}", kp);
153        assert!(kp.is_ok());
154    }
155
156    #[test]
157    fn test_sign_verify() {
158        let kp = new_session_id_pair().unwrap();
159        let ss = kp
160            .sign(vec!["1234".as_bytes(), "testdata".as_bytes()])
161            .unwrap();
162        // console_log!("sig: {0:?}, salt: {1:?}", ss.signature, ss.salt);
163
164        let session_id = kp.get_id();
165        let res = session_id.verify(vec!["1234".as_bytes(), "testdata".as_bytes()], &ss);
166        assert!(res.is_ok());
167
168        let res = session_id.verify(vec!["0234".as_bytes(), "testdata".as_bytes()], &ss);
169        assert!(res.is_err());
170
171        let kp = new_session_id_pair().unwrap();
172        let session_id = kp.get_id();
173        let res = session_id.verify(vec!["1234".as_bytes(), "testdata".as_bytes()], &ss);
174        assert!(res.is_err());
175
176        let ss1 = SignatureSet {
177            signature: ss.signature.clone(),
178            salt: Default::default(),
179        };
180        let res = session_id.verify(vec!["1234".as_bytes(), "testdata".as_bytes()], &ss1);
181        assert!(res.is_err());
182
183        let ss1 = SignatureSet {
184            signature: [0; SIGNATURE_SIZE],
185            salt: ss.salt.clone(),
186        };
187        let res = session_id.verify(vec!["1234".as_bytes(), "testdata".as_bytes()], &ss1);
188        assert!(res.is_err());
189    }
190    #[test]
191    fn test_ss_serialize() {
192        let ss = SignatureSet {
193            signature: [1; SIGNATURE_SIZE],
194            salt: [2; SIGNATURE_SALT_SIZE],
195        };
196        let serialized = serde_json::to_string(&ss).unwrap();
197        let deserialized: SignatureSet = serde_json::from_str(&serialized).unwrap();
198        assert_eq!(ss, deserialized);
199    }
200    #[test]
201    fn test_ss_serialize_str() {
202        let ss = SignatureSet {
203            signature: [1; SIGNATURE_SIZE],
204            salt: [2; SIGNATURE_SALT_SIZE],
205        };
206        let serialized = ss.to_string();
207        let deserialized: SignatureSet = serialized.parse().unwrap();
208        assert_eq!(ss, deserialized);
209    }
210}