tink_aead/subtle/
chacha20poly1305.rs

1// Copyright 2020 The Tink-Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15////////////////////////////////////////////////////////////////////////////////
16
17//! ChaCha20 Poly1305 implementation of AEAD.
18
19use chacha20poly1305::{
20    aead::{Aead, Payload},
21    KeyInit,
22};
23use tink_core::{utils::wrap_err, TinkError};
24
25/// Size of a ChaCh20 key in bytes.
26pub const CHA_CHA20_KEY_SIZE: usize = 32;
27/// Size of a ChaCh20 nonce in bytes.
28pub const CHA_CHA20_NONCE_SIZE: usize = 12;
29/// Size of a Poly1305 tag in bytes.
30const POLY1305_TAG_SIZE: usize = 16;
31
32/// `ChaCha20Poly1305` is an implementation of the [`tink_core::Aead`] trait.
33#[derive(Clone)]
34pub struct ChaCha20Poly1305 {
35    key: chacha20poly1305::Key,
36}
37
38impl ChaCha20Poly1305 {
39    /// Return an `ChaCha20Poly1305` instance.
40    /// The `key` argument should be a 32-byte key.
41    pub fn new(key: &[u8]) -> Result<ChaCha20Poly1305, TinkError> {
42        if key.len() != CHA_CHA20_KEY_SIZE {
43            return Err("ChaCha20Poly1305: bad key length".into());
44        }
45
46        Ok(ChaCha20Poly1305 {
47            key: chacha20poly1305::Key::clone_from_slice(key),
48        })
49    }
50}
51
52impl tink_core::Aead for ChaCha20Poly1305 {
53    /// Encrypt `pt` with `aad` as additional
54    /// authenticated data. The resulting ciphertext consists of two parts:
55    /// (1) the nonce used for encryption and (2) the actual ciphertext.
56    fn encrypt(&self, pt: &[u8], aad: &[u8]) -> Result<Vec<u8>, TinkError> {
57        if pt.len() > (isize::MAX as usize) - CHA_CHA20_NONCE_SIZE - POLY1305_TAG_SIZE {
58            return Err("ChaCha20Poly1305: plaintext too long".into());
59        }
60        let cipher = chacha20poly1305::ChaCha20Poly1305::new(&self.key);
61        let n = new_nonce();
62        let ct = cipher
63            .encrypt(&n, Payload { msg: pt, aad })
64            .map_err(|e| wrap_err("ChaCha20Poly1305", e))?;
65
66        let mut ret = Vec::with_capacity(n.len() + ct.len());
67        ret.extend_from_slice(&n);
68        ret.extend_from_slice(&ct);
69        Ok(ret)
70    }
71
72    /// Decrypt `ct` with `aad` as the additional authenticated data.
73    fn decrypt(&self, ct: &[u8], aad: &[u8]) -> Result<Vec<u8>, TinkError> {
74        if ct.len() < CHA_CHA20_NONCE_SIZE + POLY1305_TAG_SIZE {
75            return Err("ChaCha20pPly1305: ciphertext too short".into());
76        }
77
78        let cipher = chacha20poly1305::ChaCha20Poly1305::new(&self.key);
79        let n = chacha20poly1305::Nonce::from_slice(&ct[..CHA_CHA20_NONCE_SIZE]);
80        cipher
81            .decrypt(
82                n,
83                Payload {
84                    msg: &ct[CHA_CHA20_NONCE_SIZE..],
85                    aad,
86                },
87            )
88            .map_err(|e| wrap_err("ChaCha20Poly1305", e))
89    }
90}
91
92/// Create a new nonce for encryption.
93fn new_nonce() -> chacha20poly1305::Nonce {
94    let iv = tink_core::subtle::random::get_random_bytes(CHA_CHA20_NONCE_SIZE);
95    *chacha20poly1305::Nonce::from_slice(&iv)
96}