dup_crypto/private_message/
authentication.rs

1//  Copyright (C) 2020  Éloïs SANCHEZ.
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16//! Handle private message authentication policy
17
18use super::{PrivateMessageError, AUTHENTICATION_DATAS_LEN, SENDER_PUBLIC_KEY_LEN};
19use crate::keys::x25519::{diffie_hellman, X25519PublicKey, X25519SecretKey};
20use crate::keys::{KeyPair, PublicKey, Signator};
21use crate::{
22    hashs::Hash64,
23    keys::ed25519::{Ed25519KeyPair, PublicKey as Ed25519PublicKey, Signature},
24};
25use std::{convert::TryFrom, hint::unreachable_unchecked};
26
27#[derive(Clone, Copy, Debug)]
28/// Authentication policy.
29///
30/// **Warning**: Take the time to study which is the authentication policy adapted to **your specific use case**.
31/// Choosing an unsuitable authentication policy can be **dramatic for your end users**.
32pub enum AuthenticationPolicy {
33    /// Only the sender and the recipient have proof that the message was written by one of them.
34    /// The recipient knows that he is not the author of the message so he has proof that the message was necessarily written by the sender.
35    /// If your use case is the encrypted correspondence between machines in a decentralized network,
36    /// and you sometimes need to prove that a machine has sent this or that message (to prove for example that it has not honored a commitment),
37    /// then choose policy `Signature` instead.
38    PrivateAuthentication,
39    /// The sender proves that he is the author of the message.
40    /// If the message is publicly disclosed, everyone will have proof that the sender is indeed the one who wrote the message.
41    /// In certain uses this can be harmful to the sender: in case of conflict with the recipient,
42    /// the latter may threaten to disclose their private correspondence to blackmail the sender.
43    /// If your use case is private messaging between humans, choose method `PrivateAuthentication` instead.
44    Signature,
45}
46
47impl From<AuthenticationPolicy> for u8 {
48    fn from(val: AuthenticationPolicy) -> Self {
49        match val {
50            AuthenticationPolicy::PrivateAuthentication => 0,
51            AuthenticationPolicy::Signature => 1,
52        }
53    }
54}
55
56impl From<u8> for AuthenticationPolicy {
57    fn from(source: u8) -> Self {
58        match source {
59            0 => Self::PrivateAuthentication,
60            _ => Self::Signature,
61        }
62    }
63}
64
65pub(crate) struct AuthenticationProof([u8; 64]);
66
67impl AsRef<[u8]> for AuthenticationProof {
68    fn as_ref(&self) -> &[u8] {
69        &self.0
70    }
71}
72
73pub(crate) fn write_anthentication_datas(
74    sender_public_key: &Ed25519PublicKey,
75    authent_proof: AuthenticationProof,
76    authent_policy: AuthenticationPolicy,
77) -> impl AsRef<[u8]> + IntoIterator<Item = u8> {
78    let mut authent_datas = arrayvec::ArrayVec::<u8, 97>::new();
79    authent_datas
80        .try_extend_from_slice(sender_public_key.datas.as_ref())
81        .unwrap_or_else(|_| unsafe { unreachable_unchecked() }); // It's safe because the public key is 32 bytes long.
82    authent_datas
83        .try_extend_from_slice(authent_proof.as_ref())
84        .unwrap_or_else(|_| unsafe { unreachable_unchecked() }); // It's safe because the authent_proof is 64 bytes long.
85    authent_datas.push(authent_policy.into());
86    authent_datas
87}
88
89pub(crate) fn generate_authentication_proof(
90    authentication_policy: AuthenticationPolicy,
91    sender_keypair: &Ed25519KeyPair,
92    receiver_public_key: &Ed25519PublicKey,
93    message: &[u8],
94) -> AuthenticationProof {
95    AuthenticationProof(match authentication_policy {
96        AuthenticationPolicy::PrivateAuthentication => diffie_hellman(
97            X25519SecretKey::from(sender_keypair.seed()),
98            X25519PublicKey::from(receiver_public_key),
99            |key_material| Hash64::sha512_multipart(&[message, key_material]).0,
100        ),
101        AuthenticationPolicy::Signature => {
102            sender_keypair.generate_signator().sign(message.as_ref()).0
103        }
104    })
105}
106
107pub(crate) fn verify_authentication_proof(
108    receiver_key_pair: &Ed25519KeyPair,
109    message: &[u8],
110    authentication_datas: &[u8],
111) -> Result<(Ed25519PublicKey, Option<Signature>), PrivateMessageError> {
112    let sender_public_key =
113        Ed25519PublicKey::try_from(&authentication_datas[..SENDER_PUBLIC_KEY_LEN])
114            .map_err(PrivateMessageError::InvalidSenderPubKey)?;
115    let mut authent_proof = AuthenticationProof([0u8; 64]);
116    authent_proof.0.copy_from_slice(
117        &authentication_datas[SENDER_PUBLIC_KEY_LEN..(AUTHENTICATION_DATAS_LEN - 1)],
118    );
119    let mut signature_opt = None;
120    match AuthenticationPolicy::from(authentication_datas[AUTHENTICATION_DATAS_LEN - 1]) {
121        AuthenticationPolicy::PrivateAuthentication => {
122            let expected_proof = AuthenticationProof(diffie_hellman(
123                X25519SecretKey::from(receiver_key_pair.seed()),
124                X25519PublicKey::from(&sender_public_key),
125                |key_material| Hash64::sha512_multipart(&[message, key_material]).0,
126            ));
127            for i in 0..32 {
128                if expected_proof.0[i] != authent_proof.0[i] {
129                    return Err(PrivateMessageError::InvalidAuthenticationProof);
130                }
131            }
132        }
133        AuthenticationPolicy::Signature => {
134            signature_opt = Some(Signature(authent_proof.0));
135            sender_public_key
136                .verify(message, &Signature(authent_proof.0))
137                .map_err(|_| PrivateMessageError::InvalidAuthenticationProof)?;
138        }
139    }
140    Ok((sender_public_key, signature_opt))
141}
142
143#[cfg(test)]
144mod tests {
145
146    use super::*;
147    use crate::keys::ed25519::KeyPairFromSeed32Generator;
148    use crate::seeds::Seed32;
149
150    const MESSAGE: &[u8] = b"message";
151
152    #[test]
153    fn private_authent_ok() -> Result<(), PrivateMessageError> {
154        let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
155        let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
156
157        let authent_policy = AuthenticationPolicy::PrivateAuthentication;
158
159        let authent_proof = generate_authentication_proof(
160            authent_policy,
161            &sender_key_pair,
162            &receiver_key_pair.public_key(),
163            MESSAGE,
164        );
165
166        let authent_datas = write_anthentication_datas(
167            &sender_key_pair.public_key(),
168            authent_proof,
169            authent_policy,
170        );
171
172        verify_authentication_proof(&receiver_key_pair, MESSAGE, authent_datas.as_ref())?;
173
174        Ok(())
175    }
176
177    #[test]
178    fn invalid_sender_pubkey() -> Result<(), PrivateMessageError> {
179        let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
180        let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
181
182        let authent_policy = AuthenticationPolicy::PrivateAuthentication;
183
184        let authent_proof = generate_authentication_proof(
185            authent_policy,
186            &sender_key_pair,
187            &receiver_key_pair.public_key(),
188            MESSAGE,
189        );
190
191        let mut authent_datas: Vec<u8> = write_anthentication_datas(
192            &sender_key_pair.public_key(),
193            authent_proof,
194            authent_policy,
195        )
196        .as_ref()
197        .to_vec();
198
199        let invalid_pubkey_bytes = [
200            206u8, 58, 67, 221, 20, 133, 0, 225, 86, 115, 26, 104, 142, 116, 140, 132, 119, 51,
201            175, 45, 82, 225, 14, 195, 7, 107, 43, 212, 8, 37, 234, 23,
202        ];
203
204        authent_datas[..32].copy_from_slice(&invalid_pubkey_bytes);
205
206        if let Err(PrivateMessageError::InvalidSenderPubKey(_)) =
207            verify_authentication_proof(&receiver_key_pair, MESSAGE, authent_datas.as_ref())
208        {
209            Ok(())
210        } else {
211            panic!("Expected PrivateMessageError::InvalidSenderPubKey.")
212        }
213    }
214
215    #[test]
216    fn invalid_private_authent_proof() -> Result<(), PrivateMessageError> {
217        let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
218        let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
219
220        let authent_policy = AuthenticationPolicy::PrivateAuthentication;
221
222        let authent_proof = generate_authentication_proof(
223            authent_policy,
224            &receiver_key_pair, // invalid key pair
225            &receiver_key_pair.public_key(),
226            MESSAGE,
227        );
228
229        let authent_datas = write_anthentication_datas(
230            &sender_key_pair.public_key(),
231            authent_proof,
232            authent_policy,
233        );
234
235        if let Err(PrivateMessageError::InvalidAuthenticationProof) =
236            verify_authentication_proof(&receiver_key_pair, MESSAGE, authent_datas.as_ref())
237        {
238            Ok(())
239        } else {
240            panic!("Expected PrivateMessageError::InvalidSenderPubKey.")
241        }
242    }
243
244    #[test]
245    fn invalid_sig_authent_proof() -> Result<(), PrivateMessageError> {
246        let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
247        let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
248
249        let authent_policy = AuthenticationPolicy::Signature;
250
251        let authent_proof = generate_authentication_proof(
252            authent_policy,
253            &receiver_key_pair, // invalid key pair
254            &receiver_key_pair.public_key(),
255            MESSAGE,
256        );
257
258        let authent_datas = write_anthentication_datas(
259            &sender_key_pair.public_key(),
260            authent_proof,
261            authent_policy,
262        );
263
264        if let Err(PrivateMessageError::InvalidAuthenticationProof) =
265            verify_authentication_proof(&receiver_key_pair, MESSAGE, authent_datas.as_ref())
266        {
267            Ok(())
268        } else {
269            panic!("Expected PrivateMessageError::InvalidSenderPubKey.")
270        }
271    }
272
273    #[test]
274    fn sig_authent_ok() -> Result<(), PrivateMessageError> {
275        let sender_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
276        let receiver_key_pair = KeyPairFromSeed32Generator::generate(Seed32::random()?);
277
278        let authent_policy = AuthenticationPolicy::Signature;
279
280        let authent_proof = generate_authentication_proof(
281            authent_policy,
282            &sender_key_pair,
283            &receiver_key_pair.public_key(),
284            MESSAGE,
285        );
286
287        let authent_datas = write_anthentication_datas(
288            &sender_key_pair.public_key(),
289            authent_proof,
290            authent_policy,
291        );
292
293        verify_authentication_proof(&receiver_key_pair, MESSAGE, authent_datas.as_ref())?;
294
295        Ok(())
296    }
297}