linkspace/commons/
handshake.rs1use std::time::Duration;
8
9use anyhow::ensure;
10use linkspace_core::prelude::*;
11
12use super::lkc_our_group;
13pub const HANDSHAKE_D: Domain = abf(b"handshake");
14pub static ANONYMOUSE_PATH: &LkPath = lkpath(&[b"anonymous"]).as_path();
15pub static ID_SENTINAL_PATH: &LkPath = lkpath(&[b"sentinal"]).as_path();
16
17fn valid_stamp_range(stamp: Stamp, max_diff_sec: usize) -> anyhow::Result<()> {
18 let dur = Duration::from_secs(max_diff_sec as u64);
19 match stamp_age(stamp) {
20 Ok(d) => ensure!(
21 d < dur,
22 "check your clocks - packet is too old {d:?} >= {dur:?}"
23 ),
24 Err(d) => ensure!(
25 d < dur,
26 "check your clocks - packet is too new {d:?} >= {dur:?}"
27 ),
28 };
29 Ok(())
30}
31
32pub fn lkc_handshake_p0(id: &LkIdentity) -> PointParts {
33 tracing::trace!("Build phase0");
34 let now = now();
35 let mut kpr = try_keypoint_ref(
36 id.pubkey(),
37 HANDSHAKE_D,
38 ID_SENTINAL_PATH,
39 &[],
40 &[],
41 now,
42 id,
43 )
44 .unwrap();
45 kpr.update_xheader(&XFlags::DISPOSABLE);
46 kpr
47}
48pub fn lkc_handshake_p1<A>(
49 theirs: impl Point,
50 id: &LkIdentity,
51 max_diff_sec: usize,
52 into: impl FnOnce(PointParts) -> A,
53) -> anyhow::Result<A> {
54 tracing::trace!("Build Phase1");
55 assert!(
56 theirs.xheader().xhop.get() > 0,
57 "the rx channel is not calling net_header.hop() {:?}",
58 theirs.xheader()
59 );
60 let their_key = match theirs.pubkey() {
61 Some(their_key) => *their_key,
62 None => anyhow::bail!("not signed"),
63 };
64 valid_stamp_range(*theirs.get_stamp(), max_diff_sec)?;
65 let our_group = super::lkc_our_group(their_key, id.pubkey());
66 ensure!(
67 our_group != PRIVATE,
68 "Connecting to yourself (using the same key) is currently not supported"
69 );
70 let links = [Link::new("auth", *theirs.hash())];
71 let mut kpr = try_keypoint_ref(
72 our_group,
73 HANDSHAKE_D,
74 ID_SENTINAL_PATH,
75 &links,
76 &[],
77 now(),
78 id,
79 )?;
80 kpr.update_xheader(&XFlags::DISPOSABLE);
81 Ok(into(kpr))
82}
83pub fn lkc_handshake_p2<A>(
84 my_phase0: impl Point,
85 server_reply: impl Point,
86 id: &LkIdentity,
87 max_diff_sec: usize,
88 into: impl FnOnce(PointParts) -> A,
89) -> anyhow::Result<(A, PubKey)> {
90 tracing::trace!("Build Phase2");
91 ensure!(
92 my_phase0.pubkey() == Some(&id.pubkey()),
93 "identity mismatch"
94 );
95 let mine_hash = my_phase0.hash();
96 let theirs = &server_reply;
97 assert!(
98 theirs.xheader().xhop.get() > 0,
99 "the rx channel is not calling net_header.hop() {:?}",
100 theirs.xheader()
101 );
102 let their_key = match theirs.pubkey() {
103 Some(p) => *p,
104 None => anyhow::bail!("hello point not signed"),
105 };
106 valid_stamp_range(*theirs.get_stamp(), max_diff_sec)?;
107 ensure!(
108 theirs.get_links().iter().any(|r| r.ptr == mine_hash),
109 "did not validate my hash {}",
110 mine_hash
111 );
112 ensure!(
113 theirs.domain() == Some(&HANDSHAKE_D),
114 "not in the session domain"
115 );
116 let our_group = super::lkc_our_group(id.pubkey(), their_key);
117 ensure!(
118 theirs.group() == Some(&our_group),
119 "not in the right group "
120 );
121 ensure!(theirs.get_path() == ID_SENTINAL_PATH, "wrong path");
122 let links = [Link::new("signed", *theirs.hash())];
123 let mut kpr = try_keypoint_ref(
124 our_group,
125 HANDSHAKE_D,
126 ID_SENTINAL_PATH,
127 &links,
128 &[],
129 now(),
130 id,
131 )?;
132 kpr.update_xheader(&XFlags::DISPOSABLE);
133 Ok((into(kpr), their_key))
134}
135pub fn lkc_handshake_p3(
136 their_init: impl Point,
137 my_phase1: impl Point,
138 theirs: impl Point,
139 id: &LkIdentity,
140) -> anyhow::Result<PubKey> {
141 ensure!(
142 my_phase1.pubkey() == Some(&id.pubkey()),
143 "your identity mismatch"
144 );
145 let theirs = theirs.as_box();
146 let mine_hash = my_phase1.hash();
147 let their_key = match theirs.pubkey() {
148 Some(p) => {
149 ensure!(theirs.pubkey() == their_init.pubkey(), "switched keys");
150 *p
151 }
152 None => anyhow::bail!("hello point not signed"),
153 };
154 let our_group = lkc_our_group(id.pubkey(), their_key);
155 ensure!(
156 theirs.get_links().iter().any(|r| r.ptr == mine_hash),
157 "did not validate my hash {}",
158 mine_hash
159 );
160 ensure!(theirs.get_path() == ID_SENTINAL_PATH, "wrong path");
161 ensure!(
162 theirs.domain() == Some(&HANDSHAKE_D),
163 "not in the session domain"
164 );
165 ensure!(theirs.group() == Some(&our_group), "not in the right group");
166 Ok(their_key)
167}