1#![doc = include_str!("../README.md")]
2
3mod error;
4
5pub use error::SimpleSrpError;
6pub use srp::groups;
7
8use std::marker::PhantomData;
9use rand::RngCore;
10use serde::{Deserialize, Serialize};
11use sha2::Digest;
12use srp::{ClientVerifier, Group};
13
14pub struct CryptoString(Vec<u8>);
17
18impl CryptoString {
19 #[inline]
20 pub const fn as_bytes(&self) -> &[u8] {
21 self.0.as_slice()
22 }
23
24 #[inline]
25 pub fn hex(self) -> String {
26 hex::encode(self.0)
27 }
28}
29
30impl From<Vec<u8>> for CryptoString {
31 #[inline]
32 fn from(value: Vec<u8>) -> Self {
33 CryptoString(value)
34 }
35}
36
37impl TryFrom<String> for CryptoString {
38 type Error = hex::FromHexError;
39
40 #[inline]
41 fn try_from(value: String) -> Result<Self, Self::Error> {
42 hex::decode(value).map(CryptoString::from)
43 }
44}
45
46pub struct KeyPair {
49 pub private: CryptoString,
50 pub public: CryptoString,
51}
52
53impl KeyPair {
54 #[inline]
55 pub fn from_parts(private: String, public: String) -> Result<Self, hex::FromHexError> {
56 Ok(KeyPair {
57 private: CryptoString::try_from(private)?,
58 public: CryptoString::try_from(public)?,
59 })
60 }
61}
62
63#[derive(Debug, Serialize, Deserialize)]
66pub struct SignupCredentials {
67 pub username: String,
68 pub salt: String,
69 pub verifier: String,
70}
71
72#[derive(Debug, Serialize, Deserialize)]
73pub struct ClientHello {
74 pub username: String,
75 pub client: String,
77}
78
79#[derive(Debug, Serialize, Deserialize)]
80pub struct ServerHello {
81 pub salt: String,
82 pub server: String,
84}
85
86#[derive(Debug, Serialize, Deserialize)]
87pub struct LoginEvidence {
88 pub evidence: String,
90}
91
92#[derive(Debug, Serialize, Deserialize)]
93pub struct AuthResult {
94 pub result: bool,
95 pub evidence: String,
97}
98
99pub struct Client<G: Group, D: Digest> {
100 d: PhantomData<(G, D)>,
101}
102
103impl<G: Group, D: Digest> Client<G, D> {
104 pub fn new() -> Self {
105 Client {
106 d: PhantomData,
107 }
108 }
109
110 pub fn sign_up(&self, username: String, password: String) -> SignupCredentials {
111 let mut salt = [0u8; 64];
112 rand::rng().fill_bytes(&mut salt);
113 let verifier = srp::Client::<G, D>::new()
114 .compute_verifier(
115 username.as_bytes(),
116 password.as_bytes(),
117 &salt
118 );
119
120 SignupCredentials {
121 username,
122 salt: hex::encode(salt),
123 verifier: hex::encode(verifier),
124 }
125 }
126
127
128 pub fn login_hello(&self, username: String) -> (ClientHello, KeyPair) {
129 let mut private = [0u8; 64];
130 rand::rng().fill_bytes(&mut private);
131 let public = srp::Client::<G, D>::new()
132 .compute_public_ephemeral(&private);
133
134 (
135 ClientHello {
136 username,
137 client: hex::encode(&public),
138 },
139 KeyPair {
140 private: Vec::from(private).into(),
141 public: public.into(),
142 },
143 )
144 }
145
146 pub fn create_evidence(
147 &self,
148 username: String, password: String,
149 salt: String, server: String, pair: KeyPair
150 ) -> Result<(LoginEvidence, ClientVerifier<D>), SimpleSrpError> {
151 let client = srp::Client::<G, D>::new();
152 let session = client.process_reply(
153 pair.private.as_bytes(),
154 username.as_bytes(),
155 password.as_bytes(),
156 &hex::decode(salt)?,
157 &hex::decode(server)?,
158 )?;
159
160 Ok((
161 LoginEvidence {
162 evidence: hex::encode(session.proof()),
163 },
164 session,
165 ))
166 }
167
168 pub fn verify_server<'a>(&self, expected: &'a ClientVerifier<D>, server_evidence: String) -> Result<&'a [u8], SimpleSrpError> {
169 expected.verify_server(&hex::decode(server_evidence)?)
170 .map_err(SimpleSrpError::from)
171 }
172}
173
174pub struct Server<G: Group, D: Digest> {
175 d: PhantomData<(G, D)>,
176}
177
178impl<G: Group, D: Digest> Server<G, D> {
179 pub fn new() -> Self {
180 Server {
181 d: PhantomData,
182 }
183 }
184
185 pub fn hello_reply(&self, salt: String, verifier: String) -> Result<(ServerHello, KeyPair), SimpleSrpError> {
186 let mut private = [0u8; 64];
187 rand::rng().fill_bytes(&mut private);
188 let public = srp::Server::<G, D>::new()
189 .compute_public_ephemeral(&private, &hex::decode(verifier)?);
190
191 Ok((
192 ServerHello {
193 salt,
194 server: hex::encode(&public),
195 },
196 KeyPair {
197 private: Vec::from(private).into(),
198 public: public.into(),
199 },
200 ))
201 }
202
203 pub fn authenticate(
204 &self,
205 username: String, salt: String, verifier: String,
206 pair: KeyPair, client: String, evidence: String
207 ) -> Result<AuthResult, SimpleSrpError> {
208 let server = srp::Server::<G, D>::new();
209 let session = server.process_reply(
210 username.as_bytes(),
211 &hex::decode(salt)?,
212 pair.private.as_bytes(),
213 &hex::decode(verifier)?,
214 &hex::decode(client)?,
215 )?;
216 session.verify_client(&hex::decode(evidence)?)?;
217
218 Ok(AuthResult {
219 result: true,
220 evidence: hex::encode(session.proof()),
221 })
222 }
223}