Skip to main content

lb_rs/model/
pubkey.rs

1use crate::model::clock::{TimeGetter, timestamp};
2use crate::model::crypto::*;
3use libsecp256k1::{Message, PublicKey, SecretKey, SharedSecret, Signature};
4use rand::rngs::OsRng;
5use serde::Serialize;
6use sha2::{Digest, Sha256};
7use std::convert::TryInto;
8
9use super::errors::{LbErrKind, LbResult, SignError, Unexpected};
10
11pub fn generate_key() -> SecretKey {
12    SecretKey::random(&mut OsRng)
13}
14
15pub fn sign<T: Serialize>(
16    sk: &SecretKey, pk: &PublicKey, to_sign: T, time_getter: TimeGetter,
17) -> LbResult<ECSigned<T>> {
18    let timestamped = timestamp(to_sign, time_getter);
19    let serialized = bincode::serialize(&timestamped).map_unexpected()?;
20    let digest = Sha256::digest(&serialized);
21    let message = &Message::parse_slice(&digest)
22        .map_err(|err| LbErrKind::Sign(SignError::SignatureParseError(err)))?;
23    let (signature, _) = libsecp256k1::sign(message, sk);
24    Ok(ECSigned {
25        timestamped_value: timestamped,
26        signature: signature.serialize().to_vec(),
27        public_key: *pk,
28    })
29}
30
31pub fn verify<T: Serialize>(
32    pk: &PublicKey, signed: &ECSigned<T>, max_delay_ms: u64, max_skew_ms: u64,
33    time_getter: TimeGetter,
34) -> LbResult<()> {
35    if &signed.public_key != pk {
36        Err(LbErrKind::Sign(SignError::WrongPublicKey))?;
37    }
38
39    let auth_time = signed.timestamped_value.timestamp;
40    let current_time = time_getter().0;
41    let max_skew_ms = max_skew_ms as i64;
42    let max_delay_ms = max_delay_ms as i64;
43
44    if current_time < auth_time - max_skew_ms {
45        Err(LbErrKind::Sign(SignError::SignatureInTheFuture(
46            (current_time - (auth_time - max_delay_ms)) as u64,
47        )))?;
48    }
49
50    if current_time > auth_time + max_delay_ms {
51        Err(LbErrKind::Sign(SignError::SignatureExpired(
52            (auth_time + max_delay_ms - current_time) as u64,
53        )))?;
54    }
55
56    // todo: evaluate potential waste here: didn't we just have this in it's
57    // serialized form?
58    let serialized = bincode::serialize(&signed.timestamped_value)?;
59
60    let digest = Sha256::digest(&serialized).to_vec();
61    let message = &Message::parse_slice(&digest).map_err(|err| {
62        LbErrKind::Unexpected(format!(
63            "unexpected failure to produce a message after hashing: {err:?}"
64        ))
65    })?;
66
67    let signature = Signature::parse_standard_slice(&signed.signature)
68        .map_err(|err| LbErrKind::Sign(SignError::SignatureParseError(err)))?;
69
70    if libsecp256k1::verify(message, &signature, &signed.public_key) {
71        Ok(())
72    } else {
73        Err(LbErrKind::Sign(SignError::SignatureInvalid))?
74    }
75}
76
77pub fn get_aes_key(sk: &SecretKey, pk: &PublicKey) -> LbResult<AESKey> {
78    SharedSecret::<Sha256>::new(pk, sk)
79        .map_unexpected()?
80        .as_ref()
81        .try_into()
82        .map_unexpected()
83}
84
85#[cfg(test)]
86mod unit_tests {
87    use libsecp256k1::PublicKey;
88
89    use crate::model::clock::Timestamp;
90    use crate::model::pubkey::*;
91
92    static EARLY_CLOCK: fn() -> Timestamp = || Timestamp(500);
93    static LATE_CLOCK: fn() -> Timestamp = || Timestamp(520);
94
95    #[test]
96    fn ec_test_sign_verify() {
97        let sk = generate_key();
98        let pk = PublicKey::from_secret_key(&sk);
99        let value = sign(&sk, &pk, "Test", EARLY_CLOCK).unwrap();
100        verify(&pk, &value, 20, 20, LATE_CLOCK).unwrap();
101    }
102
103    #[test]
104    fn ec_test_sign_verify_late() {
105        let sk = generate_key();
106        let pk = PublicKey::from_secret_key(&sk);
107        let value = sign(&sk, &pk, "Test", EARLY_CLOCK).unwrap();
108        verify(&pk, &value, 10, 10, LATE_CLOCK).unwrap_err();
109    }
110
111    #[test]
112    fn ec_test_shared_secret_one_party() {
113        // Just sanity checks
114
115        let key = generate_key();
116        let shared_secret1 = get_aes_key(&key, &PublicKey::from_secret_key(&key)).unwrap();
117
118        let shared_secret2 = get_aes_key(&key, &PublicKey::from_secret_key(&key)).unwrap();
119
120        assert_eq!(shared_secret1, shared_secret2);
121    }
122
123    #[test]
124    fn ec_test_shared_secret_two_parties() {
125        // Just sanity checks
126
127        let key1 = generate_key();
128        let key2 = generate_key();
129
130        let shared_secret1 = get_aes_key(&key1, &PublicKey::from_secret_key(&key2)).unwrap();
131
132        let shared_secret2 = get_aes_key(&key2, &PublicKey::from_secret_key(&key1)).unwrap();
133
134        assert_eq!(shared_secret1, shared_secret2);
135    }
136
137    #[test]
138    fn same_sk_same_pk_sanity_check() {
139        let key1 = generate_key();
140        let key2 = SecretKey::parse(&key1.serialize()).unwrap();
141
142        assert_eq!(key1, key2);
143
144        assert_eq!(PublicKey::from_secret_key(&key1), PublicKey::from_secret_key(&key2));
145    }
146}