eidolon/
lib.rs

1// Set of libraries for privacy-preserving networking apps
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2023 by
6//     Dr. Maxim Orlovsky <orlovsky@cyphernet.org>
7//
8// Copyright 2022-2023 Cyphernet DAO, Switzerland
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22#![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    /// authorization message has invalid length {0}
35    InvalidLen(usize),
36
37    /// the provided identity certificate doesn't contain a valid signature
38    InvalidCert,
39
40    /// the provided credentials has invalid nonce signature
41    SigMismatch,
42
43    /// remote id {0:?} is not authorized
44    Unauthorized(Id),
45
46    /// authentication is complete and cant advance anymore
47    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}