dryoc/classic/
crypto_auth.rs1use subtle::ConstantTimeEq;
46
47use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_HMACSHA512256_BYTES, CRYPTO_AUTH_KEYBYTES};
48use crate::error::Error;
49use crate::sha512::Sha512;
50use crate::types::*;
51
52struct HmacSha512State {
53 octx: Sha512,
54 ictx: Sha512,
55}
56
57pub type Key = [u8; CRYPTO_AUTH_KEYBYTES];
59pub type Mac = [u8; CRYPTO_AUTH_BYTES];
61
62fn crypto_auth_hmacsha512256(output: &mut Mac, message: &[u8], key: &Key) {
63 let mut state = crypto_auth_hmacsha512256_init(key);
64 crypto_auth_hmacsha512256_update(&mut state, message);
65 crypto_auth_hmacsha512256_final(state, output);
66}
67
68fn crypto_auth_hmacsha512256_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
69 let mut computed_mac = Mac::default();
70 crypto_auth_hmacsha512256(&mut computed_mac, input, key);
71 if mac.ct_eq(&computed_mac).unwrap_u8() == 1 {
72 Ok(())
73 } else {
74 Err(dryoc_error!("authentication codes do not match"))
75 }
76}
77
78fn crypto_auth_hmacsha512256_init(key: &[u8]) -> HmacSha512State {
79 let mut pad = [0x36u8; 128];
80 let mut khash = [0u8; 64];
81 let keylen = key.len();
82
83 let key = if keylen > 128 {
84 Sha512::compute_into_bytes(&mut khash, key);
85 &khash
86 } else {
87 key
88 };
89
90 let mut ictx = Sha512::new();
91 for i in 0..keylen {
92 pad[i] ^= key[i]
93 }
94 ictx.update(&pad);
95
96 let mut octx = Sha512::new();
97 pad.fill(0x5c);
98 for i in 0..keylen {
99 pad[i] ^= key[i]
100 }
101 octx.update(&pad);
102
103 HmacSha512State { octx, ictx }
104}
105
106fn crypto_auth_hmacsha512256_update(state: &mut HmacSha512State, input: &[u8]) {
107 state.ictx.update(input)
108}
109fn crypto_auth_hmacsha512256_final(
110 mut state: HmacSha512State,
111 output: &mut [u8; CRYPTO_AUTH_HMACSHA512256_BYTES],
112) {
113 let mut ihash = [0u8; 64];
114 state.ictx.finalize_into_bytes(&mut ihash);
115 state.octx.update(&ihash);
116 state.octx.finalize_into_bytes(&mut ihash);
117 output.copy_from_slice(&ihash[..CRYPTO_AUTH_HMACSHA512256_BYTES])
118}
119
120pub fn crypto_auth(mac: &mut Mac, message: &[u8], key: &Key) {
125 crypto_auth_hmacsha512256(mac, message, key)
126}
127
128pub fn crypto_auth_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> {
133 crypto_auth_hmacsha512256_verify(mac, input, key)
134}
135
136pub struct AuthState {
138 state: HmacSha512State,
139}
140
141pub fn crypto_auth_keygen() -> Key {
147 Key::gen()
148}
149
150pub fn crypto_auth_init(key: &Key) -> AuthState {
157 AuthState {
158 state: crypto_auth_hmacsha512256_init(key),
159 }
160}
161
162pub fn crypto_auth_update(state: &mut AuthState, input: &[u8]) {
165 crypto_auth_hmacsha512256_update(&mut state.state, input)
166}
167
168pub fn crypto_auth_final(state: AuthState, output: &mut [u8; CRYPTO_AUTH_BYTES]) {
171 crypto_auth_hmacsha512256_final(state.state, output)
172}
173
174#[cfg(test)]
175mod tests {
176 use rand::TryRngCore;
177
178 use super::*;
179
180 #[test]
181 fn test_crypto_auth() {
182 use rand_core::OsRng;
183 use sodiumoxide::crypto::auth;
184 use sodiumoxide::crypto::auth::Key as SOKey;
185
186 use crate::rng::copy_randombytes;
187
188 for _ in 0..20 {
189 let mlen = (OsRng.try_next_u32().unwrap() % 5000) as usize;
190 let mut message = vec![0u8; mlen];
191 copy_randombytes(&mut message);
192 let key = crypto_auth_keygen();
193
194 let so_tag =
195 auth::authenticate(&message, &SOKey::from_slice(&key).expect("key failed"));
196
197 let mut mac = Mac::new_byte_array();
198 crypto_auth(&mut mac, &message, &key);
199
200 assert_eq!(mac, so_tag.0);
201
202 crypto_auth_verify(&mac, &message, &key).expect("verify failed");
203 crypto_auth_verify(&mac, b"invalid message", &key)
204 .expect_err("verify should have failed");
205 }
206 }
207}