parity_crypto/hmac/
mod.rs

1// Copyright 2020 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::marker::PhantomData;
10use std::ops::Deref;
11
12use digest::generic_array::{
13	typenum::{U32, U64},
14	GenericArray,
15};
16use hmac::{Hmac, Mac as _, NewMac as _};
17use zeroize::Zeroize;
18
19use crate::digest::{Sha256, Sha512};
20
21/// HMAC signature.
22#[derive(Debug)]
23pub struct Signature<T>(HashInner, PhantomData<T>);
24
25#[derive(Debug)]
26enum HashInner {
27	Sha256(GenericArray<u8, U32>),
28	Sha512(GenericArray<u8, U64>),
29}
30
31impl<T> Deref for Signature<T> {
32	type Target = [u8];
33
34	fn deref(&self) -> &Self::Target {
35		match &self.0 {
36			HashInner::Sha256(a) => a.as_slice(),
37			HashInner::Sha512(a) => a.as_slice(),
38		}
39	}
40}
41
42/// HMAC signing key.
43pub struct SigKey<T>(KeyInner, PhantomData<T>);
44
45#[derive(PartialEq)]
46// Using `Box[u8]` guarantees no reallocation can happen
47struct DisposableBox(Box<[u8]>);
48
49impl std::fmt::Debug for DisposableBox {
50	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51		write!(f, "{:?}", &self.0.as_ref())
52	}
53}
54
55impl DisposableBox {
56	fn from_slice(data: &[u8]) -> Self {
57		Self(data.to_vec().into_boxed_slice())
58	}
59}
60
61impl Drop for DisposableBox {
62	fn drop(&mut self) {
63		self.0.zeroize()
64	}
65}
66
67#[derive(Debug, PartialEq)]
68enum KeyInner {
69	Sha256(DisposableBox),
70	Sha512(DisposableBox),
71}
72
73impl SigKey<Sha256> {
74	pub fn sha256(key: &[u8]) -> SigKey<Sha256> {
75		SigKey(KeyInner::Sha256(DisposableBox::from_slice(key)), PhantomData)
76	}
77}
78
79impl SigKey<Sha512> {
80	pub fn sha512(key: &[u8]) -> SigKey<Sha512> {
81		SigKey(KeyInner::Sha512(DisposableBox::from_slice(key)), PhantomData)
82	}
83}
84
85/// Compute HMAC signature of `data`.
86pub fn sign<T>(k: &SigKey<T>, data: &[u8]) -> Signature<T> {
87	let mut signer = Signer::with(k);
88	signer.update(data);
89	signer.sign()
90}
91
92/// Stateful HMAC computation.
93pub struct Signer<T>(SignerInner, PhantomData<T>);
94
95enum SignerInner {
96	Sha256(Hmac<sha2::Sha256>),
97	Sha512(Hmac<sha2::Sha512>),
98}
99
100impl<T> Signer<T> {
101	pub fn with(key: &SigKey<T>) -> Signer<T> {
102		match &key.0 {
103			KeyInner::Sha256(key_bytes) => Signer(
104				SignerInner::Sha256(Hmac::<sha2::Sha256>::new_varkey(&key_bytes.0).expect("always returns Ok; qed")),
105				PhantomData,
106			),
107			KeyInner::Sha512(key_bytes) => Signer(
108				SignerInner::Sha512(Hmac::<sha2::Sha512>::new_varkey(&key_bytes.0).expect("always returns Ok; qed")),
109				PhantomData,
110			),
111		}
112	}
113
114	pub fn update(&mut self, data: &[u8]) {
115		match &mut self.0 {
116			SignerInner::Sha256(hmac) => hmac.update(data),
117			SignerInner::Sha512(hmac) => hmac.update(data),
118		}
119	}
120
121	pub fn sign(self) -> Signature<T> {
122		match self.0 {
123			SignerInner::Sha256(hmac) => Signature(HashInner::Sha256(hmac.finalize().into_bytes()), PhantomData),
124			SignerInner::Sha512(hmac) => Signature(HashInner::Sha512(hmac.finalize().into_bytes()), PhantomData),
125		}
126	}
127}
128
129/// HMAC signature verification key.
130pub struct VerifyKey<T>(KeyInner, PhantomData<T>);
131
132impl VerifyKey<Sha256> {
133	pub fn sha256(key: &[u8]) -> VerifyKey<Sha256> {
134		VerifyKey(KeyInner::Sha256(DisposableBox::from_slice(key)), PhantomData)
135	}
136}
137
138impl VerifyKey<Sha512> {
139	pub fn sha512(key: &[u8]) -> VerifyKey<Sha512> {
140		VerifyKey(KeyInner::Sha512(DisposableBox::from_slice(key)), PhantomData)
141	}
142}
143
144/// Verify HMAC signature of `data`.
145pub fn verify<T>(key: &VerifyKey<T>, data: &[u8], sig: &[u8]) -> bool {
146	match &key.0 {
147		KeyInner::Sha256(key_bytes) => {
148			let mut ctx = Hmac::<sha2::Sha256>::new_varkey(&key_bytes.0).expect("always returns Ok; qed");
149			ctx.update(data);
150			ctx.verify(sig).is_ok()
151		}
152		KeyInner::Sha512(key_bytes) => {
153			let mut ctx = Hmac::<sha2::Sha512>::new_varkey(&key_bytes.0).expect("always returns Ok; qed");
154			ctx.update(data);
155			ctx.verify(sig).is_ok()
156		}
157	}
158}
159
160#[cfg(test)]
161mod test;