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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! Helpers to use cryptographic data types in nettext

pub use dryoc::types::Bytes;
pub use dryoc::*;

use crate::dec;
use crate::enc;

const BM_HASH: &str = "h.b2";

const BM_SIGNATURE: &str = "sig.ed25519";
const BM_SIGN_KEYPAIR: &str = "sk.ed25519";
const BM_SIGN_PUBKEY: &str = "pk.ed25519";

// ---- types ----

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Hash(pub generichash::Hash);

#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Signature(pub sign::Signature);
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct SigningPublicKey(pub sign::PublicKey);
#[derive(PartialEq, Clone, Debug)]
pub struct SigningKeyPair(pub sign::SigningKeyPair<sign::PublicKey, sign::SecretKey>);

impl SigningKeyPair {
    /// Return the public key of this keypair
    pub fn public_key(&self) -> SigningPublicKey {
        SigningPublicKey(self.0.public_key.clone())
    }
}

// ---- encoding ----

impl enc::Encode for Hash {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_HASH, self.0.as_slice())
    }
}

impl enc::Encode for Signature {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_SIGNATURE, self.0.as_slice())
    }
}

impl enc::Encode for SigningPublicKey {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_SIGN_PUBKEY, self.0.as_slice())
    }
}

impl enc::Encode for SigningKeyPair {
    fn term(&self) -> enc::Result<'_> {
        enc::marked_bytes(BM_SIGN_KEYPAIR, self.0.secret_key.as_slice())
    }
}

// ---- calculating hashes, signatures, etc ----

/// Compute the hash of a payload with default dryoc parameters and optionnal key
pub fn compute_hash(bytes: &[u8], key: Option<&[u8; 32]>) -> Hash {
    Hash(generichash::GenericHash::hash_with_defaults(bytes, key).unwrap())
}

/// Generate a new signing keypair
pub fn gen_signing_keypair() -> SigningKeyPair {
    SigningKeyPair(sign::SigningKeyPair::gen_with_defaults())
}

/// Compute the ed25519 signature of a message using a secret key
pub fn compute_signature(message: &[u8], keypair: &SigningKeyPair) -> Signature {
    Signature(
        keypair
            .0
            .sign_with_defaults(message)
            .unwrap()
            .into_parts()
            .0,
    )
}

/// Verify the ed25519 signature of a message using a public key
pub fn verify_signature(
    signature: &Signature,
    message: &[u8],
    public_key: &SigningPublicKey,
) -> bool {
    sign::SignedMessage::from_parts(signature.0.clone(), message.to_vec())
        .verify(&public_key.0)
        .is_ok()
}

// ---- decode helpers ----

pub trait CryptoDec {
    /// Try to interpret this string as a Blake2b512 digest
    /// (32-bytes base64 encoded, prefixed by `h.b2:`)
    ///
    /// Example:
    ///
    /// ```
    /// use nettext::dec::decode;
    /// use nettext::crypto::{compute_hash, CryptoDec};
    ///
    /// let term = decode(b"{
    ///     message = hello;
    ///     hash = h.b2:Mk3PAn3UowqTLEQfNlol6GsXPe-kuOWJSCU0cbgbcs8;
    /// }").unwrap();
    /// let [msg, hash] = term.dict_of(["message", "hash"], false).unwrap();
    /// let expected_hash = compute_hash(msg.raw(), None);
    /// assert_eq!(hash.hash().unwrap(), expected_hash);
    /// ```
    fn hash(&self) -> Result<Hash, dec::TypeError>;

    /// Try to interpret this string as an ed25519 signature
    /// (64 bytes base64 encoded, prefixed by `sig.ed25519:`)
    fn signature(&self) -> Result<Signature, dec::TypeError>;

    /// Try to interpret this string as an ed25519 keypair
    /// (64 bytes base64 encoded, prefixed by `sk.ed25519:`)
    fn keypair(&self) -> Result<SigningKeyPair, dec::TypeError>;

    /// Try to interpret this string as an ed25519 public key
    /// (32 bytes base64 encoded, prefixed by `pk.ed25519:`)
    fn public_key(&self) -> Result<SigningPublicKey, dec::TypeError>;
}

impl<'a, 'b> CryptoDec for dec::Term<'a, 'b> {
    fn hash(&self) -> Result<Hash, dec::TypeError> {
        Ok(Hash(generichash::Hash::from(
            self.marked_bytes_exact(BM_HASH)?,
        )))
    }

    /// Try to interpret this string as an ed25519 signature (64 bytes base64 encoded)
    fn signature(&self) -> Result<Signature, dec::TypeError> {
        Ok(Signature(sign::Signature::from(
            self.marked_bytes_exact(BM_SIGNATURE)?,
        )))
    }

    fn keypair(&self) -> Result<SigningKeyPair, dec::TypeError> {
        let secret_key = sign::SecretKey::from(self.marked_bytes_exact(BM_SIGN_KEYPAIR)?);
        Ok(SigningKeyPair(sign::SigningKeyPair::from_secret_key(
            secret_key,
        )))
    }

    fn public_key(&self) -> Result<SigningPublicKey, dec::TypeError> {
        Ok(SigningPublicKey(sign::PublicKey::from(
            self.marked_bytes_exact(BM_SIGN_PUBKEY)?,
        )))
    }
}