refactor(router): make routes public and move crypto module to common utils (#176)

Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
ItsMeShashank
2022-12-19 19:34:24 +05:30
committed by GitHub
parent 9a4b1d023e
commit bf322c9535
8 changed files with 55 additions and 26 deletions

View File

@ -0,0 +1,353 @@
//! Utilities for cryptographic algorithms
use error_stack::{IntoReport, ResultExt};
use ring::{aead, hmac};
use crate::errors::{self, CustomResult};
const RING_ERR_UNSPECIFIED: &str = "ring::error::Unspecified";
/// Trait for cryptographically signing messages
pub trait SignMessage {
/// Takes in a secret and a message and returns the calculated signature as bytes
fn sign_message(
&self,
_secret: &[u8],
_msg: &[u8],
) -> CustomResult<Vec<u8>, errors::CryptoError>;
}
/// Trait for cryptographically verifying a message against a signature
pub trait VerifySignature {
/// Takes in a secret, the signature and the message and verifies the message
/// against the signature
fn verify_signature(
&self,
_secret: &[u8],
_signature: &[u8],
_msg: &[u8],
) -> CustomResult<bool, errors::CryptoError>;
}
/// Trait for cryptographically encoding a message
pub trait EncodeMessage {
/// Takes in a secret and the message and encodes it, returning bytes
fn encode_message(
&self,
_secret: &[u8],
_msg: &[u8],
) -> CustomResult<(Vec<u8>, Vec<u8>), errors::CryptoError>;
}
/// Trait for cryptographically decoding a message
pub trait DecodeMessage {
/// Takes in a secret, an encoded messages and attempts to decode it, returning bytes
fn decode_message(
&self,
_secret: &[u8],
_msg: &[u8],
) -> CustomResult<Vec<u8>, errors::CryptoError>;
}
/// Represents no cryptographic algorithm.
/// Implements all crypto traits and acts like a Nop
#[derive(Debug)]
pub struct NoAlgorithm;
impl SignMessage for NoAlgorithm {
fn sign_message(
&self,
_secret: &[u8],
_msg: &[u8],
) -> CustomResult<Vec<u8>, errors::CryptoError> {
Ok(Vec::new())
}
}
impl VerifySignature for NoAlgorithm {
fn verify_signature(
&self,
_secret: &[u8],
_signature: &[u8],
_msg: &[u8],
) -> CustomResult<bool, errors::CryptoError> {
Ok(true)
}
}
impl EncodeMessage for NoAlgorithm {
fn encode_message(
&self,
_secret: &[u8],
msg: &[u8],
) -> CustomResult<(Vec<u8>, Vec<u8>), errors::CryptoError> {
Ok((msg.to_vec(), Vec::new()))
}
}
impl DecodeMessage for NoAlgorithm {
fn decode_message(
&self,
_secret: &[u8],
msg: &[u8],
) -> CustomResult<Vec<u8>, errors::CryptoError> {
Ok(msg.to_vec())
}
}
/// Represents the HMAC-SHA-256 algorithm
#[derive(Debug)]
pub struct HmacSha256;
impl SignMessage for HmacSha256 {
fn sign_message(
&self,
secret: &[u8],
msg: &[u8],
) -> CustomResult<Vec<u8>, errors::CryptoError> {
let key = hmac::Key::new(hmac::HMAC_SHA256, secret);
Ok(hmac::sign(&key, msg).as_ref().to_vec())
}
}
impl VerifySignature for HmacSha256 {
fn verify_signature(
&self,
secret: &[u8],
signature: &[u8],
msg: &[u8],
) -> CustomResult<bool, errors::CryptoError> {
let key = hmac::Key::new(hmac::HMAC_SHA256, secret);
Ok(hmac::verify(&key, msg, signature).is_ok())
}
}
/// Represents the HMAC-SHA-512 algorithm
#[derive(Debug)]
pub struct HmacSha512;
impl SignMessage for HmacSha512 {
fn sign_message(
&self,
secret: &[u8],
msg: &[u8],
) -> CustomResult<Vec<u8>, errors::CryptoError> {
let key = hmac::Key::new(hmac::HMAC_SHA512, secret);
Ok(hmac::sign(&key, msg).as_ref().to_vec())
}
}
impl VerifySignature for HmacSha512 {
fn verify_signature(
&self,
secret: &[u8],
signature: &[u8],
msg: &[u8],
) -> CustomResult<bool, errors::CryptoError> {
let key = hmac::Key::new(hmac::HMAC_SHA512, secret);
Ok(hmac::verify(&key, msg, signature).is_ok())
}
}
/// Represents the GCM-AES-256 algorithm
#[derive(Debug)]
pub struct GcmAes256 {
nonce: Vec<u8>,
}
impl EncodeMessage for GcmAes256 {
fn encode_message(
&self,
secret: &[u8],
msg: &[u8],
) -> CustomResult<(Vec<u8>, Vec<u8>), errors::CryptoError> {
let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, secret)
.map_err(|_| errors::CryptoError::EncodingFailed)
.into_report()
.attach_printable(RING_ERR_UNSPECIFIED)?;
let nonce = aead::Nonce::try_assume_unique_for_key(&self.nonce)
.map_err(|_| errors::CryptoError::EncodingFailed)
.into_report()
.attach_printable(RING_ERR_UNSPECIFIED)?;
let sealing_key = aead::LessSafeKey::new(unbound_key);
let mut mutable_msg = msg.to_vec();
let tag = sealing_key
.seal_in_place_separate_tag(nonce, aead::Aad::empty(), &mut mutable_msg)
.map_err(|_| errors::CryptoError::EncodingFailed)
.into_report()
.attach_printable(RING_ERR_UNSPECIFIED)?;
Ok((mutable_msg, tag.as_ref().to_vec()))
}
}
impl DecodeMessage for GcmAes256 {
fn decode_message(
&self,
secret: &[u8],
msg: &[u8],
) -> CustomResult<Vec<u8>, errors::CryptoError> {
let unbound_key = aead::UnboundKey::new(&aead::AES_256_GCM, secret)
.map_err(|_| errors::CryptoError::DecodingFailed)
.into_report()
.attach_printable(RING_ERR_UNSPECIFIED)?;
let nonce = aead::Nonce::try_assume_unique_for_key(&self.nonce)
.map_err(|_| errors::CryptoError::DecodingFailed)
.into_report()
.attach_printable(RING_ERR_UNSPECIFIED)?;
let opening_key = aead::LessSafeKey::new(unbound_key);
let mut mutable_msg = msg.to_vec();
let output = opening_key
.open_in_place(nonce, aead::Aad::empty(), &mut mutable_msg)
.map_err(|_| errors::CryptoError::DecodingFailed)
.into_report()
.attach_printable(RING_ERR_UNSPECIFIED)?;
Ok(output.to_vec())
}
}
#[cfg(test)]
mod crypto_tests {
#![allow(clippy::expect_used)]
use super::{DecodeMessage, EncodeMessage, SignMessage, VerifySignature};
#[test]
fn test_hmac_sha256_sign_message() {
let message = r#"{"type":"payment_intent"}"#.as_bytes();
let secret = "hmac_secret_1234".as_bytes();
let right_signature =
hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823e")
.expect("Right signature decoding");
let signature = super::HmacSha256
.sign_message(secret, message)
.expect("Signature");
assert_eq!(signature, right_signature);
}
#[test]
fn test_hmac_sha256_verify_signature() {
let right_signature =
hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823e")
.expect("Right signature decoding");
let wrong_signature =
hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823f")
.expect("Wrong signature decoding");
let secret = "hmac_secret_1234".as_bytes();
let data = r#"{"type":"payment_intent"}"#.as_bytes();
let right_verified = super::HmacSha256
.verify_signature(secret, &right_signature, data)
.expect("Right signature verification result");
assert!(right_verified);
let wrong_verified = super::HmacSha256
.verify_signature(secret, &wrong_signature, data)
.expect("Wrong signature verification result");
assert!(!wrong_verified);
}
#[test]
fn test_hmac_sha512_sign_message() {
let message = r#"{"type":"payment_intent"}"#.as_bytes();
let secret = "hmac_secret_1234".as_bytes();
let right_signature = hex::decode("38b0bc1ea66b14793e39cd58e93d37b799a507442d0dd8d37443fa95dec58e57da6db4742636fea31201c48e57a66e73a308a2e5a5c6bb831e4e39fe2227c00f")
.expect("signature decoding");
let signature = super::HmacSha512
.sign_message(secret, message)
.expect("Signature");
assert_eq!(signature, right_signature);
}
#[test]
fn test_hmac_sha512_verify_signature() {
let right_signature = hex::decode("38b0bc1ea66b14793e39cd58e93d37b799a507442d0dd8d37443fa95dec58e57da6db4742636fea31201c48e57a66e73a308a2e5a5c6bb831e4e39fe2227c00f")
.expect("signature decoding");
let wrong_signature =
hex::decode("d5550730377011948f12cc28889bee590d2a5434d6f54b87562f2dbc2657823f")
.expect("Wrong signature decoding");
let secret = "hmac_secret_1234".as_bytes();
let data = r#"{"type":"payment_intent"}"#.as_bytes();
let right_verified = super::HmacSha512
.verify_signature(secret, &right_signature, data)
.expect("Right signature verification result");
assert!(right_verified);
let wrong_verified = super::HmacSha256
.verify_signature(secret, &wrong_signature, data)
.expect("Wrong signature verification result");
assert!(!wrong_verified);
}
#[test]
fn test_gcm_aes_256_encode_message() {
let message = r#"{"type":"PAYMENT"}"#.as_bytes();
let secret =
hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")
.expect("Secret decoding");
let nonce = hex::decode("000000000000000000000000").expect("Nonce hex decoding");
let actual_encoded_message =
hex::decode("0A3471C72D9BE49A8520F79C66BBD9A12FF9").expect("Message decoding");
let actual_auth_tag =
hex::decode("CE573FB7A41AB78E743180DC83FF09BD").expect("Auth tag decoding");
let algorithm = super::GcmAes256 {
nonce: nonce.to_vec(),
};
let (encoded_message, auth_tag) = algorithm
.encode_message(&secret, message)
.expect("Encoded message and tag");
assert_eq!(encoded_message, actual_encoded_message);
assert_eq!(auth_tag, actual_auth_tag);
}
#[test]
fn test_gcm_aes_256_decode_message() {
let right_secret =
hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")
.expect("Secret decoding");
let wrong_secret =
hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0e")
.expect("Secret decoding");
let nonce = hex::decode("000000000000000000000000").expect("Nonce hex decoding");
let mut auth_tag =
hex::decode("CE573FB7A41AB78E743180DC83FF09BD").expect("Auth tag decoding");
let mut message =
hex::decode("0A3471C72D9BE49A8520F79C66BBD9A12FF9").expect("Message decoding");
message.append(&mut auth_tag);
let algorithm = super::GcmAes256 {
nonce: nonce.to_vec(),
};
let decoded = algorithm
.decode_message(&right_secret, &message)
.expect("Decoded message");
assert_eq!(decoded, r#"{"type":"PAYMENT"}"#.as_bytes());
let err_decoded = algorithm.decode_message(&wrong_secret, &message);
assert!(err_decoded.is_err());
}
}

View File

@ -54,3 +54,20 @@ pub enum ValidationError {
#[error("{message}")]
InvalidValue { message: String },
}
/// Cryptograpic algorithm errors
#[derive(Debug, thiserror::Error)]
pub enum CryptoError {
/// The cryptographic algorithm was unable to encode the message
#[error("Failed to encode given message")]
EncodingFailed,
/// The cryptographic algorithm was unable to decode the message
#[error("Failed to decode given message")]
DecodingFailed,
/// The cryptographic algorithm was unable to sign the message
#[error("Failed to sign message")]
MessageSigningFailed,
/// The cryptographic algorithm was unable to verify the given signature
#[error("Failed to verify signature")]
SignatureVerificationFailed,
}

View File

@ -15,6 +15,7 @@
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR" ), "/", "README.md"))]
pub mod consts;
pub mod crypto;
pub mod custom_serde;
pub mod errors;
pub mod ext_traits;