rc_crypto/
hmac.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5// This file contains code that was copied from the ring crate which is under
6// the ISC license, reproduced below:
7
8// Copyright 2015-2017 Brian Smith.
9
10// Permission to use, copy, modify, and/or distribute this software for any
11// purpose with or without fee is hereby granted, provided that the above
12// copyright notice and this permission notice appear in all copies.
13
14// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
15// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
17// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21
22use crate::{constant_time, digest, error::*};
23
24/// A calculated signature value.
25/// This is a type-safe wrappper that discourages attempts at comparing signatures
26/// for equality, which might naively be done using a non-constant-time comparison.
27#[derive(Clone)]
28pub struct Signature(pub(crate) digest::Digest);
29
30impl AsRef<[u8]> for Signature {
31    #[inline]
32    fn as_ref(&self) -> &[u8] {
33        self.0.as_ref()
34    }
35}
36
37/// A key to use for HMAC signing.
38pub struct SigningKey {
39    pub(crate) digest_alg: &'static digest::Algorithm,
40    pub(crate) key_value: Vec<u8>,
41}
42
43impl SigningKey {
44    pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8]) -> Self {
45        SigningKey {
46            digest_alg,
47            key_value: key_value.to_vec(),
48        }
49    }
50
51    #[inline]
52    pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
53        self.digest_alg
54    }
55}
56
57/// A key to use for HMAC authentication.
58pub struct VerificationKey {
59    wrapped: SigningKey,
60}
61
62impl VerificationKey {
63    pub fn new(digest_alg: &'static digest::Algorithm, key_value: &[u8]) -> Self {
64        VerificationKey {
65            wrapped: SigningKey::new(digest_alg, key_value),
66        }
67    }
68
69    #[inline]
70    pub fn digest_algorithm(&self) -> &'static digest::Algorithm {
71        self.wrapped.digest_algorithm()
72    }
73}
74
75/// Calculate the HMAC of `data` using `key` and verify it corresponds to the provided signature.
76pub fn verify(key: &VerificationKey, data: &[u8], signature: &[u8]) -> Result<()> {
77    verify_with_own_key(&key.wrapped, data, signature)
78}
79
80/// Equivalent to `verify` but allows the consumer to pass a `SigningKey`.
81pub fn verify_with_own_key(key: &SigningKey, data: &[u8], signature: &[u8]) -> Result<()> {
82    constant_time::verify_slices_are_equal(sign(key, data)?.as_ref(), signature)
83}
84
85/// Calculate the HMAC of `data` using `key`.
86pub fn sign(key: &SigningKey, data: &[u8]) -> Result<Signature> {
87    let value = nss::pk11::context::hmac_sign(key.digest_alg, &key.key_value, data)?;
88    Ok(Signature(digest::Digest {
89        value,
90        algorithm: *key.digest_alg,
91    }))
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use nss::ensure_initialized;
98
99    const KEY: &[u8] = b"key";
100    const MESSAGE: &[u8] = b"The quick brown fox jumps over the lazy dog";
101    const SIGNATURE_HEX: &str = "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8";
102
103    #[test]
104    fn hmac_sign() {
105        ensure_initialized();
106        let key = SigningKey::new(&digest::SHA256, KEY);
107        let signature = sign(&key, MESSAGE).unwrap();
108        let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
109        assert_eq!(signature.as_ref(), expected_signature.as_slice());
110        assert!(verify_with_own_key(&key, MESSAGE, &expected_signature).is_ok());
111    }
112
113    #[test]
114    fn hmac_sign_gives_different_signatures_for_different_keys() {
115        ensure_initialized();
116        let key = SigningKey::new(&digest::SHA256, b"another key");
117        let signature = sign(&key, MESSAGE).unwrap();
118        let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
119        assert_ne!(signature.as_ref(), expected_signature.as_slice());
120    }
121
122    #[test]
123    fn hmac_sign_gives_different_signatures_for_different_messages() {
124        ensure_initialized();
125        let key = SigningKey::new(&digest::SHA256, KEY);
126        let signature = sign(&key, b"a different message").unwrap();
127        let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
128        assert_ne!(signature.as_ref(), expected_signature.as_slice());
129    }
130
131    #[test]
132    fn hmac_verify() {
133        ensure_initialized();
134        let key = VerificationKey::new(&digest::SHA256, KEY);
135        let expected_signature = hex::decode(SIGNATURE_HEX).unwrap();
136        assert!(verify(&key, MESSAGE, &expected_signature).is_ok());
137    }
138
139    #[test]
140    fn hmac_verify_fails_with_incorrect_signature() {
141        ensure_initialized();
142        let key = VerificationKey::new(&digest::SHA256, KEY);
143        let signature = hex::decode(SIGNATURE_HEX).unwrap();
144        for i in 0..signature.len() {
145            let mut wrong_signature = signature.clone();
146            wrong_signature[i] = wrong_signature[i].wrapping_add(1);
147            assert!(verify(&key, MESSAGE, &wrong_signature).is_err());
148        }
149    }
150
151    #[test]
152    fn hmac_verify_fails_with_incorrect_key() {
153        ensure_initialized();
154        let key = VerificationKey::new(&digest::SHA256, b"wrong key");
155        let signature = hex::decode(SIGNATURE_HEX).unwrap();
156        assert!(verify(&key, MESSAGE, &signature).is_err());
157    }
158
159    #[test]
160    fn hmac_sign_cleanly_rejects_gigantic_keys() {
161        ensure_initialized();
162        if (u32::MAX as usize) < usize::MAX {
163            let key_bytes = vec![0; (u32::MAX as usize) + 1];
164            // Direct construction of SigningKey to avoid instantiating the array.
165            let key = SigningKey {
166                digest_alg: &digest::SHA256,
167                key_value: key_bytes,
168            };
169            assert!(sign(&key, MESSAGE).is_err());
170        }
171    }
172
173    #[test]
174    fn hmac_verify_cleanly_rejects_gigantic_keys() {
175        ensure_initialized();
176        if (u32::MAX as usize) < usize::MAX {
177            let key_bytes = vec![0; (u32::MAX as usize) + 1];
178            // Direct construction of VerificationKey to avoid instantiating the array.
179            let key = VerificationKey {
180                wrapped: SigningKey {
181                    digest_alg: &digest::SHA256,
182                    key_value: key_bytes,
183                },
184            };
185            let signature = hex::decode(SIGNATURE_HEX).unwrap();
186            assert!(verify(&key, MESSAGE, &signature).is_err());
187        }
188    }
189
190    #[test]
191    fn hmac_sign_cleanly_rejects_gigantic_messages() {
192        ensure_initialized();
193        if (u32::MAX as usize) < usize::MAX {
194            let key = SigningKey::new(&digest::SHA256, KEY);
195            let message = vec![0; (u32::MAX as usize) + 1];
196            assert!(sign(&key, &message).is_err());
197        }
198    }
199
200    #[test]
201    fn hmac_verify_cleanly_rejects_gigantic_messages() {
202        ensure_initialized();
203        if (u32::MAX as usize) < usize::MAX {
204            let key = VerificationKey::new(&digest::SHA256, KEY);
205            let signature = hex::decode(SIGNATURE_HEX).unwrap();
206            let message = vec![0; (u32::MAX as usize) + 1];
207            assert!(verify(&key, &message, &signature).is_err());
208        }
209    }
210}