1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
23
24#[macro_use]
25extern crate amplify;
26
27use std::fmt::Debug;
28
29use cypher::{Cert, EcPk, EcSig, EcSign};
30
31#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From)]
32#[display(doc_comments)]
33pub enum Error<Id: EcPk> {
34 InvalidLen(usize),
36
37 InvalidCert,
39
40 SigMismatch,
42
43 Unauthorized(Id),
45
46 Completed,
48}
49
50#[derive(Debug)]
51pub enum EidolonState<S: EcSig> {
52 Uninit(Cert<S>, Vec<S::Pk>, bool),
53 Initiator(Cert<S>, Vec<S::Pk>, Vec<u8>),
54 ResponderAwaits(Cert<S>, Vec<S::Pk>, Vec<u8>),
55 CredentialsSent(Vec<S::Pk>, Vec<u8>),
56 Complete(Cert<S>),
57}
58
59impl<S: EcSig> EidolonState<S> {
60 const MESSAGE_LEN: usize = S::Pk::COMPRESSED_LEN + S::COMPRESSED_LEN * 2;
61
62 pub fn initiator(creds: Cert<S>, allowed_ids: Vec<S::Pk>) -> Self {
63 Self::Uninit(creds, allowed_ids, true)
64 }
65
66 pub fn responder(creds: Cert<S>, allowed_ids: Vec<S::Pk>) -> Self {
67 Self::Uninit(creds, allowed_ids, false)
68 }
69
70 pub fn init(&mut self, nonce: impl AsRef<[u8]>) {
71 let nonce = nonce.as_ref().to_vec();
72 *self = match self {
73 Self::Uninit(cert, allowed_ids, true) => {
74 Self::Initiator(cert.clone(), allowed_ids.clone(), nonce)
75 }
76 Self::Uninit(cert, allowed_ids, false) => {
77 Self::ResponderAwaits(cert.clone(), allowed_ids.clone(), nonce)
78 }
79 _ => panic!("repeated call to init method"),
80 };
81 }
82
83 pub fn is_init(&self) -> bool { !matches!(self, Self::Uninit(..)) }
84
85 pub fn advance<P: EcSign>(
86 &mut self,
87 input: &[u8],
88 signer: &P,
89 ) -> Result<Vec<u8>, Error<S::Pk>> {
90 match self {
91 EidolonState::Uninit(_, _, _) => panic!("advancing uninitialized state machine"),
92 EidolonState::Initiator(creds, allowed_ids, nonce) => {
93 debug_assert!(input.is_empty());
94 let data = Self::serialize_creds(creds, nonce, signer);
95 *self = EidolonState::CredentialsSent(allowed_ids.clone(), nonce.clone());
96 Ok(data)
97 }
98 EidolonState::ResponderAwaits(creds, allowed_ids, nonce) => {
99 let cert = Self::verify_input(input, nonce, allowed_ids)?;
100 let data = Self::serialize_creds(creds, nonce, signer);
101 *self = EidolonState::Complete(cert);
102 Ok(data)
103 }
104 EidolonState::CredentialsSent(allowed_ids, nonce) => {
105 let cert = Self::verify_input(input, nonce, allowed_ids)?;
106 *self = EidolonState::Complete(cert);
107 Ok(vec![])
108 }
109 EidolonState::Complete(_) => Err(Error::Completed),
110 }
111 }
112
113 pub fn is_complete(&self) -> bool { matches!(self, Self::Complete(_)) }
114
115 pub fn remote_cert(&self) -> Option<&Cert<S>> {
116 if let Self::Complete(cert) = self {
117 Some(cert)
118 } else {
119 None
120 }
121 }
122
123 pub fn next_read_len(&self) -> usize {
124 match self {
125 EidolonState::Uninit(_, _, _) => 0,
126 EidolonState::Initiator(_, _, _) => 0,
127 EidolonState::ResponderAwaits(_, _, _) | EidolonState::CredentialsSent(_, _) => {
128 S::Pk::COMPRESSED_LEN + 2 * S::COMPRESSED_LEN
129 }
130 EidolonState::Complete(_) => 0,
131 }
132 }
133
134 fn verify_input(
135 input: &[u8],
136 nonce: &[u8],
137 allowed_ids: &[S::Pk],
138 ) -> Result<Cert<S>, Error<S::Pk>> {
139 if input.len() != Self::MESSAGE_LEN {
140 return Err(Error::InvalidLen(input.len()));
141 }
142 let pk = &input[..S::Pk::COMPRESSED_LEN];
143 let next = &input[S::Pk::COMPRESSED_LEN..];
144 let sig = &next[..S::COMPRESSED_LEN];
145 let sig_nonce = &next[S::COMPRESSED_LEN..];
146
147 let pk = S::Pk::from_pk_compressed_slice(pk).expect("fixed length");
148 let sig = S::from_sig_compressed_slice(sig).expect("fixed length");
149 let sig_nonce = S::from_sig_compressed_slice(sig_nonce).expect("fixed length");
150
151 sig.verify(&pk, pk.to_pk_compressed()).map_err(|_| Error::InvalidCert)?;
152 sig_nonce.verify(&pk, nonce).map_err(|_| Error::SigMismatch)?;
153
154 if !allowed_ids.is_empty() {
155 for id in allowed_ids {
156 if id == &pk {
157 return Ok(Cert { pk, sig });
158 }
159 }
160 } else {
161 return Ok(Cert { pk, sig });
162 }
163
164 Err(Error::Unauthorized(pk))
165 }
166
167 fn serialize_creds<P: EcSign>(creds: &Cert<S>, nonce: &[u8], signer: &P) -> Vec<u8> {
168 let sig = signer.sign(nonce);
169 let mut data = Vec::with_capacity(S::Pk::COMPRESSED_LEN + S::COMPRESSED_LEN * 2);
170 data.extend_from_slice(creds.pk.to_pk_compressed().as_ref());
171 data.extend_from_slice(creds.sig.to_sig_compressed().as_ref());
172 data.extend_from_slice(sig.to_sig_compressed().as_ref());
173 data
174 }
175}