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
157
158
159
160
use crate::prelude::*;
pub use hdi::x_salsa20_poly1305::*;

// -- secretbox encryption -- //

/// Generate a new secure random shared secret suitable for encrypting and
/// decrypting using x_salsa20_poly1305_{en,de}crypt.
/// If key_ref is `None` an opaque reference will be auto-generated.
/// If key_ref is `Some` and that key already exists in the store,
/// this function will return an error.
/// If `Ok`, this function will return the KeyRef by which the shared
/// secret may be accessed.
pub fn x_salsa20_poly1305_shared_secret_create_random(
    key_ref: Option<XSalsa20Poly1305KeyRef>,
) -> ExternResult<XSalsa20Poly1305KeyRef> {
    HDK.with(|h| {
        h.borrow()
            .x_salsa20_poly1305_shared_secret_create_random(key_ref)
    })
}

/// Using the Libsodium box algorithm, encrypt a shared secret so that it
/// may be forwarded to another specific peer.
pub fn x_salsa20_poly1305_shared_secret_export(
    sender: X25519PubKey,
    recipient: X25519PubKey,
    key_ref: XSalsa20Poly1305KeyRef,
) -> ExternResult<XSalsa20Poly1305EncryptedData> {
    HDK.with(|h| {
        h.borrow()
            .x_salsa20_poly1305_shared_secret_export(XSalsa20Poly1305SharedSecretExport::new(
                sender, recipient, key_ref,
            ))
    })
}

/// Using the Libsodium box algorithm, decrypt a shared secret, storing it
/// in the keystore so that it may be used in `x_salsa20_poly1305_decrypt`.
/// This method may be co-opted to ingest shared secrets generated by other
/// custom means. Just be careful, as WASM memory is not a very secure
/// environment for cryptographic secrets.
/// If key_ref is `None` an opaque reference string will be auto-generated.
/// If key_ref is `Some` and that key already exists in the store,
/// this function will return an error.
/// If `Ok`, this function will return the KeyRef by which the shared
/// secret may be accessed.
pub fn x_salsa20_poly1305_shared_secret_ingest(
    recipient: X25519PubKey,
    sender: X25519PubKey,
    encrypted_data: XSalsa20Poly1305EncryptedData,
    key_ref: Option<XSalsa20Poly1305KeyRef>,
) -> ExternResult<XSalsa20Poly1305KeyRef> {
    HDK.with(|h| {
        h.borrow()
            .x_salsa20_poly1305_shared_secret_ingest(XSalsa20Poly1305SharedSecretIngest::new(
                recipient,
                sender,
                encrypted_data,
                key_ref,
            ))
    })
}

/// Libsodium secret-key authenticated encryption: secretbox.
///
/// Libsodium symmetric encryption (a shared key to encrypt/decrypt) is called secretbox.
/// Secretbox can be used directly to hide data and is part of cryptographic systems such as
/// [saltpack](https://saltpack.org/).
///
/// Important information about secretbox:
///  - Wasm memory is NOT secure, a compromised host can steal the key.
///  - The key is SECRET, anyone with the key and nonce can read the encrypted message.
///  - The nonce is PUBLIC and UNIQUE, it must NEVER be re-used (so we don't allow it to be set).
///  - Secretbox is designed for 'small' data, break large data into chunks with unique nonces.
///  - Secretbox is NOT quantum resistant.
///
/// @todo shift all the secret handling into lair so that we only work with opaque key references.
///
/// If you want to hide data:
///  - Consider using capability tokens and/or dedicated DHT networks to control access.
///  - Consider how the shared key is being distributed, e.g. maybe use a key exchange protocol.
///  - Consider that a hybrid approach between network access + encryption might be best.
///  - Consider that encrypted data cannot be validated effectively by the public DHT.
///
/// The main use-case is to control access to data that may be broadcast across a semi-trusted or
/// untrusted context, where the intended recipients have all negotiated or shared a key outside
/// that context.
///
/// If you want to encrypt content so that a _specific_ recipient (i.e. public key) can decrypt it
/// then see the libsodium `box` algorithm or similar.
///
/// See <https://doc.libsodium.org/secret-key_cryptography/secretbox>
/// See <https://nacl.cr.yp.to/secretbox.html>
pub fn x_salsa20_poly1305_encrypt(
    key_ref: XSalsa20Poly1305KeyRef,
    data: XSalsa20Poly1305Data,
) -> ExternResult<XSalsa20Poly1305EncryptedData> {
    HDK.with(|h| {
        h.borrow()
            .x_salsa20_poly1305_encrypt(XSalsa20Poly1305Encrypt::new(key_ref, data))
    })
}

// -- curve25519 box encryption -- //

/// Generate a new x25519 keypair in lair from entropy.
/// Only the pubkey is returned from lair because the secret key never leaves lair.
pub fn create_x25519_keypair() -> ExternResult<X25519PubKey> {
    HDK.with(|h| h.borrow().create_x25519_keypair(()))
}

/// Libsodium keypair based authenticated encryption: box.
///
/// Libsodium asymmetric encryption (two keypairs to encrypt/decrypt) is called box.
/// Box can be used directly to hide data and is part of cryptographic systems such as
/// [saltpack](https://saltpack.org/).
///
/// Important information about box:
///  - The secret half of the keypair is generated in and remains in lair.
///  - The nonce is randomly generated in lair for every call to encrypt.
///  - The nonce is PUBLIC and UNIQUE, it must NEVER be re-used (currently can't be set directly).
///  - Box is the same encryption as secretbox using ECDH off the keypairs for the shared key.
///  - Box is repudible. Either keypair can create any message to be read by the other party. Each
///    party knows they did not create a certain message so they know it came from the counterpary
///    but neither can prove to a third party that any message wasn't forged. Note that if you want
///    the opposite it is not enough to simply layer signatures and encryption.
///    See <https://theworld.com/~dtd/sign_encrypt/sign_encrypt7.html>
///  - To encrypt something potentially large for potentially many recipients efficiently it may be
///    worth chunking the large data, secret boxing it with a unique key for each chunk, then
///    boxing the _keys_ for each recipient alongside the chunks, to avoid encrypting the large
///    data repeatedly for every recipient.
///  - Box is NOT quantum resistant.
///
/// If you want to hide data:
///  - Consider using capability tokens and/or dedicated DHT networks to control access.
///  - Consider how the keypairs are being generated and pubkeys distributed.
///  - Consider that a hybrid approach between network access + encryption might be best.
///  - Consider that encrypted data cannot be validated effectively by the public DHT.
///
/// The main use-case is to control access to data that may be broadcast across a semi-trusted or
/// untrusted context, where the intended recipients have all negotiated or shared a key outside
/// that context.
///
/// If you want to encrypt content so that _any_ recipient with a shared secret can decrypt it
/// then see the libsodium `secretbox` algorithm or similar.
///
/// See <https://doc.libsodium.org/public-key_cryptography/authenticated_encryption>
/// See <https://nacl.cr.yp.to/box.html>
pub fn x_25519_x_salsa20_poly1305_encrypt(
    sender: X25519PubKey,
    recipient: X25519PubKey,
    data: XSalsa20Poly1305Data,
) -> ExternResult<XSalsa20Poly1305EncryptedData> {
    HDK.with(|h| {
        h.borrow()
            .x_25519_x_salsa20_poly1305_encrypt(X25519XSalsa20Poly1305Encrypt::new(
                sender, recipient, data,
            ))
    })
}