1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Method for handling cryptography including ECDH shared secret derivation and symmetric key encryption
//!
//! **Step 1**: [`get_random_asym_pair`] Generate a public/private key pair on each side
//!
//! **Step 2**: Swap the [`p256::EncodedPoint`]s from step 1 between parties
//!
//! **Step 3**: [`get_shared_asym_secret`] Combine one private key with the other public key to end up at the same shared secret
//!
//! **Step 4**: [`asym_to_sym_key`] Take the [`p256::NistP256`] shared asymmetric secret key and use it as a symmetric key ready for encryption decryption
//!
//! **Step 5**: [`generate_aes_nonce`] Get a nonce to use when encrypting
//!
//! **Step 6**: [`encrypt`] Use the key from step 4 with the nonce from step 5 to encrypt arbitrary data
//!
//! **Step 7**: [`decrypt`] Use the same key from step 6 and ***the same nonce from step 6*** to decrypt the outputted ciphertext from step 6.

#[cfg(test)]
mod tests;

use std::str::FromStr;
use p256::{PublicKey, ecdh::EphemeralSecret, NistP256};
use p256::elliptic_curve::ecdh::SharedSecret;
use aes_gcm_siv::{aead::{Aead, KeyInit}, AeadCore, Aes256GcmSiv, Nonce};

use rand_core::OsRng;

pub const PUBLIC_KEY_OPENING: &str = "-----BEGIN PUBLIC KEY-----\n";
pub const PUBLIC_KEY_CLOSING: &str = "\n-----END PUBLIC KEY-----\n";

/// Generate a public/private key pair
pub fn get_random_asym_pair() -> (EphemeralSecret, String)
{
    let secret = EphemeralSecret::random(&mut OsRng);
    let public_point = secret.public_key().to_string();

    (secret, public_point)
}

/// Use one private key and an opposing public key to arrive at the same shared secret
pub fn get_shared_asym_secret(secret: &EphemeralSecret, opposing_public_key: &String) -> Result<SharedSecret<NistP256>, ()> {

    match PublicKey::from_str(opposing_public_key) {
        Ok(other_public) => {
            Ok(secret.diffie_hellman(&other_public))
        }
        Err(_) => {
            Err(())
        }
    }
}

/// Generate a safe nonce to use in symmetric encryption
pub fn generate_aes_nonce() -> Nonce
{
    Aes256GcmSiv::generate_nonce(OsRng)
}

/// Turn the asymmetric shared secret into a symmetric encryption key
pub fn asym_to_sym_key(secret: &SharedSecret<NistP256>) -> Aes256GcmSiv
{
    Aes256GcmSiv::new(secret.raw_secret_bytes())
}

/// Symmetrically encrypt data using a key derived from ECDH
pub fn encrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec<u8>, ()>
{
    match key.encrypt(nonce, bytes.as_ref()) {
        Ok(r) => {
            Ok(r)
        }
        Err(_) => {
            Err(())
        }
    }
}

/// Symmetrically decrypt data using a key derived from ECDH
pub fn decrypt(key: &Aes256GcmSiv, nonce: &Nonce, bytes: &Vec<u8>) -> Result<Vec<u8>, ()>
{
    match key.decrypt(nonce, bytes.as_ref()) {
        Ok(r) => {
            Ok(r)
        }
        Err(_) => {
            Err(())
        }
    }
}

pub fn trim_public_key(public: &String) -> String
{
    public[27.. 125 + 27].to_string().replace('\n', ".")
}

pub fn fatten_public_key(public: &String) -> String
{
    let mut fattened = public.clone();
    fattened.insert_str(0, PUBLIC_KEY_OPENING);
    fattened.push_str(PUBLIC_KEY_CLOSING);

    fattened.replace('.', "\n")
}