gcrypt 0.5.3

Libgcrypt bindings for Rust
Documentation
use std::ffi::{CStr, CString};
use std::io::{self, Write};
use std::ptr;
use std::result;
use std::slice;
use std::str::Utf8Error;

use ffi;
use libc::c_int;

use {NonZero, Result};

ffi_enum_wrapper! {
    #[allow(non_camel_case_types)]
    pub enum Algorithm: c_int {
        Md5           = ffi::GCRY_MD_MD5,
        Sha1          = ffi::GCRY_MD_SHA1,
        Rmd160        = ffi::GCRY_MD_RMD160,
        Md2           = ffi::GCRY_MD_MD2,
        Tiger         = ffi::GCRY_MD_TIGER,
        Haval         = ffi::GCRY_MD_HAVAL,
        Sha256        = ffi::GCRY_MD_SHA256,
        Sha384        = ffi::GCRY_MD_SHA384,
        Sha512        = ffi::GCRY_MD_SHA512,
        Sha224        = ffi::GCRY_MD_SHA224,
        Md4           = ffi::GCRY_MD_MD4,
        Crc32         = ffi::GCRY_MD_CRC32,
        Crc32Rfc1510  = ffi::GCRY_MD_CRC32_RFC1510,
        Crc24Rfc2440  = ffi::GCRY_MD_CRC24_RFC2440,
        Whirlpool     = ffi::GCRY_MD_WHIRLPOOL,
        Tiger1        = ffi::GCRY_MD_TIGER1,
        Tiger2        = ffi::GCRY_MD_TIGER2,
        GostR3411_94  = ffi::GCRY_MD_GOSTR3411_94,
        Stribog256    = ffi::GCRY_MD_STRIBOG256,
        Stribog512    = ffi::GCRY_MD_STRIBOG512,
        Gostr3411Cp   = ffi::GCRY_MD_GOSTR3411_CP,
        Sha3_224      = ffi::GCRY_MD_SHA3_224,
        Sha3_256      = ffi::GCRY_MD_SHA3_256,
        Sha3_384      = ffi::GCRY_MD_SHA3_384,
        Sha3_512      = ffi::GCRY_MD_SHA3_512,
        Shake128      = ffi::GCRY_MD_SHAKE128,
        Shake256      = ffi::GCRY_MD_SHAKE256,
        Blake2B512    = ffi::GCRY_MD_BLAKE2B_512,
        Blake2B384    = ffi::GCRY_MD_BLAKE2B_384,
        Blake2B256    = ffi::GCRY_MD_BLAKE2B_256,
        Blake2B160    = ffi::GCRY_MD_BLAKE2B_160,
        Blake2S256    = ffi::GCRY_MD_BLAKE2S_256,
        Blake2S224    = ffi::GCRY_MD_BLAKE2S_224,
        Blake2S160    = ffi::GCRY_MD_BLAKE2S_160,
        Blake2S128    = ffi::GCRY_MD_BLAKE2S_128,
    }
}

impl Algorithm {
    #[inline]
    pub fn from_name<S: Into<String>>(name: S) -> Option<Algorithm> {
        let name = try_opt!(CString::new(name.into()).ok());
        let result = unsafe { ffi::gcry_md_map_name(name.as_ptr()) };
        if result != 0 {
            unsafe { Some(Algorithm::from_raw(result)) }
        } else {
            None
        }
    }

    #[inline]
    pub fn is_available(&self) -> bool {
        let _ = ::get_token();
        unsafe { ffi::gcry_md_test_algo(self.raw()) == 0 }
    }

    #[inline]
    pub fn name(&self) -> result::Result<&'static str, Option<Utf8Error>> {
        self.name_raw()
            .map_or(Err(None), |s| s.to_str().map_err(Some))
    }

    #[inline]
    pub fn name_raw(&self) -> Option<&'static CStr> {
        unsafe {
            ffi::gcry_md_algo_name(self.raw())
                .as_ref()
                .map(|s| CStr::from_ptr(s))
        }
    }

    #[inline]
    pub fn digest_len(&self) -> usize {
        unsafe { ffi::gcry_md_get_algo_dlen(self.raw()) as usize }
    }
}

