1#![warn(missing_docs)]
2use std::str::FromStr;
75
76use rings_derive::wasm_export;
77use serde::Deserialize;
78use serde::Serialize;
79
80use crate::consts::DEFAULT_SESSION_TTL_MS;
81use crate::dht::Did;
82use crate::ecc::keccak256;
83use crate::ecc::keys::AccountVerifier;
84use crate::ecc::keys::SignatureAlgorithm;
85use crate::ecc::keys::VerificationPublicKey;
86use crate::ecc::signers;
87use crate::ecc::PublicKey;
88use crate::ecc::SecretKey;
89use crate::error::Error;
90use crate::error::Result;
91use crate::utils;
92
93fn pack_session(session_id: Did, ts_ms: u128, ttl_ms: u64) -> String {
94 format!("{session_id}\n{ts_ms}\n{ttl_ms}")
95}
96
97#[wasm_export]
104pub struct SessionSkBuilder {
105 sk: SecretKey,
106 account_entity: String,
108 account_type: String,
110 ttl_ms: u64,
112 ts_ms: u128,
114 sig: Vec<u8>,
116}
117
118#[wasm_export]
128#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
129pub struct SessionSk {
130 session: Session,
132 sk: SecretKey,
134}
135
136#[derive(Deserialize, Serialize, PartialEq, Eq, Debug, Clone)]
142pub struct Session {
143 session_id: Did,
145 account: Account,
147 ttl_ms: u64,
149 ts_ms: u128,
151 sig: Vec<u8>,
153}
154
155#[derive(Deserialize, Serialize, PartialEq, Eq, Debug, Clone)]
159pub enum Account {
160 Secp256k1(Did),
162 Secp256r1(PublicKey<33>),
164 EIP191(Did),
166 BIP137(Did),
168 Ed25519(PublicKey<33>),
170 Bls12381(PublicKey<48>),
172}
173
174impl TryFrom<(String, String)> for Account {
175 type Error = Error;
176
177 fn try_from((account_entity, account_type): (String, String)) -> Result<Self> {
178 match AccountVerifier::from_account_parts(&account_entity, &account_type)? {
179 AccountVerifier::Recoverable {
180 algorithm: SignatureAlgorithm::Secp256k1,
181 did,
182 } => Ok(Account::Secp256k1(did)),
183 AccountVerifier::Recoverable {
184 algorithm: SignatureAlgorithm::Eip191,
185 did,
186 } => Ok(Account::EIP191(did)),
187 AccountVerifier::Recoverable {
188 algorithm: SignatureAlgorithm::Bip137,
189 did,
190 } => Ok(Account::BIP137(did)),
191 AccountVerifier::PublicKey(VerificationPublicKey::Secp256r1(pk)) => {
192 Ok(Account::Secp256r1(pk))
193 }
194 AccountVerifier::PublicKey(VerificationPublicKey::Ed25519(pk)) => {
195 Ok(Account::Ed25519(pk))
196 }
197 AccountVerifier::PublicKey(VerificationPublicKey::Bls12381(pk)) => {
198 Ok(Account::Bls12381(pk))
199 }
200 _ => Err(Error::UnknownAccount),
201 }
202 }
203}
204
205impl Account {
206 fn account_verifier(&self) -> AccountVerifier {
207 match self {
208 Self::Secp256k1(did) => AccountVerifier::Recoverable {
209 algorithm: SignatureAlgorithm::Secp256k1,
210 did: *did,
211 },
212 Self::EIP191(did) => AccountVerifier::Recoverable {
213 algorithm: SignatureAlgorithm::Eip191,
214 did: *did,
215 },
216 Self::BIP137(did) => AccountVerifier::Recoverable {
217 algorithm: SignatureAlgorithm::Bip137,
218 did: *did,
219 },
220 Self::Secp256r1(pk) => {
221 AccountVerifier::PublicKey(VerificationPublicKey::Secp256r1(*pk))
222 }
223 Self::Ed25519(pk) => AccountVerifier::PublicKey(VerificationPublicKey::Ed25519(*pk)),
224 Self::Bls12381(pk) => AccountVerifier::PublicKey(VerificationPublicKey::Bls12381(*pk)),
225 }
226 }
227}
228
229impl FromStr for SessionSk {
232 type Err = Error;
233
234 fn from_str(s: &str) -> Result<Self> {
235 let s = base58_monero::decode_check(s).map_err(|_| Error::Decode)?;
236 let session_sk: SessionSk = serde_json::from_slice(&s).map_err(Error::Deserialize)?;
237 Ok(session_sk)
238 }
239}
240
241#[wasm_export]
242impl SessionSkBuilder {
243 pub fn new(account_entity: String, account_type: String) -> SessionSkBuilder {
247 let sk = SecretKey::random();
248 Self {
249 sk,
250 account_entity,
251 account_type,
252 ttl_ms: DEFAULT_SESSION_TTL_MS,
253 ts_ms: utils::get_epoch_ms(),
254 sig: vec![],
255 }
256 }
257
258 pub fn validate_account(&self) -> bool {
260 Account::try_from((self.account_entity.clone(), self.account_type.clone()))
261 .map_err(|e| {
262 tracing::warn!("validate_account error: {:?}", e);
263 e
264 })
265 .is_ok()
266 }
267
268 pub fn unsigned_proof(&self) -> String {
270 pack_session(self.sk.address().into(), self.ts_ms, self.ttl_ms)
271 }
272
273 pub fn set_session_sig(mut self, sig: Vec<u8>) -> Self {
275 self.sig = sig;
276 self
277 }
278
279 pub fn set_ttl(mut self, ttl_ms: u64) -> Self {
281 self.ttl_ms = ttl_ms;
282 self
283 }
284
285 pub fn build(self) -> Result<SessionSk> {
287 let account = Account::try_from((self.account_entity, self.account_type))?;
288 let session = Session {
289 session_id: self.sk.address().into(),
290 account,
291 ttl_ms: self.ttl_ms,
292 ts_ms: self.ts_ms,
293 sig: self.sig,
294 };
295
296 session.verify_self()?;
297
298 Ok(SessionSk {
299 session,
300 sk: self.sk,
301 })
302 }
303}
304
305impl Session {
306 pub fn pack(&self) -> Vec<u8> {
308 pack_session(self.session_id, self.ts_ms, self.ttl_ms)
309 .as_bytes()
310 .to_vec()
311 }
312
313 pub fn is_expired(&self) -> bool {
315 let now = utils::get_epoch_ms();
316 now > self.ts_ms + self.ttl_ms as u128
317 }
318
319 pub fn verify_self(&self) -> Result<()> {
321 if self.is_expired() {
322 return Err(Error::SessionExpired);
323 }
324
325 let auth_bytes = self.pack();
326
327 if !self
328 .account
329 .account_verifier()
330 .verify(&auth_bytes, &self.sig)
331 {
332 return Err(Error::VerifySignatureFailed);
333 }
334
335 Ok(())
336 }
337
338 pub fn verify(&self, msg: &[u8], sig: impl AsRef<[u8]>) -> Result<()> {
340 self.verify_self()?;
341 if !signers::secp256k1::verify(msg, &self.session_id, sig) {
342 return Err(Error::VerifySignatureFailed);
343 }
344 Ok(())
345 }
346
347 pub fn account_pubkey(&self) -> Result<PublicKey<33>> {
351 match self.account_verification_pubkey()? {
352 VerificationPublicKey::Secp256k1(pk)
353 | VerificationPublicKey::Eip191(pk)
354 | VerificationPublicKey::Bip137(pk) => Ok(pk),
355 VerificationPublicKey::Secp256r1(_)
356 | VerificationPublicKey::Ed25519(_)
357 | VerificationPublicKey::Bls12381(_) => Err(Error::UnknownAccount),
358 }
359 }
360
361 pub fn account_verification_pubkey(&self) -> Result<VerificationPublicKey> {
363 self.account
364 .account_verifier()
365 .verification_key_from_signature(&self.pack(), &self.sig)
366 }
367
368 pub fn account_verifier(&self) -> AccountVerifier {
370 self.account.account_verifier()
371 }
372
373 pub fn account_did(&self) -> Did {
375 self.account.account_verifier().did()
376 }
377}
378
379impl SessionSk {
380 pub fn new_with_seckey(key: &SecretKey) -> Result<Self> {
383 let account_entity = Did::from(key.address()).to_string();
384 let account_type = "secp256k1".to_string();
385
386 let mut builder = SessionSkBuilder::new(account_entity, account_type);
387
388 let sig = key.sign(&builder.unsigned_proof());
389 builder = builder.set_session_sig(sig.to_vec());
390
391 builder.build()
392 }
393
394 pub fn session(&self) -> Session {
396 self.session.clone()
397 }
398
399 pub fn sign(&self, msg: &[u8]) -> Result<Vec<u8>> {
401 let key = self.sk;
402 let h = keccak256(msg);
403 Ok(signers::secp256k1::sign(key, &h).to_vec())
404 }
405
406 pub fn account_did(&self) -> Did {
408 self.session.account_did()
409 }
410
411 pub fn account_verifier(&self) -> AccountVerifier {
413 self.session.account_verifier()
414 }
415
416 pub fn dump(&self) -> Result<String> {
419 let s = serde_json::to_string(&self).map_err(|_| Error::SerializeError)?;
420 base58_monero::encode_check(s.as_bytes()).map_err(|_| Error::Encode)
421 }
422}
423
424#[cfg(test)]
425mod test {
426 use super::*;
427 use crate::ecc::keys::SigningSecretKey;
428
429 #[test]
430 pub fn test_session_verify() {
431 let key = SecretKey::random();
432 let sm = SessionSk::new_with_seckey(&key).unwrap();
433 let session = sm.session();
434 assert!(session.verify_self().is_ok());
435 }
436
437 #[test]
438 pub fn test_account_pubkey() {
439 let key = SecretKey::random();
440 let sm = SessionSk::new_with_seckey(&key).unwrap();
441 let session = sm.session();
442 let pubkey = session.account_pubkey().unwrap();
443 assert_eq!(key.pubkey(), pubkey);
444 }
445
446 #[test]
447 pub fn test_session_verify_secp256r1_account_key() {
448 let account_entity = "17a6afd392fcbe4ac9270a599a9c5732c4f838ce35ea2234d389d8f0c367f3f5dcab906352e27289002c7f2c96039ddce7c1b5aad8b87ba94984d4c8b4f95702";
449 let account_key = VerificationPublicKey::Secp256r1(
450 PublicKey::<33>::from_hex_string(account_entity).unwrap(),
451 );
452 let signing_key =
453 SecretKey::try_from("2544acda37415a476d42312969926dc48e529867036cec71922d4177ea9c1038")
454 .unwrap();
455 let mut builder =
456 SessionSkBuilder::new(account_entity.to_string(), "secp256r1".to_string());
457 let proof = builder.unsigned_proof();
458 let sig =
459 signers::secp256r1::sign(signing_key, &signers::secp256r1::hash(proof.as_bytes()))
460 .unwrap();
461 builder = builder.set_session_sig(sig.to_vec());
462
463 let session = builder.build().unwrap().session();
464 assert_eq!(session.account_verification_pubkey().unwrap(), account_key);
465 assert_eq!(session.account_did(), account_key.did());
466 assert!(session.verify_self().is_ok());
467 assert!(session.account_pubkey().is_err());
468 }
469
470 #[test]
471 pub fn test_session_rejects_invalid_secp256r1_account_key() {
472 let mut invalid_key = None;
473 for i in 0u8..=u8::MAX {
474 let mut key = [0u8; 33];
475 key[0] = 2;
476 key[32] = i;
477 let public_key = PublicKey(key);
478 let verifying_key = public_key.ct_try_into_secp256r1_pubkey();
479 if !bool::from(verifying_key.is_some()) || verifying_key.unwrap().is_err() {
480 invalid_key = Some(key);
481 break;
482 }
483 }
484 let account_entity = hex::encode(invalid_key.expect("at least one invalid P-256 x"));
485 let builder = SessionSkBuilder::new(account_entity, "secp256r1".to_string())
486 .set_session_sig(vec![0u8; 64]);
487
488 assert!(!builder.validate_account());
489 assert!(builder.build().is_err());
490 }
491
492 #[test]
493 pub fn test_session_verify_bls12381_account_key() {
494 let signing_key = SigningSecretKey::random_bls12381().unwrap();
495 let account_key = signing_key.public_key().unwrap();
496 let VerificationPublicKey::Bls12381(raw_account_key) = account_key else {
497 unreachable!("random_bls12381 returns a BLS verification key");
498 };
499 let account_entity = base58_monero::encode_check(&raw_account_key.0).unwrap();
500 let mut builder = SessionSkBuilder::new(account_entity, "bls12-381".to_string());
501 let proof = builder.unsigned_proof();
502 builder = builder.set_session_sig(signing_key.sign_raw(proof.as_bytes()).unwrap());
503
504 let session = builder.build().unwrap().session();
505 assert_eq!(
506 session.account_verification_pubkey().unwrap(),
507 VerificationPublicKey::Bls12381(raw_account_key)
508 );
509 assert_eq!(session.account_did(), account_key.did());
510 assert!(session.verify_self().is_ok());
511 assert!(session.account_pubkey().is_err());
512 }
513
514 #[test]
515 pub fn test_session_verify_ed25519_account_key() {
516 let signing_key = SigningSecretKey::random_ed25519();
517 let account_key = signing_key.public_key().unwrap();
518 let VerificationPublicKey::Ed25519(raw_account_key) = account_key else {
519 unreachable!("random_ed25519 returns an Ed25519 verification key");
520 };
521 let account_entity = raw_account_key.to_base58_string().unwrap();
522 let mut builder = SessionSkBuilder::new(account_entity, "ed25519".to_string());
523 let proof = builder.unsigned_proof();
524 builder = builder.set_session_sig(signing_key.sign_raw(proof.as_bytes()).unwrap());
525
526 let session = builder.build().unwrap().session();
527 assert_eq!(
528 session.account_verification_pubkey().unwrap(),
529 VerificationPublicKey::Ed25519(raw_account_key)
530 );
531 assert_eq!(session.account_did(), account_key.did());
532 assert!(session.verify_self().is_ok());
533 assert!(session.account_pubkey().is_err());
534 }
535
536 #[test]
537 pub fn test_dump_restore() {
538 let key = SecretKey::random();
539 let sm = SessionSk::new_with_seckey(&key).unwrap();
540 let dump = sm.dump().unwrap();
541 let sm2 = SessionSk::from_str(&dump).unwrap();
542 assert_eq!(sm, sm2);
543 }
544}