cypher/
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//! Implementation-independent abstractions for main cryptographic algorithms
25//! used for end-to-end encryption and authorization.
26
27#[macro_use]
28extern crate amplify;
29
30mod digest;
31#[cfg(feature = "x25519")]
32pub mod x25519;
33#[cfg(feature = "ed25519")]
34pub mod ed25519;
35
36pub mod display;
37
38use std::fmt::Debug;
39
40pub use digest::*;
41
42use crate::display::{Encoding, MultiDisplay};
43
44#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From, Default)]
45#[display("invalid secret key")]
46#[non_exhaustive]
47pub struct EcSkInvalid {}
48
49#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From, Default)]
50#[display("invalid public key")]
51#[non_exhaustive]
52pub struct EcPkInvalid {}
53
54#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From, Default)]
55#[display("invalid signature data")]
56#[non_exhaustive]
57pub struct EcSigInvalid {}
58
59#[derive(Copy, Clone, Eq, PartialEq, Debug, Display, Error, From)]
60#[display(doc_comments)]
61#[non_exhaustive]
62pub enum EcdhError {
63    /// public key provided for the ECDH is a weak key
64    WeakPk,
65
66    #[display(inner)]
67    #[from]
68    InvalidPk(EcPkInvalid),
69
70    #[display(inner)]
71    #[from]
72    InvalidSk(EcSkInvalid),
73}
74
75#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
76#[display(doc_comments)]
77pub enum EcSerError {
78    #[display(inner)]
79    #[from]
80    Io(amplify::IoError),
81
82    /// public key has invalid length {0}
83    InvalidKeyLength(usize),
84
85    #[display(inner)]
86    #[from]
87    InvalidKey(EcPkInvalid),
88
89    /// error parsing encoding: {0}
90    DataEncoding(String),
91}
92
93#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
94#[display(doc_comments)]
95pub enum EcVerifyError {
96    /// public key provided for the ECDH is weak key
97    WeakPk,
98
99    #[display(inner)]
100    #[from]
101    InvalidPk(EcPkInvalid),
102
103    #[display(inner)]
104    #[from]
105    InvalidSignature(EcSigInvalid),
106
107    /// the provided signature does not matches public key or is not valid for
108    /// the given message
109    SignatureMismatch,
110}
111
112/// Elliptic-curve based public key type which can be used in ECDH or signature schemes.
113///
114/// # Safety
115///
116/// The type provides no guarantees on the key validity upon deserialization.
117pub trait EcPk: Clone + Eq + Send + Debug + MultiDisplay<Encoding> {
118    const COMPRESSED_LEN: usize;
119    const CURVE_NAME: &'static str;
120
121    // TODO: When generic_const_exprs arrive switch to Self::COMPRESSED_LEN arrays
122    type Compressed: Copy + Sized + Send + AsRef<[u8]>;
123
124    fn base_point() -> Self;
125
126    fn to_pk_compressed(&self) -> Self::Compressed;
127    fn from_pk_compressed(pk: Self::Compressed) -> Result<Self, EcPkInvalid>;
128    fn from_pk_compressed_slice(slice: &[u8]) -> Result<Self, EcPkInvalid>;
129}
130
131/// Elliptic-curve based private key type.
132///
133/// # Safety
134///
135/// The type provides no guarantees on the key validity upon deserialization.
136pub trait EcSk: Clone + Eq + Send {
137    type Pk: EcPk;
138
139    fn generate_keypair() -> (Self, Self::Pk)
140    where Self: Sized;
141    fn to_pk(&self) -> Result<Self::Pk, EcSkInvalid>;
142}
143
144/// Marker trait for elliptic-curve based signatures
145pub trait EcSig: Clone + Eq + Sized + Send + AsRef<[u8]> + Debug + MultiDisplay<Encoding> {
146    const COMPRESSED_LEN: usize;
147
148    type Pk: EcPk;
149    // TODO: When generic_const_exprs arrive switch to Self::COMPRESSED_LEN arrays
150    type Compressed: Copy + Sized + Send + AsRef<[u8]>;
151
152    fn to_sig_compressed(&self) -> Self::Compressed;
153    fn from_sig_compressed(sig: Self::Compressed) -> Result<Self, EcSigInvalid>;
154    fn from_sig_compressed_slice(slice: &[u8]) -> Result<Self, EcSigInvalid>;
155
156    fn verify(&self, pk: &Self::Pk, msg: impl AsRef<[u8]>) -> Result<(), EcVerifyError>;
157}
158
159/// Elliptic-curve based public key type which can be used for ECDH.
160///
161/// # Safety
162///
163/// The type provides no guarantees on the key validity upon deserialization.
164pub trait Ecdh: EcSk {
165    type SharedSecret: Copy + Eq + Sized + Send + AsRef<[u8]>;
166
167    fn ecdh(&self, pk: &Self::Pk) -> Result<Self::SharedSecret, EcdhError>;
168}
169
170/// Signature scheme trait
171pub trait EcSign: EcSk {
172    type Sig: EcSig<Pk = Self::Pk>;
173
174    fn cert(&self) -> Result<Cert<Self::Sig>, EcSkInvalid> {
175        let pk = self.to_pk()?;
176        let sig = self.sign(pk.to_pk_compressed());
177        Ok(Cert { pk, sig })
178    }
179    fn sign(&self, msg: impl AsRef<[u8]>) -> Self::Sig;
180}
181
182#[derive(Clone, Eq, PartialEq, Debug)]
183pub struct CertFormat {
184    pub enc: Encoding,
185    pub sep: &'static str,
186}
187
188impl CertFormat {
189    pub fn new(sep: &'static str, enc: Encoding) -> Self { CertFormat { enc, sep } }
190}
191
192#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
193pub struct Cert<S: EcSig> {
194    pub pk: S::Pk,
195    pub sig: S,
196}
197
198impl<S: EcSig> MultiDisplay<CertFormat> for Cert<S>
199where
200    S: MultiDisplay<Encoding>,
201    S::Pk: MultiDisplay<Encoding>,
202{
203    type Display = String;
204    fn display_fmt(&self, f: &CertFormat) -> Self::Display {
205        format!("{}{}{}", self.pk.display_fmt(&f.enc), f.sep, self.sig.display_fmt(&f.enc))
206    }
207}
208
209impl<S: EcSig> Cert<S> {
210    pub fn verify(&self) -> Result<(), EcVerifyError> {
211        self.sig.verify(&self.pk, self.pk.to_pk_compressed())
212    }
213}
214
215#[cfg(feature = "ec25519")]
216mod ec22519_err_convert {
217    use ec25519::Error;
218
219    use super::*;
220
221    impl From<Error> for EcPkInvalid {
222        fn from(err: Error) -> Self {
223            match err {
224                Error::InvalidPublicKey => EcPkInvalid {},
225
226                Error::WeakPublicKey
227                | Error::InvalidSecretKey
228                | Error::SignatureMismatch
229                | Error::InvalidSignature
230                | Error::InvalidSeed
231                | Error::InvalidBlind
232                | Error::InvalidNoise
233                | Error::ParseError
234                | Error::NonCanonical => {
235                    unreachable!("ECDH in ed25519-compact crate should not generate this errors")
236                }
237            }
238        }
239    }
240
241    impl From<Error> for EcSkInvalid {
242        fn from(err: Error) -> Self {
243            match err {
244                Error::InvalidSecretKey => EcSkInvalid {},
245
246                Error::WeakPublicKey
247                | Error::InvalidPublicKey
248                | Error::SignatureMismatch
249                | Error::InvalidSignature
250                | Error::InvalidSeed
251                | Error::InvalidBlind
252                | Error::InvalidNoise
253                | Error::ParseError
254                | Error::NonCanonical => {
255                    unreachable!("ECDH in ed25519-compact crate should not generate this errors")
256                }
257            }
258        }
259    }
260
261    impl From<Error> for EcSerError {
262        fn from(err: Error) -> Self { EcSerError::DataEncoding(err.to_string()) }
263    }
264
265    impl From<Error> for EcdhError {
266        fn from(err: Error) -> Self {
267            match err {
268                Error::WeakPublicKey => EcdhError::WeakPk,
269                Error::InvalidPublicKey => EcdhError::InvalidPk(EcPkInvalid {}),
270                Error::InvalidSecretKey => EcdhError::InvalidSk(EcSkInvalid {}),
271
272                Error::SignatureMismatch
273                | Error::InvalidSignature
274                | Error::InvalidSeed
275                | Error::InvalidBlind
276                | Error::InvalidNoise
277                | Error::ParseError
278                | Error::NonCanonical => {
279                    unreachable!("ECDH in ed25519-compact crate should not generate this errors")
280                }
281            }
282        }
283    }
284
285    impl From<Error> for EcVerifyError {
286        fn from(err: Error) -> Self {
287            match err {
288                Error::WeakPublicKey => EcVerifyError::WeakPk,
289                Error::InvalidPublicKey => EcVerifyError::InvalidPk(EcPkInvalid {}),
290                Error::SignatureMismatch => EcVerifyError::SignatureMismatch,
291                Error::InvalidSignature => EcVerifyError::InvalidSignature(EcSigInvalid {}),
292
293                Error::InvalidSecretKey
294                | Error::InvalidSeed
295                | Error::InvalidBlind
296                | Error::InvalidNoise
297                | Error::ParseError
298                | Error::NonCanonical => {
299                    unreachable!("ECDH in ed25519-compact crate should not generate this errors")
300                }
301            }
302        }
303    }
304}
305
306#[cfg(feature = "multibase")]
307impl From<multibase::Error> for EcSerError {
308    fn from(err: multibase::Error) -> Self { EcSerError::DataEncoding(err.to_string()) }
309}