bitflags! {
    pub flags Flags: ffi::gcry_md_flags {
        const FLAGS_NONE   = 0,
        const FLAG_SECURE  = ffi::GCRY_MD_FLAG_SECURE,
        const FLAG_HMAC    = ffi::GCRY_MD_FLAG_HMAC,
        const FLAG_BUGEMU1 = ffi::GCRY_MD_FLAG_BUGEMU1,
    }
}

#[derive(Debug)]
pub struct MessageDigest(NonZero<ffi::gcry_md_hd_t>);

impl Drop for MessageDigest {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            ffi::gcry_md_close(self.as_raw());
        }
    }
}

impl MessageDigest {
    impl_wrapper!(MessageDigest: ffi::gcry_md_hd_t);

    #[inline]
    pub fn new(algo: Algorithm) -> Result<MessageDigest> {
        MessageDigest::with_flags(algo, FLAGS_NONE)
    }

    #[inline]
    pub fn with_flags(algo: Algorithm, flags: Flags) -> Result<MessageDigest> {
        let _ = ::get_token();
        unsafe {
            let mut handle: ffi::gcry_md_hd_t = ptr::null_mut();
            return_err!(ffi::gcry_md_open(&mut handle, algo.raw(), flags.bits()));
            Ok(MessageDigest::from_raw(handle))
        }
    }

    #[inline]
    pub fn try_clone(&self) -> Result<MessageDigest> {
        let mut handle: ffi::gcry_md_hd_t = ptr::null_mut();
        unsafe {
            return_err!(ffi::gcry_md_copy(&mut handle, self.as_raw()));
            Ok(MessageDigest::from_raw(handle))
        }
    }

    #[inline]
    pub fn enable(&mut self, algo: Algorithm) -> Result<()> {
        unsafe {
            return_err!(ffi::gcry_md_enable(self.as_raw(), algo.raw()));
        }
        Ok(())
    }

    #[inline]
    pub fn is_enabled(&self, algo: Algorithm) -> bool {
        unsafe { ffi::gcry_md_is_enabled(self.as_raw(), algo.raw()) != 0 }
    }

    #[inline]
    pub fn is_secure(&self) -> bool {
        unsafe { ffi::gcry_md_is_secure(self.as_raw()) != 0 }
    }

    #[inline]
    pub fn set_key<B: AsRef<[u8]>>(&mut self, key: B) -> Result<()> {
        let key = key.as_ref();
        unsafe {
            return_err!(ffi::gcry_md_setkey(
                self.as_raw(),
                key.as_ptr() as *const _,
                key.len()
            ));
        }
        Ok(())
    }

    #[inline]
    pub fn reset(&mut self) {
        unsafe { ffi::gcry_md_reset(self.as_raw()) }
    }

    #[inline]
    pub fn update(&mut self, bytes: &[u8]) {
        unsafe {
            ffi::gcry_md_write(self.as_raw(), bytes.as_ptr() as *const _, bytes.len());
        }
    }

    #[inline]
    pub fn finish(&mut self) {
        unsafe {
            ffi::gcry_md_final(self.as_raw());
        }
    }

    #[inline]
    pub fn get_only_digest(&mut self) -> Option<&[u8]> {
        let algo = unsafe { ffi::gcry_md_get_algo(self.as_raw()) };
        if algo != 0 {
            unsafe { self.get_digest(Algorithm::from_raw(algo)) }
        } else {
            None
        }
    }

    #[inline]
    pub fn get_digest(&mut self, algo: Algorithm) -> Option<&[u8]> {
        let len = algo.digest_len();
        if len == 0 {
            return None;
        }

        unsafe {
            ffi::gcry_md_read(self.as_raw(), algo.raw())
                .as_ref()
                .map(|x| slice::from_raw_parts(x, len))
        }
    }
}

impl Write for MessageDigest {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.update(buf);
        Ok(buf.len())
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

#[inline]
pub fn hash(algo: Algorithm, src: &[u8], dst: &mut [u8]) {
    assert!(algo.is_available());
    assert!(dst.len() >= algo.digest_len());
    let _ = ::get_token();
    unsafe {
        ffi::gcry_md_hash_buffer(
            algo.raw(),
            dst.as_mut_ptr() as *mut _,
            src.as_ptr() as *const _,
            src.len().into(),
        );
    }
}