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::str::Utf8Error;

use ffi;
use libc::c_int;

use {NonZero, Result};

ffi_enum_wrapper! {
    #[allow(non_camel_case_types)]
    pub enum Algorithm: c_int {
        HmacSha256        = ffi::GCRY_MAC_HMAC_SHA256,
        HmacSha224        = ffi::GCRY_MAC_HMAC_SHA224,
        HmacSha512        = ffi::GCRY_MAC_HMAC_SHA512,
        HmacSha384        = ffi::GCRY_MAC_HMAC_SHA384,
        HmacSha1          = ffi::GCRY_MAC_HMAC_SHA1,
        HmacMd5           = ffi::GCRY_MAC_HMAC_MD5,
        HmacMd4           = ffi::GCRY_MAC_HMAC_MD4,
        HmacRmd160        = ffi::GCRY_MAC_HMAC_RMD160,
        HmacTiger1        = ffi::GCRY_MAC_HMAC_TIGER1,
        HmacWhirlpool     = ffi::GCRY_MAC_HMAC_WHIRLPOOL,
        HmacGostR3411_94  = ffi::GCRY_MAC_HMAC_GOSTR3411_94,
        HmacStribog256    = ffi::GCRY_MAC_HMAC_STRIBOG256,
        HmacStribog512    = ffi::GCRY_MAC_HMAC_STRIBOG512,
        CmacAes           = ffi::GCRY_MAC_CMAC_AES,
        Cmac3des          = ffi::GCRY_MAC_CMAC_3DES,
        CmacCamellia      = ffi::GCRY_MAC_CMAC_CAMELLIA,
        CmacCast5         = ffi::GCRY_MAC_CMAC_CAST5,
        CmacBlowfish      = ffi::GCRY_MAC_CMAC_BLOWFISH,
        CmacTwofish       = ffi::GCRY_MAC_CMAC_TWOFISH,
        CmacSerpent       = ffi::GCRY_MAC_CMAC_SERPENT,
        CmacSeed          = ffi::GCRY_MAC_CMAC_SEED,
        CmacRfc2268       = ffi::GCRY_MAC_CMAC_RFC2268,
        CmacIdea          = ffi::GCRY_MAC_CMAC_IDEA,
        CmacGost28147     = ffi::GCRY_MAC_CMAC_GOST28147,
        GmacAes           = ffi::GCRY_MAC_GMAC_AES,
        GmacCamellia      = ffi::GCRY_MAC_GMAC_CAMELLIA,
        GmacTwofish       = ffi::GCRY_MAC_GMAC_TWOFISH,
        GmacSerpent       = ffi::GCRY_MAC_GMAC_SERPENT,
        GmacSeed          = ffi::GCRY_MAC_GMAC_SEED,
        Poly1305          = ffi::GCRY_MAC_POLY1305,
        Poly1305Aes       = ffi::GCRY_MAC_POLY1305_AES,
        Poly1305Camellia  = ffi::GCRY_MAC_POLY1305_CAMELLIA,
        Poly1305Twofish   = ffi::GCRY_MAC_POLY1305_TWOFISH,
        Poly1305Serpent   = ffi::GCRY_MAC_POLY1305_SERPENT,
        Poly1305Seed      = ffi::GCRY_MAC_POLY1305_SEED,
    }
}

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_mac_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_mac_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_mac_algo_name(self.raw())
                .as_ref()
                .map(|s| CStr::from_ptr(s))
        }
    }

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

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

bitflags! {
    pub flags Flags: ffi::gcry_mac_flags {
        const FLAGS_NONE  = 0,
        const FLAG_SECURE = ffi::GCRY_MAC_FLAG_SECURE,
    }
}

#[derive(Debug)]
pub struct Mac(NonZero<ffi::gcry_mac_hd_t>);

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

impl Mac {
    impl_wrapper!(Mac: ffi::gcry_mac_hd_t);

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

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

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

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

    #[inline]
    pub fn reset(&mut self) -> Result<()> {
        unsafe {
            return_err!(ffi::gcry_mac_reset(self.as_raw()));
        }
        Ok(())
    }

    #[inline]
    pub fn update(&mut self, bytes: &[u8]) -> Result<()> {
        unsafe {
            return_err!(ffi::gcry_mac_write(
                self.as_raw(),
                bytes.as_ptr() as *const _,
                bytes.len()
            ));
        }
        Ok(())
    }

    #[inline]
    pub fn get_mac(&mut self, buf: &mut [u8]) -> Result<usize> {
        let mut len = buf.len();
        unsafe {
            return_err!(ffi::gcry_mac_read(
                self.as_raw(),
                buf.as_mut_ptr() as *mut _,
                &mut len
            ));
        }
        Ok(len)
    }

    #[inline]
    pub fn verify(&mut self, buf: &[u8]) -> Result<()> {
        unsafe {
            return_err!(ffi::gcry_mac_verify(
                self.as_raw(),
                buf.as_ptr() as *mut _,
                buf.len()
            ));
        }
        Ok(())
    }
}

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

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