mini_monocypher/
lib.rs

1//! # mini-monocypher
2//!
3//! A C-like Rust API for [`Monocypher`]
4//!
5//! This documentation covers the usage of Monocypher through these bindings,
6//! but for the intricacies regarding cryptographic usage and security hygiene
7//! it is recommended to refer to the [`Monocypher` manual].
8//!
9//! [`Monocypher`]: https://monocypher.org/
10//! [`Monocypher` manual]: https://monocypher.org/manual/
11
12#![allow(non_upper_case_globals)]
13#![allow(non_camel_case_types)]
14#![allow(non_snake_case)]
15#![deny(missing_docs)]
16
17use std::error::Error;
18use std::fmt;
19
20/// Errors returned by Monocypher
21#[derive(Debug)]
22pub enum ErrorKind {
23    /// Decryption failed due to incorrect nonce, integrity failure
24    /// in ciphertext, or incorrect MAC.
25    AuthenticationFailure,
26}
27
28impl fmt::Display for ErrorKind {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        write!(f, "authentication failure trying to decrypt ciphertext")
31    }
32}
33
34impl Error for ErrorKind {}
35
36/// Computes a BLAKE2b hash for a message.
37///
38/// The output hash is written to `hash` which must have a length
39/// between 1 and 64. Anything below 32 is discouraged when using
40/// BLAKE2b as a general purpose hash function.
41///
42/// # Examples
43///
44/// ```
45/// use mini_monocypher::crypto_blake2b;
46///
47/// let mut hash = [0u8; 32];
48/// let message = b"Lorem ipsum";
49/// crypto_blake2b(&mut hash, message);
50/// ```
51///
52/// # Panics
53///
54/// This function can panic if `hash.len()` is not between 1 and 64.
55pub fn crypto_blake2b(hash: &mut [u8], message: &[u8]) {
56    assert!(hash.len() > 0 && hash.len() <= 64);
57
58    let hash_buf = hash.as_mut_ptr();
59    let message_buf = message.as_ptr();
60
61    unsafe {
62        mini_monocypher_sys::crypto_blake2b(hash_buf, hash.len(), message_buf, message.len());
63    }
64}
65
66/// Computes a BLAKE2b Message Authentication Code (MAC) for a message.
67///
68/// The output MAC is written to `hash` which must have a length
69/// between 1 and 64. The input key material `key` must have a length
70/// between 0 and 64.
71///
72/// # Examples
73///
74/// In this example we will derive a receive and transmit key from input key
75/// material.
76///
77/// ```
78/// use mini_monocypher::crypto_blake2b_keyed;
79///
80/// use rand_core::{OsRng, RngCore};
81/// let mut input_key = [0u8; 32];
82/// let mut rx_key = [0u8; 32];
83/// let mut tx_key = [0u8; 32];
84///
85/// // usually key material comes from somewhere but for this example
86/// // we will just fill with cryptographic random bytes
87/// OsRng.fill_bytes(&mut input_key);
88///
89/// crypto_blake2b_keyed(&mut rx_key, &input_key, b"receive");
90/// crypto_blake2b_keyed(&mut tx_key, &input_key, b"transmit");
91/// ```
92///
93/// # Panics
94///
95/// This function can panic if `hash.len()` is not between 1 and 64,
96/// or if `key.len()` is >64.
97pub fn crypto_blake2b_keyed(hash: &mut [u8], key: &[u8], message: &[u8]) {
98    assert!(hash.len() > 0 && hash.len() <= 64);
99    assert!(key.len() <= 64);
100
101    let hash_buf = hash.as_mut_ptr();
102    let key_buf = key.as_ptr();
103    let message_buf = message.as_ptr();
104
105    unsafe {
106        mini_monocypher_sys::crypto_blake2b_keyed(
107            hash_buf,
108            hash.len(),
109            key_buf,
110            key.len(),
111            message_buf,
112            message.len(),
113        );
114    }
115}
116
117/// BLAKE2b context for use with incremental interface.
118type crypto_blake2b_ctx = mini_monocypher_sys::crypto_blake2b_ctx;
119
120/// Returns a BLAKE2b context for use with the incremental interface.
121/// Needs to be initialised with [`crypto_blake2b_init`].
122pub fn crypto_blake2b_ctx_new() -> crypto_blake2b_ctx {
123    crypto_blake2b_ctx {
124        hash: [0u64; 8],
125        input_offset: [0u64; 2],
126        input: [0u64; 16],
127        input_idx: 0,
128        hash_size: 0,
129    }
130}
131
132/// Initialises a BLAKE2B context for use with the incremental interface.
133///
134/// `hash_size` specifies the output hash length and must be between 1 and 64.
135///
136/// Use this interface if you don't want handle streams of data or large files
137/// without using too much memory.
138///
139/// # Examples
140///
141/// This example demonstrates using the incremental interface.
142///
143/// ```
144/// use mini_monocypher::{
145///   crypto_blake2b_ctx_new,
146///   crypto_blake2b_init,
147///   crypto_blake2b_update,
148///   crypto_blake2b_final,
149/// };
150///
151/// let mut hash = [0u8; 32];
152/// let mut ctx = crypto_blake2b_ctx_new();
153///
154/// crypto_blake2b_init(&mut ctx, hash.len());
155/// crypto_blake2b_update(&mut ctx, b"Lorem");
156/// crypto_blake2b_update(&mut ctx, b" ");
157/// crypto_blake2b_update(&mut ctx, b"ipsum");
158/// crypto_blake2b_final(&mut ctx, &mut hash);
159/// ```
160///
161/// # Panics
162///
163/// The function will panic if `hash_size` is not between 1 and 64.
164pub fn crypto_blake2b_init(ctx: &mut crypto_blake2b_ctx, hash_size: usize) {
165    assert!(hash_size > 0 && hash_size <= 64);
166
167    let ctx_ptr = ctx as *mut crypto_blake2b_ctx;
168
169    unsafe {
170        mini_monocypher_sys::crypto_blake2b_init(ctx_ptr, hash_size);
171    }
172}
173
174/// Incrementally computes a BLAKE2b hash based on `ctx` and a message.
175///
176/// See [`crypto_blake2b_init`] for a full example of using the incremental
177/// interface.
178pub fn crypto_blake2b_update(ctx: &mut crypto_blake2b_ctx, message: &[u8]) {
179    let ctx_ptr = ctx as *mut crypto_blake2b_ctx;
180    let message_buf = message.as_ptr();
181
182    unsafe {
183        mini_monocypher_sys::crypto_blake2b_update(ctx_ptr, message_buf, message.len());
184    }
185}
186
187/// Computes the final BLAKE2b hash based on `ctx`. The output of the hash
188/// will reside in `hash`.
189///
190/// `hash.len()` must be at least the size specified as
191/// `hash_size` in [`crypto_blake2b_init`] and cannot be greater than 64.
192///
193/// See [`crypto_blake2b_init`] for a full example of using the incremental
194/// interface.
195///
196/// # Panics
197///
198/// This function will panic if `hash.len()` is >64.
199pub fn crypto_blake2b_final(ctx: &mut crypto_blake2b_ctx, hash: &mut [u8]) {
200    assert!(hash.len() > 0 && hash.len() <= 64);
201    let ctx_ptr = ctx as *mut crypto_blake2b_ctx;
202    let hash_buf = hash.as_mut_ptr();
203
204    unsafe {
205        mini_monocypher_sys::crypto_blake2b_final(ctx_ptr, hash_buf);
206    }
207}
208
209/// Encrypts and authenticates a plaintext. The output can then be decrypted
210/// by [`crypto_aead_unlock`].
211///
212/// Given a 32-byte cryptographic quality `key`, and a 24-byte unique or
213/// random `nonce`, (optional) additional data `ad`, encrypts `plain_text` into
214/// `cipher_text` and `mac`.
215///
216/// `cipher_text.len()` must be >= `plain_text.len()`.
217///
218/// It is recommended to read the AEAD section of the [`Monocypher` manual]
219/// for the full intricacies on using the `crypto_aead_{lock,unlock}` interface.
220///
221/// # Examples
222///
223/// This example is a full end to end example of encryption and decryption:
224///
225/// ```
226/// use rand_core::{OsRng, RngCore};
227/// use mini_monocypher::{crypto_aead_lock, crypto_aead_unlock};
228///
229/// let mut key = [0u8; 32];
230/// let mut nonce = [0u8; 24];
231/// let ad = b"authentication data";
232/// let plaintext = b"hello world";
233/// let mut aead_ciphertext = [0u8; 11+16]; // plaintext.len() + MAC
234///
235/// OsRng.fill_bytes(&mut key);
236/// OsRng.fill_bytes(&mut nonce);
237///
238/// // encrypt
239///
240/// // note: the aead_ciphertext is the ciphertext (size is plaintext.len())
241/// // concatenated with the MAC. If using random nonce, you would also want
242/// // to include nonce as part of the aead_ciphertext as well.
243/// let (ciphertext_detached, mac) = aead_ciphertext.split_at_mut(plaintext.len());
244///
245/// crypto_aead_lock(
246///     ciphertext_detached,
247///     mac,
248///     &key,
249///     &nonce,
250///     Some(ad),
251///     plaintext,
252/// );
253///
254/// // decrypt
255///
256/// let mut plaintext = [0u8; 11];
257///
258/// crypto_aead_unlock(
259///     &mut plaintext,
260///     &aead_ciphertext[(aead_ciphertext.len() - 16)..],
261///     &key,
262///     &nonce,
263///     Some(ad),
264///     &aead_ciphertext[..aead_ciphertext.len() - 16]
265/// ).expect("decryption should succeed");
266/// ```
267///
268/// # Panics
269///
270/// This function panics if `mac.len() != 16` or `key.len() != 32`
271/// or `nonce.len() != 24` or if `cipher_text.len() < plain_text.len()`.
272///
273/// [`Monocypher` manual]: (https://monocypher.org/manual/aead)
274pub fn crypto_aead_lock(
275    cipher_text: &mut [u8],
276    mac: &mut [u8],
277    key: &[u8],
278    nonce: &[u8],
279    ad: Option<&[u8]>,
280    plain_text: &[u8],
281) {
282    assert!(mac.len() == 16);
283    assert!(key.len() == 32);
284    assert!(nonce.len() == 24);
285    assert!(cipher_text.len() >= plain_text.len());
286
287    let cipher_text_buf = cipher_text.as_mut_ptr();
288    let mac_buf = mac.as_mut_ptr();
289    let key_buf = key.as_ptr();
290    let nonce_buf = nonce.as_ptr();
291    let (ad_buf, ad_len) = match ad {
292        None => (std::ptr::null(), 0),
293        Some(x) => (x.as_ptr(), x.len()),
294    };
295    let plain_text_buf = plain_text.as_ptr();
296
297    unsafe {
298        mini_monocypher_sys::crypto_aead_lock(
299            cipher_text_buf,
300            mac_buf,
301            key_buf,
302            nonce_buf,
303            ad_buf,
304            ad_len,
305            plain_text_buf,
306            plain_text.len(),
307        );
308    }
309}
310
311/// Decrypts an authenticated ciphertext. Returns `()` if successful,
312/// or [`ErrorKind`] if there is a decryption failure.
313///
314/// Given a 32-byte cryptographic quality `key`, a 24-byte `nonce`,
315/// (optional) additional data `ad`, decrypt `cipher_text` and 16-byte `mac` into
316/// `plain_text`.
317///
318/// `plain_text.len()` must be >= `cipher_text.len()`.
319///
320/// See [`crypto_aead_lock`] for a full round trip example of performing
321/// a lock (encrypt) and unlock (decrypt).
322///
323/// It crypto_aead_lockis recommended to read the AEAD section of the [`Monocypher` manual]
324/// for the full intricacies on using the `crypto_aead_{lock,unlock}` interface.
325///
326/// # Panics
327///
328/// This function panics if `mac.len() != 16` or `key.len() != 32`
329/// or `nonce.len() != 24` or if `plain_text.len() < cipher_text.len()`.
330///
331/// [`Monocypher` manual]: (https://monocypher.org/manual/aead)
332pub fn crypto_aead_unlock(
333    plain_text: &mut [u8],
334    mac: &[u8],
335    key: &[u8],
336    nonce: &[u8],
337    ad: Option<&[u8]>,
338    cipher_text: &[u8],
339) -> Result<(), ErrorKind> {
340    assert!(mac.len() == 16);
341    assert!(key.len() == 32);
342    assert!(nonce.len() == 24);
343    assert!(plain_text.len() >= cipher_text.len());
344
345    let plain_text_buf = plain_text.as_mut_ptr();
346    let mac_buf = mac.as_ptr();
347    let key_buf = key.as_ptr();
348    let nonce_buf = nonce.as_ptr();
349    let (ad_buf, ad_len) = match ad {
350        None => (std::ptr::null(), 0),
351        Some(x) => (x.as_ptr(), x.len()),
352    };
353    let cipher_text_buf = cipher_text.as_ptr();
354
355    unsafe {
356        let result = mini_monocypher_sys::crypto_aead_unlock(
357            plain_text_buf,
358            mac_buf,
359            key_buf,
360            nonce_buf,
361            ad_buf,
362            ad_len,
363            cipher_text_buf,
364            cipher_text.len(),
365        );
366
367        if result != 0 {
368            return Err(ErrorKind::AuthenticationFailure);
369        }
370    }
371
372    Ok(())
373}
374
375/// Generate an X25519 public key from a secret key.
376///
377/// # Examples
378///
379/// ```
380/// use rand_core::{OsRng, RngCore};
381/// use mini_monocypher::crypto_x25519_public_key;
382///
383/// let mut secret_key = [0u8; 32];
384/// let mut public_key = [0u8; 32];
385///
386/// OsRng.fill_bytes(&mut secret_key);
387/// crypto_x25519_public_key(&mut public_key, &secret_key);
388/// ```
389pub fn crypto_x25519_public_key(public_key: &mut [u8], secret_key: &[u8]) {
390    assert!(public_key.len() == 32);
391    assert!(secret_key.len() == 32);
392
393    let pub_buf = public_key.as_mut_ptr();
394    let secret_buf = secret_key.as_ptr();
395
396    unsafe { mini_monocypher_sys::crypto_x25519_public_key(pub_buf, secret_buf) }
397}
398
399/// Perform an X25519 key exchange between `your_secret_key` and `their_public_key`.
400///
401/// # Examples
402///
403/// ```
404/// use rand_core::{OsRng, RngCore};
405/// use mini_monocypher::{crypto_x25519, crypto_x25519_public_key};
406///
407/// let mut your_secret_key = [0u8; 32];
408/// OsRng.fill_bytes(&mut your_secret_key);
409///
410/// // generate other party's public key
411/// let mut their_secret_key = [0u8; 32]; // assume you don't know this
412/// let mut their_public_key = [0u8; 32];
413/// OsRng.fill_bytes(&mut their_secret_key);
414/// crypto_x25519_public_key(&mut their_public_key, &their_secret_key);
415///
416/// // assume you only have: your_secret_key and their_public_key
417/// let mut shared_secret = [0u8; 32];
418/// crypto_x25519(&mut shared_secret, &your_secret_key, &their_public_key);
419///
420/// // to use shared_secret, use a crypto_blake2b as a KDF
421/// // or crypto_blake2b_keyed with key=shared_secret to derive
422/// // multiple keys
423/// ```
424pub fn crypto_x25519(
425    raw_shared_secret: &mut [u8],
426    your_secret_key: &[u8],
427    their_public_key: &[u8],
428) {
429    assert!(raw_shared_secret.len() == 32);
430    assert!(your_secret_key.len() == 32);
431    assert!(their_public_key.len() == 32);
432
433    let raw_buf = raw_shared_secret.as_mut_ptr();
434    let secret_buf = your_secret_key.as_ptr();
435    let pub_buf = their_public_key.as_ptr();
436
437    unsafe { mini_monocypher_sys::crypto_x25519(raw_buf, secret_buf, pub_buf) }
438}