ouisync/network/
runtime_id.rs1use crate::crypto::{
6 sign::{Keypair, PublicKey, Signature},
7 Digest, Hashable,
8};
9use ouisync_macros::api;
10use rand::{rngs::OsRng, CryptoRng, Rng};
11use serde::{Deserialize, Serialize};
12use std::io;
13use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
14
15pub struct SecretRuntimeId {
16 keypair: Keypair,
17}
18
19impl SecretRuntimeId {
20 pub fn generate<R: Rng + CryptoRng>(rng: &mut R) -> Self {
21 Self {
22 keypair: Keypair::generate(rng),
23 }
24 }
25
26 pub fn random() -> Self {
27 Self {
28 keypair: Keypair::random(),
29 }
30 }
31
32 pub fn public(&self) -> PublicRuntimeId {
33 PublicRuntimeId {
34 public: self.keypair.public_key(),
35 }
36 }
37}
38
39impl From<Keypair> for SecretRuntimeId {
40 fn from(keypair: Keypair) -> Self {
41 Self { keypair }
42 }
43}
44
45#[derive(PartialEq, Eq, Ord, PartialOrd, Hash, Clone, Copy, Deserialize, Serialize, Debug)]
46#[serde(transparent)]
47#[api(repr(Bytes))]
48pub struct PublicRuntimeId {
49 public: PublicKey,
50}
51
52impl PublicRuntimeId {
53 async fn read_from<R>(io: &mut R) -> io::Result<Self>
54 where
55 R: AsyncRead + Unpin,
56 {
57 let bytes = read_bytes::<{ PublicKey::SIZE }, R>(io).await?;
58 Ok(Self {
59 public: bytes
60 .as_slice()
61 .try_into()
62 .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?,
63 })
64 }
65
66 async fn write_into<W>(&self, io: &mut W) -> io::Result<()>
67 where
68 W: AsyncWrite + Unpin,
69 {
70 io.write_all(self.public.as_ref()).await
71 }
72
73 pub fn as_public_key(&self) -> &PublicKey {
74 &self.public
75 }
76}
77
78impl AsRef<[u8]> for PublicRuntimeId {
79 fn as_ref(&self) -> &[u8] {
80 self.public.as_ref()
81 }
82}
83
84impl Hashable for PublicRuntimeId {
85 fn update_hash<S: Digest>(&self, state: &mut S) {
86 self.public.update_hash(state)
87 }
88}
89
90pub async fn exchange<W, R>(
91 our_runtime_id: &SecretRuntimeId,
92 writer: &mut W,
93 reader: &mut R,
94) -> io::Result<PublicRuntimeId>
95where
96 W: AsyncWrite + Unpin,
97 R: AsyncRead + Unpin,
98{
99 let our_challenge: [u8; 32] = OsRng.r#gen();
100
101 writer.write_all(&our_challenge).await?;
102 our_runtime_id.public().write_into(writer).await?;
103
104 let their_challenge: [_; 32] = read_bytes(reader).await?;
105 let their_runtime_id = PublicRuntimeId::read_from(reader).await?;
106
107 let our_signature = our_runtime_id.keypair.sign(&to_sign(&their_challenge));
108
109 writer.write_all(&our_signature.to_bytes()).await?;
110
111 let their_signature: [_; Signature::SIZE] = read_bytes(reader).await?;
112 let their_signature = Signature::from(&their_signature);
113
114 if !their_runtime_id
115 .public
116 .verify(&to_sign(&our_challenge), &their_signature)
117 {
118 return Err(io::Error::other("Failed to verify runtime ID"));
119 }
120
121 Ok(their_runtime_id)
122}
123
124const TO_SIGN_PREFIX: &[u8; 10] = b"runtime-id";
125
126fn to_sign(buf: &[u8; 32]) -> [u8; 32 + TO_SIGN_PREFIX.len()] {
127 let mut out = [0u8; 32 + TO_SIGN_PREFIX.len()];
128 out[..TO_SIGN_PREFIX.len()].clone_from_slice(TO_SIGN_PREFIX);
129 out[TO_SIGN_PREFIX.len()..].clone_from_slice(buf);
130 out
131}
132
133async fn read_bytes<const N: usize, R>(io: &mut R) -> io::Result<[u8; N]>
134where
135 R: AsyncRead + Unpin,
136{
137 let mut out = [0u8; N];
138 io.read_exact(&mut out).await?;
139 Ok(out)
140}