cypher/
x25519.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//! Curve25519 keys and ECDH algorithm implementation for X25519 scheme.
23
24use std::cmp::Ordering;
25use std::ops::Deref;
26
27use crate::*;
28
29// ============================================================================
30// ed25519_compact keys
31
32impl MultiDisplay<Encoding> for ec25519::x25519::PublicKey {
33    type Display = String;
34    fn display_fmt(&self, f: &Encoding) -> String { f.encode(self.as_slice()) }
35}
36
37impl EcPk for ec25519::x25519::PublicKey {
38    const COMPRESSED_LEN: usize = 32;
39    const CURVE_NAME: &'static str = "Curve25519";
40    type Compressed = [u8; 32];
41
42    fn base_point() -> Self { ec25519::x25519::PublicKey::base_point() }
43
44    fn to_pk_compressed(&self) -> Self::Compressed { *self.deref() }
45
46    fn from_pk_compressed(pk: Self::Compressed) -> Result<Self, EcPkInvalid> {
47        Ok(ec25519::x25519::PublicKey::new(pk))
48    }
49
50    fn from_pk_compressed_slice(slice: &[u8]) -> Result<Self, EcPkInvalid> {
51        if slice.len() != Self::COMPRESSED_LEN {
52            return Err(EcPkInvalid {});
53        }
54        let mut buf = [0u8; 32];
55        buf.copy_from_slice(slice);
56        Self::from_pk_compressed(buf)
57    }
58}
59
60impl EcSk for ec25519::x25519::SecretKey {
61    type Pk = ec25519::x25519::PublicKey;
62
63    fn generate_keypair() -> (Self, Self::Pk)
64    where Self: Sized {
65        let pair = ec25519::x25519::KeyPair::generate();
66        (pair.sk, pair.pk)
67    }
68
69    fn to_pk(&self) -> Result<Self::Pk, EcSkInvalid> {
70        self.recover_public_key().map_err(EcSkInvalid::from)
71    }
72}
73
74// ============================================================================
75// Key newtypes
76
77#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
78#[wrapper(Deref)]
79pub struct SharedSecret([u8; 32]);
80
81impl AsRef<[u8]> for SharedSecret {
82    fn as_ref(&self) -> &[u8] { &self.0 }
83}
84
85impl SharedSecret {
86    pub fn empty() -> Self { SharedSecret([0u8; 32]) }
87
88    pub fn is_empty(self) -> bool { self == Self::empty() }
89}
90
91#[derive(Wrapper, Copy, Clone, PartialEq, Eq, Hash, Debug, From)]
92#[wrapper(Deref)]
93// TODO: Do serde
94/* #[cfg_attr(
95    feature = "serde",
96    derive(Serialize, Deserialize),
97    serde(into = "String", try_from = "String")
98)] */
99pub struct PublicKey(#[from] ec25519::x25519::PublicKey);
100
101impl PartialOrd for PublicKey {
102    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
103        self.0.as_ref().partial_cmp(other.0.as_ref())
104    }
105}
106
107impl Ord for PublicKey {
108    fn cmp(&self, other: &Self) -> Ordering { self.0.as_ref().cmp(other.0.as_ref()) }
109}
110
111impl EcPk for PublicKey {
112    const COMPRESSED_LEN: usize = 32;
113    const CURVE_NAME: &'static str = "Curve25519";
114    type Compressed = [u8; 32];
115
116    fn base_point() -> Self { Self(ec25519::x25519::PublicKey::base_point()) }
117
118    fn to_pk_compressed(&self) -> Self::Compressed { self.0.to_pk_compressed() }
119
120    fn from_pk_compressed(pk: Self::Compressed) -> Result<Self, EcPkInvalid> {
121        ec25519::x25519::PublicKey::from_pk_compressed(pk).map(Self)
122    }
123
124    fn from_pk_compressed_slice(slice: &[u8]) -> Result<Self, EcPkInvalid> {
125        ec25519::x25519::PublicKey::from_pk_compressed_slice(slice).map(Self)
126    }
127}
128
129impl MultiDisplay<Encoding> for PublicKey {
130    type Display = String;
131    fn display_fmt(&self, f: &Encoding) -> Self::Display { self.0.display_fmt(f) }
132}
133
134#[derive(Wrapper, Clone, PartialEq, Eq, Hash, Debug, From)]
135#[wrapper(Deref)]
136pub struct PrivateKey(#[from] ec25519::x25519::SecretKey);
137
138impl PartialOrd for PrivateKey {
139    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
140}
141
142impl Ord for PrivateKey {
143    fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) }
144}
145
146impl EcSk for PrivateKey {
147    type Pk = PublicKey;
148
149    fn generate_keypair() -> (Self, Self::Pk)
150    where Self: Sized {
151        let (sk, pk) = ec25519::x25519::SecretKey::generate_keypair();
152        (sk.into(), pk.into())
153    }
154
155    fn to_pk(&self) -> Result<PublicKey, EcSkInvalid> { self.0.to_pk().map(PublicKey::from) }
156}
157
158// ============================================================================
159// ECDH
160
161impl Ecdh for ec25519::x25519::SecretKey {
162    type SharedSecret = [u8; 32];
163
164    fn ecdh(&self, pk: &Self::Pk) -> Result<Self::SharedSecret, EcdhError> { Ok(*pk.dh(self)?) }
165}
166
167impl Ecdh for PrivateKey {
168    type SharedSecret = SharedSecret;
169
170    fn ecdh(&self, pk: &PublicKey) -> Result<SharedSecret, EcdhError> {
171        self.0.ecdh(&pk.0).map(SharedSecret::from)
172    }
173}