helium-crypto 0.10.0

Helium Blockchain cryptography library
use byteorder::{LittleEndian, ReadBytesExt};
use rsa::{BigUint, RSAPublicKey};
use std::{
    convert::TryFrom,
    io::{Cursor, Read},
};

use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Key blob length is to small: {0}")]
    KeyBlobLength(usize),

    #[error("Bad digest padding value: {0}")]
    DigestPadding(u32),

    #[error("Modulus read failed")]
    Modulus,

    #[error("Public exponent read failed")]
    PublicExponent,

    #[error("Private exponent read failed")]
    PrivateExponent,

    #[error("Failed to create RSAPublicKey")]
    RSAPublicKey(#[from] rsa::errors::Error),
}

pub enum DigestPadAlgo {
    RsaDigestPaddingNone = 0,
    RsaPkcs115Sha2_256 = 1,
    RsaPssSha2_256 = 2,
}

impl TryFrom<u32> for DigestPadAlgo {
    type Error = Error;

    fn try_from(value: u32) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(DigestPadAlgo::RsaDigestPaddingNone),
            1 => Ok(DigestPadAlgo::RsaPkcs115Sha2_256),
            2 => Ok(DigestPadAlgo::RsaPssSha2_256),
            _ => Err(Error::DigestPadding(value)),
        }
    }
}

// Note (iegor.s): values are copied from qualcomm qseecom module
const RSA_KEY_SIZE_MAX: usize = 512 + 16;
const RSA_IV_LENGTH: usize = 16;
const RSA_HMAC_LENGTH: usize = 32;
const RSA_KEY_BLOB_LENGHT: usize = 1656;

pub fn parse_key_blob(buf: &[u8]) -> Result<RSAPublicKey, Error> {
    // Key blobs are always exactly RSA_KEY_BLOB_LENGHT long, but
    // let's tolerate extra.
    if buf.len() < RSA_KEY_BLOB_LENGHT {
        return Err(Error::KeyBlobLength(buf.len()));
    }

    let mut data = Cursor::new(buf);
    let _ = data
        .read_u32::<LittleEndian>()
        .expect("expected magic number");
    let _ = data
        .read_u32::<LittleEndian>()
        .expect("expected version number");
    let digest_padding = data
        .read_u32::<LittleEndian>()
        .expect("expected digest padding");
    let _ = DigestPadAlgo::try_from(digest_padding)?;
    let mut modulus = vec![0u8; RSA_KEY_SIZE_MAX];
    data.read_exact(modulus.as_mut_slice())
        .expect("expected modulus");
    let modulus_len = data
        .read_u32::<LittleEndian>()
        .expect("expected modulus length");
    if modulus_len > RSA_KEY_SIZE_MAX as u32 {
        return Err(Error::Modulus);
    }
    modulus.truncate(modulus_len as usize);
    let mut public_exponent = vec![0u8; RSA_KEY_SIZE_MAX];
    data.read_exact(public_exponent.as_mut_slice())
        .expect("expected public exponent");
    let public_exponent_len = data
        .read_u32::<LittleEndian>()
        .expect("expected public exponent length");
    if public_exponent_len > RSA_KEY_SIZE_MAX as u32 {
        return Err(Error::PublicExponent);
    }
    public_exponent.truncate(public_exponent_len as usize);
    let mut iv = vec![0u8; RSA_IV_LENGTH];
    data.read_exact(iv.as_mut_slice())
        .expect("expected initial vector");
    let mut private_exponent = vec![0u8; RSA_KEY_SIZE_MAX];
    data.read_exact(private_exponent.as_mut_slice())
        .expect("expected private exponent");
    let private_exponent_len = data
        .read_u32::<LittleEndian>()
        .expect("expected private exponent length");
    if private_exponent_len > RSA_KEY_SIZE_MAX as u32 {
        return Err(Error::PrivateExponent);
    }
    private_exponent.truncate(private_exponent_len as usize);
    let mut hmac_length = vec![0u8; RSA_HMAC_LENGTH];
    data.read_exact(hmac_length.as_mut_slice())
        .expect("expected hmac length");

    RSAPublicKey::new(
        BigUint::from_bytes_be(modulus.as_slice()),
        BigUint::from_bytes_be(public_exponent.as_slice()),
    )
    .map_err(Error::RSAPublicKey)
}

#[cfg(test)]
mod tests {
    use hex_literal::hex;

    #[test]
    fn verify_parse() {
        const BLOB: &[u8] =
            &hex!("424B4D4B0000000001000000C12D21F0297EC0E5AE3EB8FC01B0AE5B0723AEBECD6E9DEB9EC3996D6EEA504DC39DE85A58D74B8A3554F0A52AF1F7F7888F806E9CD1C610FB51BB45A7BC3795C5AD9AC439A706A0DFF0B54A5A3A3CEBF8387EC7485849308A954A55A50EEA14D0A71AA046C38D0E7925F5499F5CDFB798B47889FE215690C6C41F7FD0521579C25D258206D2B93D83F4AD307F14D13D77CC36D759BD63A1DF33AADC4AF73062675BC6E63127722C6D60C2C11226D1CBAFFF4E7B5C58460FF58B50A20B78D59BDB270F867CA157A67E388F5FBB8991EFE937EB1C29043D5A10CE7FB5481AFCBC7E5B07B92470C69B9F97446B9F2268A52FBA033EE3615B08B9476D9C2FB210C9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000008B9C0BB28187EF4F195FD1F060FBE8EE3AE16C2FF6D3EFE52BB2A68814E1150F40348B7F13B02097068356E6FC661EC3D1208EA51D7EF3424F2324B71A76179F107976680752EA5050042F778A678C5C20E8250FF872CBBF9D804EA95CEDB7A95DF633B854E908568863C45987F0D95C818D21AB02BFE72F66C74599BB28073D78DF92A24004A95481FCBA4BF82017DD67294BB6922916D1DD73FF73D5928DC065E75C3C25B0E775D517829B5A1914312ADD7BB52CD43CABF74DC0A8696C83C96746F177872E6CA3687CAC1A877E115BAB421FC6DF13B627F8DB57957ADEBE4218E9D36C672C5F895CCD2CC8F558CFCF07D8B2D7AC23DC21A31FDC2E77B02CD1FA8AB3002E30F03F8E3C2FEB227B157D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100002CE90652D0E86E40FAFCDC1D6CD40DC058659F612DF74195CDA26DCC21655281");
        super::parse_key_blob(BLOB).expect("pre-generated keyblob should parse");
    }
}