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
// MIT License

// Copyright (c) 2018-2019 The orion Developers

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//! Message authentication.
//!
//! # Use case:
//! `orion::auth` can be used to ensure message integrity and authenticity by
//! using a secret key.
//!
//! An example of this could be securing API's by having a user of a given API
//! sign their API request and having the API server verify these signed API
//! requests.
//!
//! # About:
//! - Uses HMAC-SHA512.
//!
//! # Parameters:
//! - `secret_key`: Secret key used to authenticate `data`.
//! - `data`: Data to be authenticated.
//! - `expected`: The expected authentication tag.
//!
//! # Exceptions:
//! An exception will be thrown if:
//! - The calculated `Tag` does not match the expected.
//! - The `OsRng` fails to initialize or read from its source.
//!
//! # Security:
//! - The secret key should always be generated using a CSPRNG.
//!   `SecretKey::default()` can be used for
//! this, it will generate a `SecretKey` of 32 bytes.
//! - The recommended minimum length for a `SecretKey` is 32.
//!
//! # Example:
//! ```
//! use orion::auth;
//!
//! let key = auth::SecretKey::default();
//! let msg = "Some message.".as_bytes();
//!
//! let expected_tag = auth::authenticate(&key, msg).unwrap();
//! assert!(auth::authenticate_verify(&expected_tag, &key, &msg).unwrap());
//! ```

use crate::{
	errors::{UnknownCryptoError, ValidationCryptoError},
	hazardous::mac::hmac,
};
pub use crate::{hazardous::mac::hmac::Tag, hltypes::SecretKey};

#[must_use]
/// Authenticate a message using HMAC-SHA512.
pub fn authenticate(secret_key: &SecretKey, data: &[u8]) -> Result<Tag, UnknownCryptoError> {
	let mut state = hmac::init(&hmac::SecretKey::from_slice(
		&secret_key.unprotected_as_bytes(),
	)?);
	state.update(data)?;

	Ok(state.finalize()?)
}

#[must_use]
/// Authenticate and verify a message using HMAC-SHA512.
pub fn authenticate_verify(
	expected: &Tag,
	secret_key: &SecretKey,
	data: &[u8],
) -> Result<bool, ValidationCryptoError> {
	let v_key = &hmac::SecretKey::from_slice(&secret_key.unprotected_as_bytes())?;

	hmac::verify(&expected, &v_key, &data)?;

	Ok(true)
}

#[test]
fn test_authenticate_verify_bad_key() {
	let sec_key_correct = SecretKey::generate(64).unwrap();
	let sec_key_false = SecretKey::default();
	let msg = "what do ya want for nothing?".as_bytes().to_vec();

	let hmac_bob = authenticate(&sec_key_correct, &msg).unwrap();

	assert_eq!(
		authenticate_verify(&hmac_bob, &sec_key_correct, &msg).unwrap(),
		true
	);
	assert!(authenticate_verify(&hmac_bob, &sec_key_false, &msg).is_err());
}

#[test]
fn test_authenticate_verify_bad_msg() {
	let sec_key = SecretKey::generate(64).unwrap();
	let msg = "what do ya want for nothing?".as_bytes().to_vec();

	let hmac_bob = authenticate(&sec_key, &msg).unwrap();

	assert_eq!(
		authenticate_verify(&hmac_bob, &sec_key, &msg).unwrap(),
		true
	);
	assert!(authenticate_verify(&hmac_bob, &sec_key, b"bad msg").is_err());
}