rust_ev_crypto_primitives 0.8.4

Crypto Primitives necessary for E-Voting Applications.
Documentation
// Copyright © 2023 Denis Morel

// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License and
// a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.

//! Impement necessary Hash algorithms for the crate

use std::io::BufRead;

use super::{BasisCryptoError, BasisCryptoErrorRepr};
use crate::byte_array::ByteArray;
use openssl::{
    error::ErrorStack,
    hash::{hash_xof, MessageDigest},
    md::Md,
    md_ctx::MdCtx,
};
use thiserror::Error;

#[derive(Error, Debug)]
pub(super) enum InternalStepError {
    #[error("Error creating MdCtx")]
    NewMdCtx { source: ErrorStack },
    #[error("Error initializeing the Digest")]
    InitDigest { source: ErrorStack },
    #[error("Error updating the Digest")]
    UpdateDigest { source: ErrorStack },
    #[error("Error finalizing the Digest")]
    FinalDigest { source: ErrorStack },
    #[error("Error reading the stream buffer")]
    StreamIOError { source: std::io::Error },
    #[error("Error in hash_xof")]
    HashXOF { source: ErrorStack },
}

#[derive(Error, Debug)]
pub(super) enum HashError {
    #[error("Error in sha3_256")]
    SHA3256 { source: InternalStepError },
    #[error("Error in sha_256")]
    SHA256 { source: InternalStepError },
    #[error("Error in shake128")]
    SHAKE128 { source: InternalStepError },
}

/// Wrapper for SHA3-256
///
/// # Error
/// [BasisCryptoError] if something is going wrong
pub fn sha3_256(byte_array: &ByteArray) -> Result<ByteArray, BasisCryptoError> {
    sha3_256_repr(byte_array)
        .map_err(|e| HashError::SHA3256 { source: e })
        .map_err(BasisCryptoErrorRepr::from)
        .map_err(BasisCryptoError::from)
}

fn sha3_256_repr(byte_array: &ByteArray) -> Result<ByteArray, InternalStepError> {
    let mut ctx = MdCtx::new().map_err(|e| InternalStepError::NewMdCtx { source: e })?;
    ctx.digest_init(Md::sha3_256())
        .map_err(|e| InternalStepError::InitDigest { source: e })?;
    ctx.digest_update(byte_array.to_bytes())
        .map_err(|e| InternalStepError::UpdateDigest { source: e })?;
    let mut digest = [0; 32];
    ctx.digest_final(&mut digest)
        .map_err(|e| InternalStepError::FinalDigest { source: e })?;
    Ok(ByteArray::from_bytes(&digest))
}

/// Wrapper for SHA256
///
/// # Error
/// [BasisCryptoError] if something is going wrong
pub fn sha256(byte_array: &ByteArray) -> Result<ByteArray, BasisCryptoError> {
    sha256_stream(&mut byte_array.to_bytes())
}

/// Wrapper for SHA256 for a stream reader
///
/// # Error
/// [BasisCryptoError] if something is going wrong
pub fn sha256_stream(reader: &mut dyn BufRead) -> Result<ByteArray, BasisCryptoError> {
    sha256_stream_repr(reader)
        .map_err(|e| HashError::SHA256 { source: e })
        .map_err(BasisCryptoErrorRepr::from)
        .map_err(BasisCryptoError::from)
}

fn sha256_stream_repr(reader: &mut dyn BufRead) -> Result<ByteArray, InternalStepError> {
    let mut ctx = MdCtx::new().map_err(|e| InternalStepError::NewMdCtx { source: e })?;
    ctx.digest_init(Md::sha256())
        .map_err(|e| InternalStepError::InitDigest { source: e })?;
    loop {
        let mut buf = vec![0; 2048];
        let n = reader
            .read(&mut buf)
            .map_err(|e| InternalStepError::StreamIOError { source: e })?;
        if n == 0 {
            break;
        }
        buf.truncate(n);
        ctx.digest_update(&buf)
            .map_err(|e| InternalStepError::UpdateDigest { source: e })?;
    }
    let mut digest = [0; 32];
    ctx.digest_final(&mut digest)
        .map_err(|e| InternalStepError::FinalDigest { source: e })?;
    Ok(ByteArray::from_bytes(&digest))
}

/// Wrapper for SHAKE128
///
/// # Error
/// [BasisCryptoError] if something is going wrong
pub fn shake128(byte_array: &ByteArray, length: usize) -> Result<ByteArray, BasisCryptoError> {
    let mut digest = vec![0; length];
    hash_xof(
        MessageDigest::shake_128(),
        byte_array.to_bytes(),
        digest.as_mut_slice(),
    )
    .map_err(|e| InternalStepError::HashXOF { source: e })
    .map_err(|e| HashError::SHAKE128 { source: e })
    .map_err(BasisCryptoErrorRepr::from)
    .map_err(BasisCryptoError::from)?;
    Ok(ByteArray::from_bytes(&digest))
}

/// Wrapper for SHAKE256
///
/// # Error
/// [BasisCryptoError] if something is going wrong
pub fn shake256(byte_array: &ByteArray, length: usize) -> Result<ByteArray, BasisCryptoError> {
    let mut digest = vec![0; length];
    hash_xof(
        MessageDigest::shake_256(),
        byte_array.to_bytes(),
        digest.as_mut_slice(),
    )
    .map_err(|e| InternalStepError::HashXOF { source: e })
    .map_err(|e| HashError::SHAKE128 { source: e })
    .map_err(BasisCryptoErrorRepr::from)
    .map_err(BasisCryptoError::from)?;
    Ok(ByteArray::from_bytes(&digest))
}

