summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/cng/aead.rs
blob: 7389c1f017326a5a5b55a2f0a0e96edcdf9f370e (plain)
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
//! Implementation of AEAD using Windows CNG API.
use std::cmp::Ordering;

use crate::{Error, Result};
use crate::crypto::aead::{Aead, CipherOp};
use crate::crypto::mem::secure_cmp;
use crate::seal;
use crate::types::{AEADAlgorithm, SymmetricAlgorithm};

use eax::online::{EaxOnline, Encrypt, Decrypt};
use win_crypto_ng::symmetric::{BlockCipherKey, Aes};
use win_crypto_ng::symmetric::block_cipher::generic_array::{GenericArray, ArrayLength};
use win_crypto_ng::symmetric::block_cipher::generic_array::typenum::{U128, U192, U256};

/// Disables authentication checks.
///
/// This is DANGEROUS, and is only useful for debugging problems with
/// malformed AEAD-encrypted messages.
const DANGER_DISABLE_AUTHENTICATION: bool = false;

trait GenericArrayExt<T, N: ArrayLength<T>> {
    const LEN: usize;

    /// Like [`GenericArray::from_slice`], but fallible.
    fn try_from_slice(slice: &[T]) -> Result<&GenericArray<T, N>> {
        if slice.len() == Self::LEN {
            Ok(GenericArray::from_slice(slice))
        } else {
            Err(Error::InvalidArgument(
                format!("Invalid slice length, want {}, got {}",
                        Self::LEN, slice.len())).into())
        }
    }
}

impl<T, N: ArrayLength<T>> GenericArrayExt<T, N> for GenericArray<T, N> {
    const LEN: usize = N::USIZE;
}

impl AEADAlgorithm {
    pub(crate) fn context(
        &self,
        sym_algo: SymmetricAlgorithm,
        key: &[u8],
        nonce: &[u8],
        op: CipherOp,
    ) -> Result<Box<dyn Aead>> {

        match self {
            AEADAlgorithm::EAX => match sym_algo {
                | SymmetricAlgorithm::AES128 => {
                    let key = GenericArray::try_from_slice(key)?;
                    let nonce = GenericArray::try_from_slice(nonce)?;
                    Ok(match op {
                        CipherOp::Encrypt =>
                            Box::new(EaxOnline::<BlockCipherKey<Aes, U128>, Encrypt>::with_key_and_nonce(key, nonce)),
                        CipherOp::Decrypt =>
                            Box::new(EaxOnline::<BlockCipherKey<Aes, U128>, Decrypt>::with_key_and_nonce(key, nonce)),
                    })
                }
                SymmetricAlgorithm::AES192 => {
                    let key = GenericArray::try_from_slice(key)?;
                    let nonce = GenericArray::try_from_slice(nonce)?;
                    Ok(match op {
                        CipherOp::Encrypt =>
                            Box::new(EaxOnline::<BlockCipherKey<Aes, U192>, Encrypt>::with_key_and_nonce(key, nonce)),
                        CipherOp::Decrypt =>
                            Box::new(EaxOnline::<BlockCipherKey<Aes, U192>, Decrypt>::with_key_and_nonce(key, nonce)),
                    })
                }
                SymmetricAlgorithm::AES256 => {
                    let key = GenericArray::try_from_slice(key)?;
                    let nonce = GenericArray::try_from_slice(nonce)?;
                    Ok(match op {
                        CipherOp::Encrypt =>
                            Box::new(EaxOnline::<BlockCipherKey<Aes, U256>, Encrypt>::with_key_and_nonce(key, nonce)),
                        CipherOp::Decrypt =>
                            Box::new(EaxOnline::<BlockCipherKey<Aes, U256>, Decrypt>::with_key_and_nonce(key, nonce)),
                    })
                }
                _ => Err(Error::UnsupportedSymmetricAlgorithm(sym_algo).into()),
            },
            _ => Err(Error::UnsupportedAEADAlgorithm(self.clone()).into()),
        }
    }
}

macro_rules! impl_aead {
    ($($type: ty),*) => {
        $(
        impl Aead for EaxOnline<$type, Encrypt> {
            fn update(&mut self, ad: &[u8]) -> Result<()> {
                self.update_assoc(ad);
                Ok(())
            }
            fn digest_size(&self) -> usize {
                <eax::Tag as GenericArrayExt<_, _>>::LEN
            }
            fn digest(&mut self, digest: &mut [u8]) -> Result<()> {
                let tag = self.tag_clone();
                digest[..tag.len()].copy_from_slice(&tag[..]);
                Ok(())
            }
            fn encrypt(&mut self, dst: &mut [u8], src: &[u8]) -> Result<()> {
                let len = core::cmp::min(dst.len(), src.len());
                dst[..len].copy_from_slice(&src[..len]);
                EaxOnline::<$type, Encrypt>::encrypt(self, &mut dst[..len]);
                Ok(())
            }
            fn decrypt_verify(&mut self, _dst: &mut [u8], _src: &[u8], _digest: &[u8]) -> Result<()> {
                panic!("AEAD decryption called in the encryption context")
            }
        }
        impl seal::Sealed for EaxOnline<$type, Encrypt> {}
        )*
        $(
        impl Aead for EaxOnline<$type, Decrypt> {
            fn update(&mut self, ad: &[u8]) -> Result<()> {
                self.update_assoc(ad);
                Ok(())
            }
            fn digest_size(&self) -> usize {
                <eax::Tag as GenericArrayExt<_, _>>::LEN
            }
            fn digest(&mut self, digest: &mut [u8]) -> Result<()> {
                let tag = self.tag_clone();
                digest[..tag.len()].copy_from_slice(&tag[..]);
                Ok(())
            }
            fn encrypt(&mut self, _dst: &mut [u8], _src: &[u8]) -> Result<()> {
                panic!("AEAD encryption called in the decryption context")
            }
            fn decrypt_verify(&mut self, dst: &mut [u8], src: &[u8], digest: &[u8]) -> Result<()> {
                let len = core::cmp::min(dst.len(), src.len());
                dst[..len].copy_from_slice(&src[..len]);
                self.decrypt_unauthenticated_hazmat(&mut dst[..len]);
                let mut chunk_digest = vec![0u8; self.digest_size()];

                self.digest(&mut chunk_digest)?;
                if secure_cmp(&chunk_digest[..], digest)
                    != Ordering::Equal && ! DANGER_DISABLE_AUTHENTICATION
                {
                    return Err(Error::ManipulatedMessage.into());
                }
                Ok(())
            }
        }
        impl seal::Sealed for EaxOnline<$type, Decrypt> {}
        )*
    };
}

impl_aead!(BlockCipherKey<Aes, U128>, BlockCipherKey<Aes, U192>, BlockCipherKey<Aes, U256>);