forked from RustCrypto/traits
-
Notifications
You must be signed in to change notification settings - Fork 0
/
x3dh.rs
152 lines (125 loc) · 4.93 KB
/
x3dh.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use kem::{Decapsulate, Encapsulate, FullKEM};
use p256::ecdsa::Signature;
use rand_core::CryptoRngCore;
use x3dh_ke::{x3dh_a, x3dh_b, EphemeralKey, IdentityKey, Key, OneTimePreKey, SignedPreKey};
/// A struct representing the x3dh KEM
struct X3Dh;
/// The shared secret type defined by x3dh_ke
type SharedSecret = [u8; 32];
// Define the recipient privkey type. This is a bundle of 3 privkeys of different lifespans
struct X3DhPrivkeyBundle {
ik: IdentityKey,
spk: SignedPreKey,
sig: Signature,
opk: OneTimePreKey,
}
impl X3DhPrivkeyBundle {
fn gen() -> X3DhPrivkeyBundle {
// The default() method does actual key generation here
let ik = IdentityKey::default();
let spk = SignedPreKey::default();
let sig = ik.sign(&spk.pk_to_bytes());
let opk = OneTimePreKey::default();
X3DhPrivkeyBundle { ik, spk, sig, opk }
}
fn as_pubkeys(&self) -> X3DhPubkeyBundle {
X3DhPubkeyBundle {
ik: self.ik.strip(),
spk: self.spk.strip(),
opk: self.opk.strip(),
sig: self.sig,
}
}
}
// The pubkeys keys associated with a privkey bundle. In x3dh-ke, all the keys serve as both
// pubkeys and privkeys. This seems dangerous but hey this isn't prod.
type X3DhPubkeyBundle = X3DhPrivkeyBundle;
/// To encap, we need the recipient's public keys and the sender's private key
struct EncapContext(X3DhPubkeyBundle, IdentityKey);
/// To decap, we need the recipient's private keys and the sender's public key
struct DecapContext(X3DhPrivkeyBundle, IdentityKey);
// Define an authenticated encapsulator. To authenticate, we need a full sender keypair.
impl Encapsulate<EphemeralKey, SharedSecret> for EncapContext {
type Error = &'static str;
fn encapsulate(
&self,
_: &mut impl CryptoRngCore,
) -> Result<(EphemeralKey, SharedSecret), Self::Error> {
// Make a new ephemeral key. This will be the encapped key
let ek = EphemeralKey::default();
// Deconstruct the recipient's pubkey bundle
let X3DhPubkeyBundle {
ref ik,
ref spk,
ref sig,
ref opk,
} = self.0;
let my_ik = &self.1;
// Do the X3DH operation to get the shared secret
let shared_secret = x3dh_a(sig, my_ik, spk, &ek, ik, opk)?;
Ok((ek, shared_secret))
}
}
// Define an decapsulator. Since authenticated and unauthenticated encapped keys are represented by
// the same type (which, outside of testing, should not be the case), this can do both auth'd and
// unauth'd decapsulation.
impl Decapsulate<EphemeralKey, SharedSecret> for DecapContext {
// TODO: Decapsulation is infallible. Make the Error type `!` when it's stable.
type Error = ();
fn decapsulate(&self, ek: &EphemeralKey) -> Result<SharedSecret, Self::Error> {
// Deconstruct our private keys bundle
let X3DhPrivkeyBundle {
ref ik,
ref spk,
ref opk,
..
} = self.0;
let sender_pubkey = &self.1;
// Now decapsulate
Ok(x3dh_b(sender_pubkey, spk, ek, ik, opk))
}
}
impl FullKEM for X3Dh {
type PrivateKey = X3DhPrivkeyBundle;
type PublicKey = X3DhPubkeyBundle;
type DecapsulatingKey = DecapContext;
type EncapsulatingKey = EncapContext;
type EncapsulatedKey = EphemeralKey;
type SharedSecret = SharedSecret;
fn random_keypair(_: &mut impl CryptoRngCore) -> (Self::PrivateKey, Self::PublicKey) {
let sk = Self::PrivateKey::gen();
let pk = sk.as_pubkeys();
(sk, pk)
}
}
#[test]
fn test_x3dh() {
let mut rng = rand::thread_rng();
// We use _a and _b suffixes to denote whether a key belongs to Alice or Bob. Alice is the
// sender in this case.
let sk_ident_a = IdentityKey::default();
let pk_ident_a = sk_ident_a.strip();
let sk_bundle_b = X3DhPrivkeyBundle::gen();
let pk_bundle_b = sk_bundle_b.as_pubkeys();
let encap_context = EncapContext(pk_bundle_b, sk_ident_a);
let decap_context = DecapContext(sk_bundle_b, pk_ident_a);
// Now do an authenticated encap
let (encapped_key, ss1) = encap_context.encapsulate(&mut rng).unwrap();
let ss2 = decap_context.decapsulate(&encapped_key).unwrap();
assert_eq!(ss1, ss2);
}
#[test]
fn test_kemtrait_x3dh() {
let mut rng = rand::thread_rng();
// We use _a and _b suffixes to denote whether a key belongs to Alice or Bob. Alice is the
// sender in this case.
let sk_ident_a = IdentityKey::default();
let pk_ident_a = sk_ident_a.strip();
let (sk_bundle_b, pk_bundle_b) = X3Dh::random_keypair(&mut rng);
let encap_context = EncapContext(pk_bundle_b, sk_ident_a);
let decap_context = DecapContext(sk_bundle_b, pk_ident_a);
// Now do an authenticated encap
let (encapped_key, ss1) = X3Dh::encapsulate(&encap_context, &mut rng).unwrap();
let ss2 = X3Dh::decapsulate(&decap_context, &encapped_key).unwrap();
assert_eq!(ss1, ss2);
}