#[cfg(test)]
mod test {
    use super::super::super::byte_array::DecodeTrait;
    use super::*;
    use crate::{EncodeTrait, GROUP_PARAMETER_P_LENGTH};
    use std::io::BufReader;
    use std::path::Path;

    #[test]
    fn test_sha3_256() {
        let e: Vec<u8> = vec![
            28u8, 158u8, 189u8, 108u8, 175u8, 2u8, 132u8, 10u8, 91u8, 43u8, 127u8, 15u8, 200u8,
            112u8, 236u8, 29u8, 177u8, 84u8, 136u8, 106u8, 233u8, 254u8, 98u8, 27u8, 130u8, 43u8,
            20u8, 253u8, 11u8, 245u8, 19u8, 214u8,
        ];
        assert_eq!(
            sha3_256(&ByteArray::from_bytes(b"\x41"))
                .unwrap()
                .to_bytes(),
            e
        );
    }

    #[test]
    fn test_sha256() {
        assert_eq!(
            sha256(&ByteArray::from_bytes(b"Some Crypto Text"))
                .unwrap()
                .to_bytes(),
            b"\x60\x78\x56\x38\x8a\xca\x5c\x51\x83\xc4\xd1\x4d\xc8\xf9\xcc\xf2\
            \xa5\x21\xb3\x10\x93\x72\xfa\xd6\x7c\x55\xf5\xc9\xe3\xd1\x83\x19"
        );
    }

    #[test]
    fn test_shake128() {
        assert_eq!(
            shake128(&ByteArray::from_bytes(b"Some Crypto Text"), GROUP_PARAMETER_P_LENGTH / 8).unwrap(),
            ByteArray::base16_decode(
                "76237D07F362A5A115FFA4830C75DD2AAEC818FB7236A35FF1643300FBA19D3EDF90490E8B05B26926E8A5FF2F2A4E16526CFA48B11B4A0FDBD8AA1AA124F578671ACEC8B51D154F1DFDE424D51EC26A8F8DFD2253550F3421E18E967509D1C26FCAB13410093C7EAF57B4C2CFD9505E99714BCA3D558B1E9DF23DEE0623854378E1B1077904D77192482DA475210EB023432FF9154D6F655020DE494AD8B3732BA89606C1E54E8260F49C4E6DB7B4408568192F72E08883C016681D9869F622393D824EE579BD70BF1C8B50ED90A1B6091A5E6A0E9C6DCBC57C6005BCED5F9E01FCEA3B4D96FE24475FF73A85A451DE673875D262D76CCFE583503F509EF957B25B2CD5FACA49D1C813AF6854FDC7B1A27A6645964975B37B6DF4D2441F8832361F2DADD48FE8C47D57D4F33AF1C85977ABE2A58D8CEB4366BD46801DC14837005A6496D4BDE2A154EA99BE6F4453501CF827E5108D067C8BFBDF8FB7BDC1D221DB11B34BD47AFC88B4D1DBEA7E967111631E22E40AF45735E1E789DBB7178F"
            )
            .unwrap()
        );
    }

    #[test]
    fn test_shake256() {
        assert_eq!(
            shake256(&ByteArray::from_bytes(b"Some Crypto Text"), GROUP_PARAMETER_P_LENGTH / 8).unwrap(),
            ByteArray::base16_decode(
                "36D790E2881A710CE583CD643E9AAFF6FCEF86BAD49B849656C7060871AA8A837A9191B0BDE0F7EE3B57787BF5CAF0B4387C7ACAD063F409CB1CD4E4B2E0163E35DCCDCE60141B486395C672C7DE808D4B98EA84083F655F76CD23C5CDC6CA9FB1F5099FF3F2BB0753B7EBB8C0D576F5299402AE752D420BB8B8CF6B24094008349C7992FDCCB81DFA3292AB9167D7EB9E79440EA82AE25521746F2720B794E6B528CDFA1437CA289AD0753570B4BD38159F50FD1ACE554C97D065EB055D08FF85F330240B7B21D62C315C995E34D42D7570D4F01D4FF93F7DC665664130A6F001E36F47374B1C40ABE7010C4B14BEEB1FEAF41BFEEC0DCE87FFAEA758C53F7F7C0BCB590621AF4A393845BDBA734296D920B8F71D02A7A4CA2666EC7890034C2DF7D0F64D27E1A3E281058DE11F2859A270988EC60C56BD8AF81ED290ADF8F4B02B3C4FF095AFAAEC4BC8B942845CE9566A4B9C564E11A0888FF56DBC254CD6E86DE958A6346D1A8DD6025165A64E4F8596C2562CB7071078A47DC2DA9285F0"
            )
            .unwrap()
        );
    }

    #[test]
    fn test_sha256_stream() {
        let path = Path::new("./")
            .join("test_data")
            .join("test_sha256_stream.zip");
        let f = std::fs::File::open(path).unwrap();
        let mut reader = BufReader::new(f);
        let sha = sha256_stream(&mut reader).unwrap();
        assert_eq!(
            sha.base16_encode().unwrap(),
            "07CC28A8D8604FF9CD34EC0E5067E970087EF3CAC07A2247B88160F5A0AF2D36"
        )
    }
}