botan-0.10.7/.cargo_vcs_info.json0000644000000001430000000000100122220ustar { "git": { "sha1": "4cd024a093f450aa90b23c96da3fcae8df819111" }, "path_in_vcs": "botan" }botan-0.10.7/Cargo.toml0000644000000022240000000000100102220ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.58" name = "botan" version = "0.10.7" authors = ["Jack Lloyd "] description = "Rust wrapper for Botan cryptography library" homepage = "https://botan.randombit.net/" documentation = "https://docs.rs/botan" readme = "README.md" keywords = ["crypto"] categories = [ "cryptography", "api-bindings", "no-std", ] license = "MIT" repository = "https://github.com/randombit/botan-rs" [dependencies.botan-sys] version = "0.10.5" [dev-dependencies.hex] version = "0.4" [dev-dependencies.wycheproof] version = "0.5" [features] botan3 = ["botan-sys/botan3"] default = [] no-std = ["botan-sys/no-std"] vendored = ["botan-sys/vendored"] botan-0.10.7/Cargo.toml.orig000064400000000000000000000012621046102023000137040ustar 00000000000000[package] name = "botan" version = "0.10.7" authors = ["Jack Lloyd "] description = "Rust wrapper for Botan cryptography library" license = "MIT" homepage = "https://botan.randombit.net/" repository = "https://github.com/randombit/botan-rs" documentation = "https://docs.rs/botan" keywords = [ "crypto" ] readme = "../README.md" categories = [ "cryptography", "api-bindings", "no-std" ] edition = "2021" rust-version = "1.58" [dependencies] botan-sys = { version = "0.10.5", path = "../botan-sys" } [dev-dependencies] wycheproof = "0.5" hex = "0.4" [features] default = [] no-std = ["botan-sys/no-std"] vendored = ["botan-sys/vendored"] botan3 = ["botan-sys/botan3"] botan-0.10.7/README.md000064400000000000000000000027331046102023000123000ustar 00000000000000# botan-rs [![Build status](https://github.com/randombit/botan-rs/workflows/ci/badge.svg)](https://github.com/randombit/botan-rs/actions) [![crates.io](https://img.shields.io/crates/v/botan.svg)](https://crates.io/crates/botan) [![docs.rs](https://docs.rs/botan/badge.svg)](https://docs.rs/botan) This crate wraps the C API exposed by the [Botan](https://botan.randombit.net/) cryptography library. Currently the crate exposes ciphers, hashes, MACs, KDFs, password based key derivation (PBKDF2, Scrypt, Argon2, etc), bcrypt password hashes, random number generators, X.509 certificates, format preserving encryption, HOTP/TOTP, NIST key wrapping, multiprecision integers, and the usual public key algorithms (RSA, ECDSA, ECDH, DH, ...) PRs and comments/issues happily accepted. MSRV ----- The Minimum Supported Rust Version of this crate is Rust 1.58.0, *unless* you enable support for `no_std` builds, in which case Rust 1.64.0 is required. Botan Versions Supported -------------------------- This crate requires Botan 2.13.0 or higher. Features --------- The following features are supported: * `no-std`: Enable a no-std build. This requires Rust 1.64.0 or higher, as well as `alloc` support * `vendored`: Build a copy of the C++ library directly, without relying on a system installed version. * `botan3`: Enable support for using APIs added in Botan 3. This enables several new features, and more efficient operation. This feature is implicitly enabled if you use `vendored`. botan-0.10.7/src/bcrypt.rs000064400000000000000000000031211046102023000134510ustar 00000000000000use crate::utils::*; use botan_sys::*; use crate::rng::RandomNumberGenerator; const BCRYPT_SIZE: usize = 60; /// Produce a bcrypt password hash /// /// # Examples /// /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let bcrypt1 = botan::bcrypt_hash("password", &mut rng, 10).unwrap(); /// let bcrypt2 = botan::bcrypt_hash("password", &mut rng, 10).unwrap(); /// assert_ne!(bcrypt1, bcrypt2); // different salt each time /// ``` pub fn bcrypt_hash( pass: &str, rng: &mut RandomNumberGenerator, workfactor: usize, ) -> Result { let mut out = vec![0; BCRYPT_SIZE + 1]; let mut out_len = out.len(); botan_call!( botan_bcrypt_generate, out.as_mut_ptr(), &mut out_len, make_cstr(pass)?.as_ptr(), rng.handle(), workfactor, 0u32 )?; out.resize(out_len - 1, 0); String::from_utf8(out).map_err(Error::conversion_error) } /// Verify a bcrypt password hash /// /// # Examples /// /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let bcrypt = botan::bcrypt_hash("password", &mut rng, 10).unwrap(); /// assert_eq!(botan::bcrypt_verify("not even close", &bcrypt), Ok(false)); /// assert_eq!(botan::bcrypt_verify("password", &bcrypt), Ok(true)); /// ``` pub fn bcrypt_verify(pass: &str, hash: &str) -> Result { let rc = unsafe { botan_bcrypt_is_valid(make_cstr(pass)?.as_ptr(), make_cstr(hash)?.as_ptr()) }; if rc == 0 { Ok(true) } else if rc == BOTAN_FFI_INVALID_VERIFIER { Ok(false) } else { Err(Error::from_rc(rc)) } } botan-0.10.7/src/block.rs000064400000000000000000000150501046102023000132440ustar 00000000000000use crate::utils::*; use botan_sys::*; #[derive(Debug)] /// A raw block cipher interface (ie ECB mode) /// /// Warning: you almost certainly want an AEAD cipher mode instead pub struct BlockCipher { obj: botan_block_cipher_t, block_size: usize, min_keylen: usize, max_keylen: usize, mod_keylen: usize, } unsafe impl Sync for BlockCipher {} unsafe impl Send for BlockCipher {} botan_impl_drop!(BlockCipher, botan_block_cipher_destroy); impl BlockCipher { /// Create a new block cipher instance, failing if the cipher is unknown /// /// # Examples /// /// ``` /// let cipher = botan::BlockCipher::new("AES-128"); /// assert!(cipher.is_ok()); /// let no_such_cipher = botan::BlockCipher::new("SuperCipher9000"); /// assert!(no_such_cipher.is_err()); /// ``` pub fn new(name: &str) -> Result { let obj = botan_init!(botan_block_cipher_init, make_cstr(name)?.as_ptr())?; let block_size = { let rc = unsafe { botan_block_cipher_block_size(obj) }; if rc < 0 { return Err(Error::from_rc(rc)); } rc as usize }; let (min_keylen, max_keylen, mod_keylen) = botan_usize3!(botan_block_cipher_get_keyspec, obj)?; Ok(BlockCipher { obj, block_size, min_keylen, max_keylen, mod_keylen, }) } /// Return the block size of the cipher, in bytes /// /// # Examples /// /// ``` /// let cipher = botan::BlockCipher::new("AES-128").unwrap(); /// assert_eq!(cipher.block_size().unwrap(), 16); /// ``` pub fn block_size(&self) -> Result { Ok(self.block_size) } /// Return the name of this algorithm which may or may not exactly /// match what was provided to new() /// /// # Examples /// /// ``` /// let cipher = botan::BlockCipher::new("AES-128").unwrap(); /// assert_eq!(cipher.algo_name().unwrap(), "AES-128"); /// ``` pub fn algo_name(&self) -> Result { call_botan_ffi_returning_string(32, &|out_buf, out_len| unsafe { botan_block_cipher_name(self.obj, out_buf as *mut c_char, out_len) }) } /// Return information about the key lengths supported by this object pub fn key_spec(&self) -> Result { KeySpec::new(self.min_keylen, self.max_keylen, self.mod_keylen) } /// Set the key for the cipher. /// /// # Errors /// /// Fails if the key is not a valid length for the cipher /// /// # Examples /// /// ``` /// let mut cipher = botan::BlockCipher::new("AES-128").unwrap(); /// assert!(cipher.set_key(&vec![0; 32]).is_err()); /// assert!(cipher.set_key(&vec![0; 16]).is_ok()); /// ``` pub fn set_key(&mut self, key: &[u8]) -> Result<()> { botan_call!( botan_block_cipher_set_key, self.obj, key.as_ptr(), key.len() ) } /// Encrypt some blocks of data /// /// # Errors /// /// Fails if the input is not a multiple of the block size, or if the /// key was not set on the object. /// /// # Examples /// /// ``` /// let mut cipher = botan::BlockCipher::new("AES-128").unwrap(); /// // Key is not set /// assert!(cipher.encrypt_blocks(&vec![0; 16]).is_err()); /// assert!(cipher.set_key(&vec![0; 16]).is_ok()); /// // Not a multiple of block size /// assert!(cipher.encrypt_blocks(&vec![0; 17]).is_err()); /// // Key is set and multiple of block size - ok /// assert!(cipher.encrypt_blocks(&vec![0; 16]).is_ok()); /// ``` pub fn encrypt_blocks(&self, input: &[u8]) -> Result> { let mut ivec = input.to_vec(); self.encrypt_in_place(&mut ivec)?; Ok(ivec) } /// Encrypt in place /// /// # Errors /// /// Fails if the input is not a multiple of the block size, or if the /// key was not set on the object. pub fn encrypt_in_place(&self, buf: &mut [u8]) -> Result<()> { if buf.len() % self.block_size != 0 { return Err(Error::with_message( ErrorType::InvalidInput, "Invalid input size".to_string(), )); } let blocks = buf.len() / self.block_size; botan_call!( botan_block_cipher_encrypt_blocks, self.obj, buf.as_ptr(), buf.as_mut_ptr(), blocks ) } /// Decrypt some blocks of data /// /// # Errors /// /// Fails if the input is not a multiple of the block size, or if the /// key was not set on the object. /// /// # Examples /// /// ``` /// let mut cipher = botan::BlockCipher::new("AES-128").unwrap(); /// // Key is not set /// assert!(cipher.decrypt_blocks(&vec![0; 16]).is_err()); /// assert!(cipher.set_key(&vec![0; 16]).is_ok()); /// // Not a multiple of block size /// assert!(cipher.decrypt_blocks(&vec![0; 17]).is_err()); /// // Key is set and multiple of block size - ok /// assert!(cipher.decrypt_blocks(&vec![0; 16]).is_ok()); /// ``` pub fn decrypt_blocks(&self, input: &[u8]) -> Result> { let mut ivec = input.to_vec(); self.decrypt_in_place(&mut ivec)?; Ok(ivec) } /// Decrypt in place /// /// # Errors /// /// Fails if the input is not a multiple of the block size, or if the /// key was not set on the object. pub fn decrypt_in_place(&self, buf: &mut [u8]) -> Result<()> { if buf.len() % self.block_size != 0 { return Err(Error::with_message( ErrorType::InvalidInput, "Invalid input size".to_string(), )); } let blocks = buf.len() / self.block_size; botan_call!( botan_block_cipher_decrypt_blocks, self.obj, buf.as_ptr(), buf.as_mut_ptr(), blocks ) } /// Clear the key set on the cipher from memory. After this, the /// object is un-keyed and must be re-keyed before use. /// /// # Examples /// /// ``` /// let mut cipher = botan::BlockCipher::new("AES-128").unwrap(); /// assert!(cipher.set_key(&vec![0; 16]).is_ok()); /// assert!(cipher.encrypt_blocks(&vec![0; 16]).is_ok()); /// assert!(cipher.clear().is_ok()); /// assert!(cipher.encrypt_blocks(&vec![0; 16]).is_err()); /// ``` pub fn clear(&mut self) -> Result<()> { botan_call!(botan_block_cipher_clear, self.obj) } } botan-0.10.7/src/cipher.rs000064400000000000000000000317341046102023000134330ustar 00000000000000use crate::utils::*; use botan_sys::*; #[derive(Debug)] /// A symmetric cipher pub struct Cipher { obj: botan_cipher_t, direction: CipherDirection, tag_length: usize, update_granularity: usize, ideal_update_granularity: Option, default_nonce_length: usize, min_keylen: usize, max_keylen: usize, mod_keylen: usize, } unsafe impl Sync for Cipher {} unsafe impl Send for Cipher {} #[derive(Eq, PartialEq, Debug, Copy, Clone)] /// Which direction the cipher processes in pub enum CipherDirection { /// Encrypt Encrypt, /// Decrypt Decrypt, } botan_impl_drop!(Cipher, botan_cipher_destroy); impl Cipher { /// Create a new cipher object in the specified direction /// /// # Examples /// ``` /// let aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// ``` pub fn new(name: &str, direction: CipherDirection) -> Result { let mut flag = 0u32; if direction == CipherDirection::Decrypt { flag |= 1u32; }; let obj = botan_init!(botan_cipher_init, make_cstr(name)?.as_ptr(), flag)?; let tag_length = botan_usize!(botan_cipher_get_tag_length, obj)?; let update_granularity = botan_usize!(botan_cipher_get_update_granularity, obj)?; let default_nonce_length = botan_usize!(botan_cipher_get_default_nonce_length, obj)?; let (min_keylen, max_keylen, mod_keylen) = botan_usize3!(botan_cipher_get_keyspec, obj)?; #[cfg(feature = "botan3")] let ideal_update_granularity = Some(botan_usize!( botan_cipher_get_ideal_update_granularity, obj )?); #[cfg(not(feature = "botan3"))] let ideal_update_granularity = None; Ok(Cipher { obj, direction, tag_length, update_granularity, ideal_update_granularity, default_nonce_length, min_keylen, max_keylen, mod_keylen, }) } /// Return the name of this algorithm which may or may not exactly /// match what was provided to new() /// /// # Examples /// /// ``` /// let cipher = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// assert_eq!(cipher.algo_name().unwrap(), "AES-128/GCM(16)"); /// ``` pub fn algo_name(&self) -> Result { call_botan_ffi_returning_string(32, &|out_buf, out_len| unsafe { botan_cipher_name(self.obj, out_buf as *mut c_char, out_len) }) } /// Return the direction this cipher object is operating in /// /// # Examples /// /// ``` /// let cipher = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// assert_eq!(cipher.direction().unwrap(), botan::CipherDirection::Encrypt); /// ``` pub fn direction(&self) -> Result { Ok(self.direction) } /// Query if a particular nonce size is valid for this cipher /// /// # Examples /// ``` /// let aes_cbc = botan::Cipher::new("AES-128/CBC", botan::CipherDirection::Encrypt).unwrap(); /// assert_eq!(aes_cbc.valid_nonce_length(16), Ok(true)); /// assert_eq!(aes_cbc.valid_nonce_length(1), Ok(false)); /// ``` pub fn valid_nonce_length(&self, l: usize) -> Result { botan_bool_in_rc!(botan_cipher_valid_nonce_length, self.obj, l) } /// For an AEAD, return the tag length of the cipher /// /// # Examples /// ``` /// let aes_cbc = botan::Cipher::new("AES-128/CBC", botan::CipherDirection::Encrypt).unwrap(); /// assert_eq!(aes_cbc.tag_length(), 0); /// let aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// assert_eq!(aes_gcm.tag_length(), 16); /// ``` #[must_use] pub fn tag_length(&self) -> usize { self.tag_length } /// Return the minimum input size that must be provided pub fn update_granularity(&self) -> usize { self.update_granularity } /// Return the ideal input size for best performance /// /// This function returns None if the C++ API does not support this pub fn ideal_update_granularity(&self) -> Option { self.ideal_update_granularity } /// Return the default nonce length for the cipher. Some ciphers only /// support a single nonce size. Others support variable sizes, but some /// particular size (typically 96 bits) is handled particularly efficiently. /// /// # Examples /// ``` /// let aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// assert_eq!(aes_gcm.default_nonce_length(), 12); /// ``` #[must_use] pub fn default_nonce_length(&self) -> usize { self.default_nonce_length } /// Return information about the key lengths supported by this object pub fn key_spec(&self) -> Result { KeySpec::new(self.min_keylen, self.max_keylen, self.mod_keylen) } /// Set the key for the cipher /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// ``` pub fn set_key(&mut self, key: &[u8]) -> Result<()> { botan_call!(botan_cipher_set_key, self.obj, key.as_ptr(), key.len())?; Ok(()) } /// Set the associated data for the cipher. This only works for AEAD modes. /// The key must already be set to set the AD. /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// aes_gcm.set_associated_data(&[1,2,3]).unwrap(); /// ``` pub fn set_associated_data(&mut self, ad: &[u8]) -> Result<()> { botan_call!( botan_cipher_set_associated_data, self.obj, ad.as_ptr(), ad.len() )?; Ok(()) } /// Encrypt or decrypt a message with the provided nonce. The key must /// already have been set. /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// let nonce = vec![0; aes_gcm.default_nonce_length()]; /// let msg = vec![0; 48]; /// let ctext = aes_gcm.process(&nonce, &msg).unwrap(); /// assert_eq!(ctext.len(), msg.len() + aes_gcm.tag_length()); /// ``` pub fn process(&mut self, nonce: &[u8], msg: &[u8]) -> Result> { botan_call!(botan_cipher_start, self.obj, nonce.as_ptr(), nonce.len())?; let flags = 1u32; // only supporting one-shot processing here // FIXME(2.8): need botan_cipher_output_len to size this correctly let mut output = vec![0; msg.len() + 64]; let mut output_written = 0; let mut input_consumed = 0; botan_call!( botan_cipher_update, self.obj, flags, output.as_mut_ptr(), output.len(), &mut output_written, msg.as_ptr(), msg.len(), &mut input_consumed )?; assert!(input_consumed == msg.len()); assert!(output_written <= output.len()); output.resize(output_written, 0); Ok(output) } /// start processing a message /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// let nonce = vec![0; aes_gcm.default_nonce_length()]; /// let msg = vec![0; 48]; /// aes_gcm.start(&nonce).unwrap(); /// let ctext = aes_gcm.finish(&msg).unwrap(); /// assert_eq!(ctext.len(), msg.len() + aes_gcm.tag_length()); /// ``` pub fn start(&mut self, nonce: &[u8]) -> Result<()> { botan_call!(botan_cipher_start, self.obj, nonce.as_ptr(), nonce.len()) } /// incremental update fn _update(&mut self, msg: &[u8], end: bool) -> Result> { let mut output = vec![0; msg.len() + if end { self.tag_length() } else { 0 }]; let output_written = self._update_into(msg, end, &mut output)?; output.resize(output_written, 0); Ok(output) } /// incremental update, writing to provided buffer /// /// Returns the number of bytes written to `output`. fn _update_into(&mut self, msg: &[u8], end: bool, output: &mut [u8]) -> Result { let expected_len = match (self.direction, end) { (CipherDirection::Encrypt, false) | (CipherDirection::Decrypt, false) => msg.len(), (CipherDirection::Encrypt, true) => msg.len() + self.tag_length(), (CipherDirection::Decrypt, true) => msg.len().saturating_sub(self.tag_length()), }; if output.len() < expected_len { return Err(Error::with_message( ErrorType::BadParameter, format!( "Provided output buffer has length {}, but expected at least {}", output.len(), expected_len ), )); } let flags = u32::from(end); let mut output_written = 0; let mut input_consumed = 0; botan_call!( botan_cipher_update, self.obj, flags, output.as_mut_ptr(), output.len(), &mut output_written, msg.as_ptr(), msg.len(), &mut input_consumed )?; assert_eq!(input_consumed, msg.len()); assert!(output_written <= output.len()); Ok(output_written) } /// incremental update /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// let nonce = vec![0; aes_gcm.default_nonce_length()]; /// let msg = vec![0; 96]; /// aes_gcm.start(&nonce).unwrap(); /// let mut ctext = vec![]; /// ctext.extend_from_slice(&aes_gcm.update(&msg[..64]).unwrap()); /// ctext.extend_from_slice(&aes_gcm.finish(&msg[64..]).unwrap()); /// assert_eq!(ctext.len(), msg.len() + aes_gcm.tag_length()); /// ``` pub fn update(&mut self, msg: &[u8]) -> Result> { self._update(msg, false) } /// incremental update writing into the given buffer /// /// The length of `output` has to be at least `msg.len()`. /// Returns the number of bytes written to `output`. /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// let nonce = vec![0; aes_gcm.default_nonce_length()]; /// let msg = vec![0; 96]; /// aes_gcm.start(&nonce).unwrap(); /// let mut ctext = vec![0; msg.len() + aes_gcm.tag_length()]; /// let mut written = 0; /// written += aes_gcm.update_into(&msg[..64], &mut ctext[written..]).unwrap(); /// written += aes_gcm.finish_into(&msg[64..], &mut ctext[written..]).unwrap(); /// assert_eq!(written, msg.len() + aes_gcm.tag_length()); /// ``` pub fn update_into(&mut self, msg: &[u8], output: &mut [u8]) -> Result { self._update_into(msg, false, output) } /// finish function /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// let nonce = vec![0; aes_gcm.default_nonce_length()]; /// let msg = vec![0; 48]; /// aes_gcm.start(&nonce).unwrap(); /// let ctext = aes_gcm.finish(&msg).unwrap(); /// assert_eq!(ctext.len(), msg.len() + aes_gcm.tag_length()); /// ``` pub fn finish(&mut self, msg: &[u8]) -> Result> { self._update(msg, true) } /// finish function writing into the given buffer /// /// The length of `output` has to be at least `msg.len() - /// self.tag_length()` for decryption, and `msg.len() + /// self.tag_length()` for encryption. Returns the number of /// bytes written to `output`. /// /// # Examples /// ``` /// let mut aes_gcm = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt).unwrap(); /// aes_gcm.set_key(&vec![0; 16]).unwrap(); /// let nonce = vec![0; aes_gcm.default_nonce_length()]; /// let msg = vec![0; 96]; /// aes_gcm.start(&nonce).unwrap(); /// let mut ctext = vec![0; msg.len() + aes_gcm.tag_length()]; /// let written = aes_gcm.finish_into(&msg, &mut ctext[..]).unwrap(); /// assert_eq!(written, msg.len() + aes_gcm.tag_length()); /// ``` pub fn finish_into(&mut self, msg: &[u8], output: &mut [u8]) -> Result { self._update_into(msg, true, output) } /// Clear all state associated with the key pub fn clear(&mut self) -> Result<()> { botan_call!(botan_cipher_clear, self.obj) } } botan-0.10.7/src/fpe.rs000064400000000000000000000037611046102023000127320ustar 00000000000000use crate::utils::*; use botan_sys::*; use crate::mp::MPI; /// Represents an instance of format preserving encryption /// /// # Examples /// /// ``` /// use std::str::FromStr; /// let modulus = botan::MPI::from_str("1000000000").unwrap(); /// let key = vec![0; 32]; /// let rounds = 16; /// let compat_mode = false; /// let fpe = botan::FPE::new_fe1(&modulus, &key, rounds, compat_mode).unwrap(); /// let input = botan::MPI::from_str("9392024").unwrap(); /// let tweak = vec![1,2,3,4,5]; /// let ctext = fpe.encrypt(&input, &tweak).unwrap(); /// assert!(ctext < modulus); /// let ptext = fpe.decrypt(&ctext, &tweak).unwrap(); /// assert_eq!(ptext, input); /// ``` #[derive(Debug)] #[allow(clippy::upper_case_acronyms)] pub struct FPE { obj: botan_fpe_t, } unsafe impl Sync for FPE {} unsafe impl Send for FPE {} botan_impl_drop!(FPE, botan_fpe_destroy); impl FPE { /// Create a new FPE instance, FE1 scheme /// Rounds should be 16 or higher for best security pub fn new_fe1(modulus: &MPI, key: &[u8], rounds: usize, compat_mode: bool) -> Result { let flags = u32::from(compat_mode); let obj = botan_init!( botan_fpe_fe1_init, modulus.handle(), key.as_ptr(), key.len(), rounds, flags )?; Ok(FPE { obj }) } /// Encrypt value under the FPE scheme using provided tweak pub fn encrypt(&self, x: &MPI, tweak: &[u8]) -> Result { let r = x.duplicate()?; botan_call!( botan_fpe_encrypt, self.obj, r.handle(), tweak.as_ptr(), tweak.len() )?; Ok(r) } /// Decrypt value under the FPE scheme using provided tweak pub fn decrypt(&self, x: &MPI, tweak: &[u8]) -> Result { let r = x.duplicate()?; botan_call!( botan_fpe_decrypt, self.obj, r.handle(), tweak.as_ptr(), tweak.len() )?; Ok(r) } } botan-0.10.7/src/hash.rs000064400000000000000000000102331046102023000130730ustar 00000000000000use crate::utils::*; use botan_sys::*; #[derive(Debug)] /// A hash function object pub struct HashFunction { obj: botan_hash_t, output_length: usize, } impl Clone for HashFunction { fn clone(&self) -> HashFunction { self.duplicate().expect("copying hash object state failed") } } unsafe impl Sync for HashFunction {} unsafe impl Send for HashFunction {} botan_impl_drop!(HashFunction, botan_hash_destroy); impl HashFunction { /// Create a new hash function /// /// # Errors /// Will fail if the named hash is not known /// # Examples /// ``` /// assert!(botan::HashFunction::new("SHA-256").is_ok()); /// assert!(botan::HashFunction::new("Hash9000").is_err()); /// ``` pub fn new(name: &str) -> Result { let obj = botan_init!(botan_hash_init, make_cstr(name)?.as_ptr(), 0u32)?; let output_length = botan_usize!(botan_hash_output_length, obj)?; Ok(HashFunction { obj, output_length }) } /// Return the name of this algorithm which may or may not exactly /// match what was provided to new() /// /// # Examples /// /// ``` /// let hash = botan::HashFunction::new("SHA-384").unwrap(); /// assert_eq!(hash.algo_name().unwrap(), "SHA-384"); /// ``` pub fn algo_name(&self) -> Result { call_botan_ffi_returning_string(32, &|out_buf, out_len| unsafe { botan_hash_name(self.obj, out_buf as *mut c_char, out_len) }) } /// Return the output length of the hash function, in bytes /// /// # Examples /// ``` /// let hash = botan::HashFunction::new("SHA-256").unwrap(); /// assert_eq!(hash.output_length().unwrap(), 32); /// ``` pub fn output_length(&self) -> Result { Ok(self.output_length) } /// Return the block length of the hash function, in bytes /// /// # Examples /// ``` /// let hash = botan::HashFunction::new("SHA-256").unwrap(); /// assert_eq!(hash.block_size().unwrap(), 64); /// ``` pub fn block_size(&self) -> Result { botan_usize!(botan_hash_block_size, self.obj) } /// Add data to a hash computation, may be called many times /// /// # Examples /// ``` /// let mut hash = botan::HashFunction::new("SHA-256").unwrap(); /// hash.update(&[1,2,3]).unwrap(); /// hash.update(&[4,5,6]).unwrap(); /// ``` pub fn update(&mut self, data: &[u8]) -> Result<()> { botan_call!(botan_hash_update, self.obj, data.as_ptr(), data.len())?; Ok(()) } /// Finalize the computation, returning the hash of the message /// /// # Examples /// ``` /// let mut hash = botan::HashFunction::new("SHA-256").unwrap(); /// hash.update(&[1,2,3]).unwrap(); /// hash.update(&[4,5,6]).unwrap(); /// let digest = hash.finish().unwrap(); /// ``` pub fn finish(&mut self) -> Result> { let mut output = vec![0; self.output_length]; botan_call!(botan_hash_final, self.obj, output.as_mut_ptr())?; Ok(output) } /// Clear the internal state of the hash function. It acts as if it /// was newly created, and is ready to compute a new digest. /// Basically the same as calling final, but without returning a /// result. pub fn clear(&mut self) -> Result<()> { botan_call!(botan_hash_clear, self.obj)?; Ok(()) } /// Copy hash object state to a new object, allowing prefixes of /// messages to be hashed. This function is also called by clone. /// /// # Errors /// Should not fail but might due to unexpected error /// # Examples /// ``` /// let mut hash = botan::HashFunction::new("SHA-256").unwrap(); /// hash.update(&[1,2,3]); /// let mut hash2 = hash.duplicate().unwrap(); /// hash2.update(&[4,5,6]); /// let result1 = hash.finish().unwrap(); // hash of 1,2,3 /// let result2 = hash2.finish().unwrap(); // hash of 1,2,3,4,5,6 /// ``` pub fn duplicate(&self) -> Result { let obj = botan_init!(botan_hash_copy_state, self.obj)?; Ok(HashFunction { obj, output_length: self.output_length, }) } } botan-0.10.7/src/kdf.rs000064400000000000000000000017241046102023000127210ustar 00000000000000use crate::utils::*; use botan_sys::*; /// Key derivation function /// /// Produces a KDF output of the specified size when run over the /// provided secret, salt, and label inputs /// /// # Examples /// ``` /// let salt = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; /// let label = vec![0x42, 0x6F, 0x62]; /// let secret = vec![0x4E, 0x6F, 0x74, 0x20, 0x54, 0x65, 0x6C, 0x6C, 0x69, 0x6E, 0x67]; /// let v = botan::kdf("HKDF(SHA-256)", 23, &secret, &salt, &label).unwrap(); /// assert_eq!(v.len(), 23); /// ``` pub fn kdf( algo: &str, output_len: usize, secret: &[u8], salt: &[u8], label: &[u8], ) -> Result> { let mut output = vec![0u8; output_len]; let algo = make_cstr(algo)?; botan_call!( botan_kdf, algo.as_ptr(), output.as_mut_ptr(), output_len, secret.as_ptr(), secret.len(), salt.as_ptr(), salt.len(), label.as_ptr(), label.len() )?; Ok(output) } botan-0.10.7/src/keywrap.rs000064400000000000000000000070101046102023000136310ustar 00000000000000use crate::utils::*; use botan_sys::*; #[cfg(feature = "botan3")] /// Wrap a key using NIST key wrap algorithm pub fn nist_kw_enc(cipher_algo: &str, padding: bool, kek: &[u8], key: &[u8]) -> Result> { let mut output = vec![0; key.len() + if padding { 32 } else { 8 }]; let mut output_len = output.len(); botan_call!( botan_nist_kw_enc, make_cstr(cipher_algo)?.as_ptr(), c_int::from(padding), key.as_ptr(), key.len(), kek.as_ptr(), kek.len(), output.as_mut_ptr(), &mut output_len )?; output.resize(output_len, 0); Ok(output) } #[cfg(feature = "botan3")] /// Unwrap a key using NIST key wrap algorithm pub fn nist_kw_dec( cipher_algo: &str, padding: bool, kek: &[u8], wrapped: &[u8], ) -> Result> { let mut output = vec![0; wrapped.len()]; let mut output_len = output.len(); botan_call!( botan_nist_kw_dec, make_cstr(cipher_algo)?.as_ptr(), c_int::from(padding), wrapped.as_ptr(), wrapped.len(), kek.as_ptr(), kek.len(), output.as_mut_ptr(), &mut output_len )?; output.resize(output_len, 0); Ok(output) } /// Wrap a key using RFC 3394's AES key wrap algorithm. /// /// The kek (key-encryption-key) must be a valid length for an AES /// key. The wrapped key must be a multiple of 8 bytes. /// /// # Examples /// /// ``` /// // Wrap a 128-bit key with a 256-bit key: /// let key = vec![0; 16]; /// let kek = vec![0; 32]; /// let wrapped = botan::rfc3394_key_wrap(&kek, &key).unwrap(); /// ``` pub fn rfc3394_key_wrap(kek: &[u8], key: &[u8]) -> Result> { if kek.len() != 16 && kek.len() != 24 && kek.len() != 32 { return Err(Error::with_message( ErrorType::InvalidKeyLength, "Invalid AES key length".to_string(), )); } if key.len() % 8 != 0 { return Err(Error::with_message( ErrorType::InvalidInput, "Invalid keywrap input length".to_string(), )); } let mut output = vec![0; key.len() + 8]; let mut output_len = output.len(); botan_call!( botan_key_wrap3394, key.as_ptr(), key.len(), kek.as_ptr(), kek.len(), output.as_mut_ptr(), &mut output_len )?; output.resize(output_len, 0); Ok(output) } /// Unwrap a key encrypted using RFC3394's AES key wrap algorithm /// # Examples /// /// ``` /// // Wrap a 128-bit key with a 256-bit key: /// let key = vec![0; 16]; /// let kek = vec![0; 32]; /// let wrapped = botan::rfc3394_key_wrap(&kek, &key).unwrap(); /// let unwrapped = botan::rfc3394_key_unwrap(&kek, &wrapped).unwrap(); /// assert_eq!(unwrapped, key); /// ``` pub fn rfc3394_key_unwrap(kek: &[u8], wrapped: &[u8]) -> Result> { if kek.len() != 16 && kek.len() != 24 && kek.len() != 32 { return Err(Error::with_message( ErrorType::InvalidKeyLength, "Invalid AES key length".to_string(), )); } if wrapped.len() % 8 != 0 { return Err(Error::with_message( ErrorType::InvalidInput, "Invalid keywrap input length".to_string(), )); } let mut output = vec![0; wrapped.len() - 8]; let mut output_len = output.len(); botan_call!( botan_key_unwrap3394, wrapped.as_ptr(), wrapped.len(), kek.as_ptr(), kek.len(), output.as_mut_ptr(), &mut output_len )?; output.resize(output_len, 0); Ok(output) } botan-0.10.7/src/lib.rs000064400000000000000000000055401046102023000127230ustar 00000000000000#![warn(missing_docs)] #![deny(missing_docs)] //! A wrapper for the Botan cryptography library #![cfg_attr(feature = "no-std", no_std)] #[cfg(feature = "no-std")] #[macro_use] extern crate alloc; extern crate botan_sys; macro_rules! botan_call { ($fn:path, $($args:expr),*) => {{ let rc = unsafe { $fn($($args),*) }; if rc == 0 { Ok(()) } else { Err(Error::from_rc(rc)) } }}; } macro_rules! botan_init { ($fn:path) => {{ let mut obj = ptr::null_mut(); let rc = unsafe { $fn(&mut obj) }; if rc == 0 { Ok(obj) } else { Err(Error::from_rc(rc)) } }}; ($fn:path, $($args:expr),*) => {{ let mut obj = ptr::null_mut(); let rc = unsafe { $fn(&mut obj, $($args),*) }; if rc == 0 { Ok(obj) } else { Err(Error::from_rc(rc)) } }}; } macro_rules! botan_impl_drop { ($typ:ty, $fn:path) => { impl Drop for $typ { fn drop(&mut self) { let rc = unsafe { $fn(self.obj) }; if rc != 0 { let err = Error::from_rc(rc); panic!("{} failed: {}", core::stringify!($fn), err); } } } }; } macro_rules! botan_usize { ($fn:path, $obj:expr) => {{ let mut val = 0; let rc = unsafe { $fn($obj, &mut val) }; if rc != 0 { Err(Error::from_rc(rc)) } else { Ok(val) } }}; } macro_rules! botan_usize3 { ($fn:path, $obj:expr) => {{ let mut val1 = 0; let mut val2 = 0; let mut val3 = 0; let rc = unsafe { $fn($obj, &mut val1, &mut val2, &mut val3) }; if rc != 0 { Err(Error::from_rc(rc)) } else { Ok((val1, val2, val3)) } }}; } macro_rules! botan_bool_in_rc { ($fn:path, $($args:expr),*) => {{ let rc = unsafe { $fn($($args),*) }; match rc { 0 => Ok(false), 1 => Ok(true), e => Err(Error::from_rc(e)), } }}; } mod bcrypt; mod block; mod cipher; mod fpe; mod hash; mod kdf; mod keywrap; mod mac; mod memutils; mod mp; mod otp; mod pbkdf; mod pk_ops; mod pubkey; mod rng; mod utils; mod version; mod x509_cert; mod x509_crl; #[cfg(feature = "botan3")] mod pk_ops_kem; #[cfg(feature = "botan3")] mod zfec; pub use crate::mp::*; pub use crate::rng::*; pub use crate::utils::*; pub use bcrypt::*; pub use block::*; pub use cipher::*; pub use fpe::*; pub use hash::*; pub use kdf::*; pub use keywrap::*; pub use mac::*; pub use memutils::*; pub use otp::*; pub use pbkdf::*; pub use pk_ops::*; pub use pubkey::*; pub use version::*; pub use x509_cert::*; pub use x509_crl::*; #[cfg(feature = "botan3")] pub use pk_ops_kem::*; #[cfg(feature = "botan3")] pub use zfec::*; botan-0.10.7/src/mac.rs000064400000000000000000000110451046102023000127120ustar 00000000000000use crate::utils::*; use botan_sys::*; #[derive(Debug)] /// Message authentication code pub struct MsgAuthCode { obj: botan_mac_t, output_length: usize, min_keylen: usize, max_keylen: usize, mod_keylen: usize, } unsafe impl Sync for MsgAuthCode {} unsafe impl Send for MsgAuthCode {} botan_impl_drop!(MsgAuthCode, botan_mac_destroy); impl MsgAuthCode { /// Create a new message authentication code /// /// # Examples /// ``` /// let hmac = botan::MsgAuthCode::new("HMAC(SHA-256)").unwrap(); /// ``` /// ``` /// let poly1305 = botan::MsgAuthCode::new("Poly1305").unwrap(); /// ``` pub fn new(name: &str) -> Result { let obj = botan_init!(botan_mac_init, make_cstr(name)?.as_ptr(), 0u32)?; let output_length = botan_usize!(botan_mac_output_length, obj)?; let (min_keylen, max_keylen, mod_keylen) = botan_usize3!(botan_mac_get_keyspec, obj)?; Ok(MsgAuthCode { obj, output_length, min_keylen, max_keylen, mod_keylen, }) } /// Return the name of this algorithm which may or may not exactly /// match what was provided to new() /// /// # Examples /// /// ``` /// let mac = botan::MsgAuthCode::new("HMAC(SHA-384)").unwrap(); /// assert_eq!(mac.algo_name().unwrap(), "HMAC(SHA-384)"); /// ``` pub fn algo_name(&self) -> Result { call_botan_ffi_returning_string(32, &|out_buf, out_len| unsafe { botan_mac_name(self.obj, out_buf as *mut c_char, out_len) }) } /// Return information about the key lengths supported by this object pub fn key_spec(&self) -> Result { KeySpec::new(self.min_keylen, self.max_keylen, self.mod_keylen) } /// Return the output length of the authentication code, in bytes /// # Examples /// ``` /// let hmac = botan::MsgAuthCode::new("HMAC(SHA-256)").unwrap(); /// assert_eq!(hmac.output_length().unwrap(), 32); /// ``` pub fn output_length(&self) -> Result { Ok(self.output_length) } /// Set the key for the authentication code object /// # Examples /// ``` /// let mut hmac = botan::MsgAuthCode::new("HMAC(SHA-256)").unwrap(); /// hmac.set_key(&vec![0; 16]).unwrap(); /// ``` pub fn set_key(&mut self, key: &[u8]) -> Result<()> { botan_call!(botan_mac_set_key, self.obj, key.as_ptr(), key.len()) } #[cfg(feature = "botan3")] /// Set the nonce for the authentication code object /// /// Only a few MACs support this; currently only GMAC /// /// # Examples /// ``` /// let mut gmac = botan::MsgAuthCode::new("GMAC(AES-128)").unwrap(); /// gmac.set_key(&vec![0; 16]).unwrap(); /// gmac.set_nonce(&vec![0; 12]).unwrap(); /// ``` pub fn set_nonce(&mut self, nonce: &[u8]) -> Result<()> { botan_call!(botan_mac_set_nonce, self.obj, nonce.as_ptr(), nonce.len()) } /// Add data to a MAC computation, may be called many times /// /// # Examples /// ``` /// let mut hmac = botan::MsgAuthCode::new("HMAC(SHA-256)").unwrap(); /// assert!(hmac.update(&[23]).is_err()); // key not set yet /// hmac.set_key(&vec![0; 16]).unwrap(); /// hmac.update(&[1,2,3]).unwrap(); /// hmac.update(&[4,5,6]).unwrap(); /// ``` pub fn update(&mut self, data: &[u8]) -> Result<()> { botan_call!(botan_mac_update, self.obj, data.as_ptr(), data.len()) } /// Complete a MAC computation, after which the object is reset to /// MAC a new message with the same key. /// /// # Examples /// ``` /// let mut hmac = botan::MsgAuthCode::new("HMAC(SHA-256)").unwrap(); /// assert!(hmac.update(&[23]).is_err()); // key not set yet /// hmac.set_key(&vec![0; 16]).unwrap(); /// hmac.update(&[1,2,3]).unwrap(); /// hmac.update(&[4,5,6]).unwrap(); /// let mac = hmac.finish().unwrap(); /// ``` pub fn finish(&mut self) -> Result> { let mut output = vec![0; self.output_length]; botan_call!(botan_mac_final, self.obj, output.as_mut_ptr())?; Ok(output) } /// Clear the MAC key /// /// # Examples /// ``` /// let mut hmac = botan::MsgAuthCode::new("HMAC(SHA-256)").unwrap(); /// hmac.set_key(&vec![0; 16]).unwrap(); /// hmac.update(&[1,2,3]).unwrap(); /// hmac.clear().unwrap(); /// assert!(hmac.update(&[23]).is_err()); // key not set anymore /// ``` pub fn clear(&mut self) -> Result<()> { botan_call!(botan_mac_clear, self.obj) } } botan-0.10.7/src/memutils.rs000064400000000000000000000050461046102023000140150ustar 00000000000000use crate::utils::*; use botan_sys::*; /// Const time comparison /// /// Compare two arrays without leaking side channel information #[must_use] pub fn const_time_compare(a: &[T], b: &[T]) -> bool { if a.len() != b.len() { return false; } let bytes = mem::size_of_val(a); let rc = unsafe { botan_constant_time_compare(a.as_ptr() as *const u8, b.as_ptr() as *const u8, bytes) }; rc == 0 } /// Securely zeroize memory /// /// Write zeros to the array (eg to clear out a key) in a way that is /// unlikely to be removed by the compiler. pub fn scrub_mem(a: &mut [T]) { let bytes = mem::size_of_val(a); unsafe { botan_scrub_mem(a.as_mut_ptr() as *mut c_void, bytes) }; } /// Hex encode some data pub fn hex_encode(x: &[u8]) -> Result { let flags = 0u32; let mut output = vec![0u8; x.len() * 2]; botan_call!( botan_hex_encode, x.as_ptr(), x.len(), output.as_mut_ptr() as *mut c_char, flags )?; String::from_utf8(output).map_err(Error::conversion_error) } /// Hex decode some data pub fn hex_decode(x: &str) -> Result> { let mut output = vec![0u8; x.len() / 2]; let mut output_len = output.len(); let input = make_cstr(x)?; botan_call!( botan_hex_decode, input.as_ptr(), x.len(), output.as_mut_ptr(), &mut output_len )?; output.resize(output_len, 0); Ok(output) } /// Base64 encode some data /// /// # Examples /// /// ``` /// assert_eq!(botan::base64_encode(&[97,98,99,100,101,102]).unwrap(), "YWJjZGVm"); /// assert_eq!(botan::base64_encode(&[0x5A, 0x16, 0xAD, 0x4E, 0x17, 0x87, 0x79, 0xC9]).unwrap(), "WhatTheHeck="); /// ``` pub fn base64_encode(x: &[u8]) -> Result { let b64_len = 1 + ((x.len() + 2) / 3) * 4; call_botan_ffi_returning_string(b64_len, &|out_buf, out_len| unsafe { botan_base64_encode(x.as_ptr(), x.len(), out_buf as *mut c_char, out_len) }) } /// Base64 decode some data /// /// # Examples /// /// ``` /// assert!(botan::base64_decode("ThisIsInvalid!").is_err()); /// assert_eq!(botan::base64_decode("YWJjZGVm").unwrap(), b"abcdef"); /// ``` pub fn base64_decode(x: &str) -> Result> { // Hard to provide a decent lower bound as it is possible x includes // lots of spaces or trailing = padding chars let bin_len = x.len(); let input = make_cstr(x)?; call_botan_ffi_returning_vec_u8(bin_len, &|out_buf, out_len| unsafe { botan_base64_decode(input.as_ptr(), x.len(), out_buf, out_len) }) } botan-0.10.7/src/mp.rs000064400000000000000000000421221046102023000125660ustar 00000000000000use crate::utils::*; use botan_sys::*; use crate::rng::RandomNumberGenerator; use core::cmp::{Eq, Ord, Ordering}; use core::fmt; use core::str::FromStr; use core::ops::{ Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }; /// A big integer type #[allow(clippy::upper_case_acronyms)] pub struct MPI { obj: botan_mp_t, } unsafe impl Sync for MPI {} unsafe impl Send for MPI {} botan_impl_drop!(MPI, botan_mp_destroy); impl Clone for MPI { fn clone(&self) -> MPI { self.duplicate().expect("copying MPI object failed") } } impl MPI { pub(crate) fn handle(&self) -> botan_mp_t { self.obj } pub(crate) fn handle_mut(&mut self) -> botan_mp_t { self.obj } /// Crate a new (zero-valued) MPI pub fn new() -> Result { let obj = botan_init!(botan_mp_init)?; Ok(MPI { obj }) } /// Crate a new MPI setting value from an array of bytes (big-endian) pub fn new_from_bytes(val: &[u8]) -> Result { let mut mpi = MPI::new()?; mpi.set_bytes(val)?; Ok(mpi) } /// Crate a new MPI setting value from a i32 pub fn new_from_i32(val: i32) -> Result { let mut mpi = MPI::new()?; mpi.set_i32(val)?; Ok(mpi) } /// Crate a new MPI setting value from a u32 pub fn new_from_u32(val: u32) -> Result { let mut mpi = MPI::new()?; mpi.mp_add_u32_assign(val)?; Ok(mpi) } /// Crate a new MPI duplicating the value of self pub fn duplicate(&self) -> Result { let mpi = MPI::new()?; botan_call!(botan_mp_set_from_mp, mpi.obj, self.obj)?; Ok(mpi) } /// Set self to value specified with an i32 pub fn set_i32(&mut self, val: i32) -> Result<()> { botan_call!(botan_mp_set_from_int, self.obj, val) } /// Set self to value specified with a string pub fn set_str(&mut self, val: &str) -> Result<()> { let cstr = make_cstr(val)?; botan_call!(botan_mp_set_from_str, self.obj, cstr.as_ptr()) } /// Set self to value specified with an array of bytes (big-endian) pub fn set_bytes(&mut self, val: &[u8]) -> Result<()> { botan_call!(botan_mp_from_bin, self.obj, val.as_ptr(), val.len()) } /// Set self to zero pub fn clear(&mut self) -> Result<()> { botan_call!(botan_mp_clear, self.obj) } /// Set a specific bit of self pub fn set_bit(&mut self, bit: usize) -> Result<()> { botan_call!(botan_mp_set_bit, self.obj, bit) } /// Clear a specific bit of self pub fn clear_bit(&mut self, bit: usize) -> Result<()> { botan_call!(botan_mp_clear_bit, self.obj, bit) } /// Return the value of a bit in self pub fn get_bit(&self, bit: usize) -> Result { botan_bool_in_rc!(botan_mp_get_bit, self.obj, bit) } /// Randomize self to an integer of specified bit size pub fn randomize(&mut self, rng: &mut RandomNumberGenerator, bits: usize) -> Result<()> { botan_call!(botan_mp_rand_bits, self.obj, rng.handle(), bits) } /// Randomize self to an integer within specified range pub fn random_range( &mut self, rng: &mut RandomNumberGenerator, lower: &MPI, upper: &MPI, ) -> Result<()> { botan_call!( botan_mp_rand_range, self.obj, rng.handle(), lower.handle(), upper.handle() ) } /// Return value of self as decimal string pub fn to_string(&self) -> Result { let bit_count = self.bit_count()? as f64; let log_base = core::f64::consts::LOG2_10; let bn_digits = 2 + (bit_count / log_base) as usize; call_botan_ffi_returning_string(bn_digits, &|out_buf, out_len| unsafe { botan_mp_to_str(self.obj, 10, out_buf as *mut c_char, out_len) }) } /// Return value of self as hex string pub fn to_hex(&self) -> Result { let byte_count = self.byte_count()?; let mut r = call_botan_ffi_returning_string(byte_count * 2 + 1, &|out_buf, out_len| unsafe { botan_mp_to_str(self.obj, 16, out_buf as *mut c_char, out_len) })?; if crate::Version::major_version() >= 3 { Ok(r.split_off(2)) // remove leading 0x } else { Ok(r) } } /// Return value of self as a byte array (big endian) pub fn to_bin(&self) -> Result> { let bytes = self.byte_count()?; let mut output = vec![0; bytes]; botan_call!(botan_mp_to_bin, self.obj, output.as_mut_ptr())?; Ok(output) } /// Return number of significant bits pub fn bit_count(&self) -> Result { let mut bits = 0; botan_call!(botan_mp_num_bits, self.obj, &mut bits)?; Ok(bits) } /// Return number of significant bytes pub fn byte_count(&self) -> Result { let mut bytes = 0; botan_call!(botan_mp_num_bytes, self.obj, &mut bytes)?; Ok(bytes) } /// Return self as a u32, if it fits pub fn to_u32(&self) -> Result { let mut val = 0; botan_call!(botan_mp_to_uint32, self.obj, &mut val)?; Ok(val) } /// Return true if self is an integer >= 0 pub fn is_positive(&self) -> Result { botan_bool_in_rc!(botan_mp_is_positive, self.obj) } /// Return true if self is an integer < 0 pub fn is_negative(&self) -> Result { botan_bool_in_rc!(botan_mp_is_negative, self.obj) } /// Return true if self is an integer == 0 pub fn is_zero(&self) -> Result { botan_bool_in_rc!(botan_mp_is_zero, self.obj) } /// Return true if self is odd pub fn is_odd(&self) -> Result { botan_bool_in_rc!(botan_mp_is_odd, self.obj) } /// Return true if self is even pub fn is_even(&self) -> Result { botan_bool_in_rc!(botan_mp_is_even, self.obj) } /// Return true if self equals other pub fn equals(&self, other: &MPI) -> Result { botan_bool_in_rc!(botan_mp_equal, self.obj, other.obj) } /// Compare self with other pub fn compare(&self, other: &MPI) -> Result { let mut r = 0; botan_call!(botan_mp_cmp, &mut r, self.obj, other.obj)?; match r { -1 => Ok(Ordering::Less), 0 => Ok(Ordering::Equal), 1 => Ok(Ordering::Greater), r => Err(Error::with_message( ErrorType::ConversionError, format!("Unexpected botan_mp_cmp result {r}"), )), } } /// Flip the sign of self pub fn flip_sign(&mut self) -> Result<()> { botan_call!(botan_mp_flip_sign, self.obj) } /// Addition operator pub fn mp_add(&self, other: &MPI) -> Result { let r = MPI::new()?; botan_call!(botan_mp_add, r.obj, self.obj, other.obj)?; Ok(r) } /// Addition operator, assignment version pub fn mp_add_assign(&mut self, other: &MPI) -> Result<()> { botan_call!(botan_mp_add, self.obj, self.obj, other.obj) } /// Addition operator pub fn mp_add_u32(&self, other: u32) -> Result { let r = MPI::new()?; botan_call!(botan_mp_add_u32, r.obj, self.obj, other)?; Ok(r) } /// Addition operator, assignment version pub fn mp_add_u32_assign(&mut self, other: u32) -> Result<()> { botan_call!(botan_mp_add_u32, self.obj, self.obj, other) } /// Subtraction operator pub fn mp_sub(&self, other: &MPI) -> Result { let r = MPI::new()?; botan_call!(botan_mp_sub, r.obj, self.obj, other.obj)?; Ok(r) } /// Subtraction operator, assignment version pub fn mp_sub_assign(&mut self, other: &MPI) -> Result<()> { botan_call!(botan_mp_sub, self.obj, self.obj, other.obj) } /// Subtraction operator pub fn mp_sub_u32(&self, other: u32) -> Result { let r = MPI::new()?; botan_call!(botan_mp_sub_u32, r.obj, self.obj, other)?; Ok(r) } /// Subtraction operator, assignment version pub fn mp_sub_u32_assign(&mut self, other: u32) -> Result<()> { botan_call!(botan_mp_sub_u32, self.obj, self.obj, other) } /// Multiplication operator pub fn mp_mul(&self, other: &MPI) -> Result { let r = MPI::new()?; botan_call!(botan_mp_mul, r.obj, self.obj, other.obj)?; Ok(r) } /// Multiplication operator, assignment version pub fn mp_mul_assign(&mut self, other: &MPI) -> Result<()> { botan_call!(botan_mp_mul, self.obj, self.obj, other.obj) } /// Bitwise left shift pub fn mp_shl(&self, shift: usize) -> Result { let r = MPI::new()?; botan_call!(botan_mp_lshift, r.obj, self.obj, shift)?; Ok(r) } /// Bitwise left shift, assignment version pub fn mp_shl_assign(&mut self, shift: usize) -> Result<()> { botan_call!(botan_mp_lshift, self.obj, self.obj, shift) } /// Bitwise right shift pub fn mp_shr(&self, shift: usize) -> Result { let r = MPI::new()?; botan_call!(botan_mp_rshift, r.obj, self.obj, shift)?; Ok(r) } /// Bitwise right shift, assignment version pub fn mp_shr_assign(&mut self, shift: usize) -> Result<()> { botan_call!(botan_mp_rshift, self.obj, self.obj, shift) } /// Division/modulo operator pub fn divrem(&self, z: &MPI) -> Result<(MPI, MPI)> { let q = MPI::new()?; let r = MPI::new()?; botan_call!(botan_mp_div, q.obj, r.obj, self.obj, z.obj)?; Ok((q, r)) } /// Swap two MPI values pub fn swap(&mut self, other: &mut MPI) -> Result<()> { botan_call!(botan_mp_swap, self.obj, other.handle_mut()) } /// Perform a primality test on self /// /// # Examples /// /// ``` /// use core::str::FromStr; /// let n = botan::MPI::from_str("1111111111111111111").unwrap(); /// let mut rng = botan::RandomNumberGenerator::new_system().unwrap(); /// assert!(n.is_prime(&mut rng, 128).unwrap()); /// ``` pub fn is_prime(&self, rng: &mut RandomNumberGenerator, test_prob: usize) -> Result { botan_bool_in_rc!(botan_mp_is_prime, self.obj, rng.handle(), test_prob) } /// Return the greatest common divisor of x and y /// # Examples /// /// ``` /// use core::str::FromStr; /// let x = botan::MPI::from_str("1111111111111111").unwrap(); /// let y = botan::MPI::from_str("111111111111").unwrap(); /// assert_eq!(botan::MPI::gcd(&x, &y).unwrap(), botan::MPI::from_str("1111").unwrap()); /// ``` pub fn gcd(x: &MPI, y: &MPI) -> Result { let r = MPI::new()?; botan_call!(botan_mp_gcd, r.obj, x.obj, y.obj)?; Ok(r) } /// Return the inverse of x modulo m, or 0 if gcd(x,m) > 1 pub fn modular_inverse(x: &MPI, m: &MPI) -> Result { let r = MPI::new()?; botan_call!(botan_mp_mod_inverse, r.obj, x.obj, m.obj)?; Ok(r) } /// Return (x^e) mod m pub fn powmod(x: &MPI, e: &MPI, m: &MPI) -> Result { let r = MPI::new()?; botan_call!(botan_mp_powmod, r.obj, x.obj, e.obj, m.obj)?; Ok(r) } } impl PartialOrd for MPI { fn partial_cmp(&self, other: &MPI) -> Option { Some(self.cmp(other)) } } impl PartialEq for MPI { fn eq(&self, other: &MPI) -> bool { self.cmp(other) == Ordering::Equal } } impl Eq for MPI {} impl Ord for MPI { fn cmp(&self, other: &MPI) -> Ordering { self.compare(other).expect("botan_mp_cmp should succeed") } } impl FromStr for MPI { type Err = Error; fn from_str(s: &str) -> Result { let mut mpi = MPI::new()?; mpi.set_str(s)?; Ok(mpi) } } impl fmt::Debug for MPI { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let s = self.to_string().map_err(|_| fmt::Error)?; if crate::Version::major_version() >= 3 { write!(formatter, "{s}") } else { let is_positive = self.is_positive().map_err(|_| fmt::Error)?; formatter.pad_integral(is_positive, "", &s) } } } impl fmt::Display for MPI { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "{self:?}") } } impl fmt::UpperHex for MPI { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let s = self.to_hex().map_err(|_| fmt::Error)?; let is_positive = self.is_positive().map_err(|_| fmt::Error)?; formatter.pad_integral(is_positive, "0x", &s) } } impl fmt::LowerHex for MPI { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let mut s = self.to_hex().map_err(|_| fmt::Error)?; let is_positive = self.is_positive().map_err(|_| fmt::Error)?; s.make_ascii_lowercase(); formatter.pad_integral(is_positive, "0x", &s) } } impl<'a> Add<&'a MPI> for MPI { type Output = MPI; fn add(mut self, other: &MPI) -> MPI { self.mp_add_assign(other) .expect("MPI::mp_add_assign succeeded"); self } } impl<'a, 'b> Add<&'a MPI> for &'b MPI { type Output = MPI; fn add(self, other: &MPI) -> MPI { self.mp_add(other).expect("MPI::mp_add succeeded") } } impl Add for MPI { type Output = MPI; fn add(mut self, other: u32) -> MPI { self.mp_add_u32_assign(other) .expect("MPI::mp_add_u32_assign succeeded"); self } } impl<'a> Add for &'a MPI { type Output = MPI; fn add(self, other: u32) -> MPI { self.mp_add_u32(other).expect("MPI::mp_add_u32 succeeded") } } impl<'a> AddAssign<&'a MPI> for MPI { fn add_assign(&mut self, other: &MPI) { self.mp_add_assign(other) .expect("MPI::mp_add_assign succeeded"); } } impl AddAssign for MPI { fn add_assign(&mut self, other: u32) { self.mp_add_u32_assign(other) .expect("MPI::mp_add_u32_assign succeeded"); } } impl<'a> Sub<&'a MPI> for MPI { type Output = MPI; fn sub(mut self, other: &MPI) -> MPI { self.mp_sub_assign(other) .expect("MPI::mp_sub_assign succeeded"); self } } impl<'a, 'b> Sub<&'a MPI> for &'b MPI { type Output = MPI; fn sub(self, other: &MPI) -> MPI { self.mp_sub(other).expect("MPI::mp_sub succeeded") } } impl Sub for MPI { type Output = MPI; fn sub(mut self, other: u32) -> MPI { self.mp_sub_u32_assign(other) .expect("MPI::mp_sub_u32_assign succeeded"); self } } impl<'a> Sub for &'a MPI { type Output = MPI; fn sub(self, other: u32) -> MPI { self.mp_sub_u32(other).expect("MPI::mp_sub_u32 succeeded") } } impl<'a> SubAssign<&'a MPI> for MPI { fn sub_assign(&mut self, other: &MPI) { self.mp_sub_assign(other) .expect("MPI::mp_sub_assign succeeded"); } } impl SubAssign for MPI { fn sub_assign(&mut self, other: u32) { self.mp_sub_u32_assign(other) .expect("MPI::mp_sub_u32_assign succeeded"); } } impl<'a> Mul<&'a MPI> for MPI { type Output = MPI; fn mul(mut self, other: &MPI) -> MPI { self.mp_mul_assign(other) .expect("MPI::mp_mul_assign succeeded"); self } } impl<'a, 'b> Mul<&'a MPI> for &'b MPI { type Output = MPI; fn mul(self, other: &MPI) -> MPI { self.mp_mul(other).expect("MPI::mp_mul succeeded") } } impl<'a> MulAssign<&'a MPI> for MPI { fn mul_assign(&mut self, other: &MPI) { self.mp_mul_assign(other) .expect("MPI::mp_mul_assign succeeded"); } } impl<'a, 'b> Div<&'b MPI> for &'a MPI { type Output = MPI; #[inline] fn div(self, other: &MPI) -> MPI { let (q, _r) = self.divrem(other).expect("MPI::divrem succeeded"); q } } impl<'a> DivAssign<&'a MPI> for MPI { fn div_assign(&mut self, other: &'a MPI) { *self = &*self / other; } } impl<'a, 'b> Rem<&'b MPI> for &'a MPI { type Output = MPI; fn rem(self, other: &MPI) -> MPI { let (_q, r) = self.divrem(other).expect("MPI::divrem succeeded"); r } } impl<'a> RemAssign<&'a MPI> for MPI { fn rem_assign(&mut self, other: &MPI) { *self = &*self % other; } } impl<'a> Shl for &'a MPI { type Output = MPI; fn shl(self, shift: usize) -> MPI { self.mp_shl(shift).expect("MPI::mp_shl succeeded") } } impl ShlAssign for MPI { fn shl_assign(&mut self, shift: usize) { self.mp_shl_assign(shift) .expect("MPI::mp_shl_assign succeeded") } } impl<'a> Shr for &'a MPI { type Output = MPI; fn shr(self, shift: usize) -> MPI { self.mp_shr(shift).expect("MPI::mp_shr succeeded") } } impl ShrAssign for MPI { fn shr_assign(&mut self, shift: usize) { self.mp_shr_assign(shift) .expect("MPI::mp_shr_assign succeeded") } } impl Neg for MPI { type Output = MPI; fn neg(mut self) -> MPI { self.flip_sign().expect("MPI::flip_sign succeeded"); self } } botan-0.10.7/src/otp.rs000064400000000000000000000060551046102023000127610ustar 00000000000000use crate::utils::*; use botan_sys::*; /// Generate or check HOTP tokens #[derive(Debug)] #[allow(clippy::upper_case_acronyms)] pub struct HOTP { obj: botan_hotp_t, } unsafe impl Sync for HOTP {} unsafe impl Send for HOTP {} botan_impl_drop!(HOTP, botan_hotp_destroy); /// Generate or check TOTP tokens #[derive(Debug)] #[allow(clippy::upper_case_acronyms)] pub struct TOTP { obj: botan_totp_t, } unsafe impl Sync for TOTP {} unsafe impl Send for TOTP {} botan_impl_drop!(TOTP, botan_totp_destroy); impl HOTP { /// Instantiate a new HOTP instance with the given parameters /// /// # Examples /// /// ``` /// let hotp = botan::HOTP::new(&[1,2,3,4], "SHA-1", 6); /// ``` pub fn new(key: &[u8], hash_algo: &str, digits: usize) -> Result { let hash_algo = make_cstr(hash_algo)?; let obj = botan_init!( botan_hotp_init, key.as_ptr(), key.len(), hash_algo.as_ptr(), digits )?; Ok(HOTP { obj }) } /// Generate an HOTP code pub fn generate(&self, counter: u64) -> Result { let mut code = 0; botan_call!(botan_hotp_generate, self.obj, &mut code, counter)?; Ok(code) } /// Check an HOTP code pub fn check(&self, code: u32, counter: u64) -> Result { let cmp_code = self.generate(counter)?; Ok(cmp_code == code) } /// Check an HOTP code, allowing counter resync pub fn check_with_resync( &self, code: u32, counter: u64, resync_range: usize, ) -> Result<(bool, u64)> { let mut new_ctr = 0; let res = botan_bool_in_rc!( botan_hotp_check, self.obj, &mut new_ctr, code, counter, resync_range )?; // Return value is inverted if !res { Ok((true, new_ctr)) } else { Ok((false, counter)) } } } impl TOTP { /// Instantiate a new TOTP instance with the given parameters /// /// # Examples /// /// ``` /// let totp = botan::TOTP::new(&[1,2,3,4], "SHA-1", 6, 30); /// ``` pub fn new(key: &[u8], hash_algo: &str, digits: usize, time_step: usize) -> Result { let hash_algo = make_cstr(hash_algo)?; let obj = botan_init!( botan_totp_init, key.as_ptr(), key.len(), hash_algo.as_ptr(), digits, time_step )?; Ok(TOTP { obj }) } /// Generate an TOTP code pub fn generate(&self, timestamp: u64) -> Result { let mut code = 0; botan_call!(botan_totp_generate, self.obj, &mut code, timestamp)?; Ok(code) } /// Check an TOTP code pub fn check(&self, code: u32, timestamp: u64, allowed_drift: usize) -> Result { // Return value is inverted Ok(!botan_bool_in_rc!( botan_totp_check, self.obj, code, timestamp, allowed_drift )?) } } botan-0.10.7/src/pbkdf.rs000064400000000000000000000065311046102023000132440ustar 00000000000000use crate::utils::*; use botan_sys::*; /// Password based key derivation function /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let salt = rng.read(10).unwrap(); /// let key = botan::derive_key_from_password("Scrypt", 32, "passphrase", &salt, 8192, 8, 1).unwrap(); /// assert_eq!(key.len(), 32); /// ``` pub fn derive_key_from_password( algo: &str, out_len: usize, passphrase: &str, salt: &[u8], param1: usize, param2: usize, param3: usize, ) -> Result> { let algo = make_cstr(algo)?; let passphrase = make_cstr(passphrase)?; let mut output = vec![0u8; out_len]; botan_call!( botan_pwdhash, algo.as_ptr(), param1, param2, param3, output.as_mut_ptr(), output.len(), passphrase.as_ptr(), 0, salt.as_ptr(), salt.len() )?; Ok(output) } /// Password based key derivation function, timed variant /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let salt = rng.read(10).unwrap(); /// let msec = 30; /// let (key,r,p,n) = botan::derive_key_from_password_timed("Scrypt", 32, "passphrase", &salt, msec).unwrap(); /// assert_eq!(key.len(), 32); /// let key2 = botan::derive_key_from_password("Scrypt", 32, "passphrase", &salt, n, r, p).unwrap(); /// assert_eq!(key, key2); /// ``` pub fn derive_key_from_password_timed( algo: &str, out_len: usize, passphrase: &str, salt: &[u8], msec: u32, ) -> Result<(Vec, usize, usize, usize)> { let algo = make_cstr(algo)?; let passphrase = make_cstr(passphrase)?; let mut output = vec![0u8; out_len]; let mut param1 = 0; let mut param2 = 0; let mut param3 = 0; botan_call!( botan_pwdhash_timed, algo.as_ptr(), msec, &mut param1, &mut param2, &mut param3, output.as_mut_ptr(), output.len(), passphrase.as_ptr(), 0, salt.as_ptr(), salt.len() )?; Ok((output, param1, param2, param3)) } /// Password based key derivation function /// /// Note currently only PBKDF2 is supported by this interface. /// For PBKDF2, iterations >= 100000 is recommended. /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let salt = rng.read(10).unwrap(); /// let key = botan::pbkdf("PBKDF2(SHA-256)", 32, "passphrase", &salt, 10000).unwrap(); /// assert_eq!(key.len(), 32); /// ``` pub fn pbkdf( algo: &str, out_len: usize, passphrase: &str, salt: &[u8], iterations: usize, ) -> Result> { derive_key_from_password(algo, out_len, passphrase, salt, iterations, 0, 0) } /// Scrypt key derivation /// /// The n, r, p parameters control how much time and memory is used. /// As of 2018, n = 32768, r = 8, p = 1 seems sufficient. /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let salt = rng.read(10).unwrap(); /// let n = 32768; /// let r = 8; /// let p = 1; /// let key = botan::scrypt(32, "passphrase", &salt, n, r, p).unwrap(); /// assert_eq!(key.len(), 32); /// ``` pub fn scrypt( out_len: usize, passphrase: &str, salt: &[u8], n: usize, r: usize, p: usize, ) -> Result> { derive_key_from_password("Scrypt", out_len, passphrase, salt, n, r, p) } botan-0.10.7/src/pk_ops.rs000064400000000000000000000163571046102023000134600ustar 00000000000000use crate::utils::*; use botan_sys::*; use crate::pubkey::{Privkey, Pubkey}; use crate::rng::RandomNumberGenerator; #[derive(Debug)] /// An object that can generate signatures /// /// # Examples /// /// ``` /// let mut rng = botan::RandomNumberGenerator::new_system().unwrap(); /// let rsa = botan::Privkey::create("RSA", "2048", &mut rng).unwrap(); /// let mut signer = botan::Signer::new(&rsa, "PKCS1v15(SHA-256)").unwrap(); /// signer.update(&[1,2,3]).unwrap(); /// let signature = signer.finish(&mut rng).unwrap(); /// ``` pub struct Signer { obj: botan_pk_op_sign_t, sig_len: usize, } unsafe impl Sync for Signer {} unsafe impl Send for Signer {} botan_impl_drop!(Signer, botan_pk_op_sign_destroy); impl Signer { /// Create a new signature operator pub fn new(key: &Privkey, padding: &str) -> Result { let padding = make_cstr(padding)?; let obj = botan_init!( botan_pk_op_sign_create, key.handle(), padding.as_ptr(), 0u32 )?; let sig_len = botan_usize!(botan_pk_op_sign_output_length, obj)?; Ok(Signer { obj, sig_len }) } /// Add more bytes of the message that will be signed pub fn update(&mut self, data: &[u8]) -> Result<()> { botan_call!(botan_pk_op_sign_update, self.obj, data.as_ptr(), data.len()) } /// Complete and return the signature pub fn finish(&mut self, rng: &mut RandomNumberGenerator) -> Result> { let rng_handle = rng.handle(); call_botan_ffi_returning_vec_u8(self.sig_len, &|out_buf, out_len| unsafe { botan_pk_op_sign_finish(self.obj, rng_handle, out_buf, out_len) }) } } #[derive(Debug)] /// An object that can perform public key decryption pub struct Decryptor { obj: botan_pk_op_decrypt_t, } unsafe impl Sync for Decryptor {} unsafe impl Send for Decryptor {} botan_impl_drop!(Decryptor, botan_pk_op_decrypt_destroy); impl Decryptor { /// Create a new decryption object pub fn new(key: &Privkey, padding: &str) -> Result { let padding = make_cstr(padding)?; let obj = botan_init!( botan_pk_op_decrypt_create, key.handle(), padding.as_ptr(), 0u32 )?; Ok(Decryptor { obj }) } /// Decrypt a message pub fn decrypt(&mut self, ctext: &[u8]) -> Result> { let mut ptext_len = 0; botan_call!( botan_pk_op_decrypt_output_length, self.obj, ctext.len(), &mut ptext_len )?; call_botan_ffi_returning_vec_u8(ptext_len, &|out_buf, out_len| unsafe { botan_pk_op_decrypt(self.obj, out_buf, out_len, ctext.as_ptr(), ctext.len()) }) } } #[derive(Debug)] /// An object that can perform public key signature verification pub struct Verifier { obj: botan_pk_op_verify_t, } unsafe impl Sync for Verifier {} unsafe impl Send for Verifier {} botan_impl_drop!(Verifier, botan_pk_op_verify_destroy); impl Verifier { /// Create a new verifier object pub fn new(key: &Pubkey, padding: &str) -> Result { let padding = make_cstr(padding)?; let obj = botan_init!( botan_pk_op_verify_create, key.handle(), padding.as_ptr(), 0u32 )?; Ok(Verifier { obj }) } /// Create a new verifier object pub fn new_with_der_formatted_signatures(key: &Pubkey, padding: &str) -> Result { let padding = make_cstr(padding)?; let obj = botan_init!( botan_pk_op_verify_create, key.handle(), padding.as_ptr(), 1u32 )?; Ok(Verifier { obj }) } /// Add more bytes of the message that will be verified pub fn update(&mut self, data: &[u8]) -> Result<()> { botan_call!( botan_pk_op_verify_update, self.obj, data.as_ptr(), data.len() ) } /// Verify the provided signature and return true if valid pub fn finish(&mut self, signature: &[u8]) -> Result { match unsafe { botan_pk_op_verify_finish(self.obj, signature.as_ptr(), signature.len()) } { 0 => Ok(true), BOTAN_FFI_INVALID_VERIFIER => Ok(false), e => Err(Error::from_rc(e)), } } } #[derive(Debug)] /// An object that performs public key encryption /// /// # Examples /// /// ``` /// let mut rng = botan::RandomNumberGenerator::new_system().unwrap(); /// let rsa = botan::Privkey::create("RSA", "2048", &mut rng).unwrap(); /// let rsa_pub = rsa.pubkey().unwrap(); /// let mut enc = botan::Encryptor::new(&rsa_pub, "OAEP(SHA-256)").unwrap(); /// let ctext = enc.encrypt(&[1,2,3], &mut rng).unwrap(); /// ``` pub struct Encryptor { obj: botan_pk_op_encrypt_t, } unsafe impl Sync for Encryptor {} unsafe impl Send for Encryptor {} botan_impl_drop!(Encryptor, botan_pk_op_encrypt_destroy); impl Encryptor { /// Create a new public key encryptor object pub fn new(key: &Pubkey, padding: &str) -> Result { let padding = make_cstr(padding)?; let obj = botan_init!( botan_pk_op_encrypt_create, key.handle(), padding.as_ptr(), 0u32 )?; Ok(Encryptor { obj }) } /// Encrypt a message using the provided public key pub fn encrypt(&mut self, ptext: &[u8], rng: &mut RandomNumberGenerator) -> Result> { let mut ctext_len = 0; botan_call!( botan_pk_op_encrypt_output_length, self.obj, ptext.len(), &mut ctext_len )?; let rng_handle = rng.handle(); call_botan_ffi_returning_vec_u8(ctext_len, &|out_buf, out_len| unsafe { botan_pk_op_encrypt( self.obj, rng_handle, out_buf, out_len, ptext.as_ptr(), ptext.len(), ) }) } } #[derive(Debug)] /// An object that performs key agreement pub struct KeyAgreement { obj: botan_pk_op_ka_t, } unsafe impl Sync for KeyAgreement {} unsafe impl Send for KeyAgreement {} botan_impl_drop!(KeyAgreement, botan_pk_op_key_agreement_destroy); impl KeyAgreement { /// Create a new key agreement operator pub fn new(key: &Privkey, kdf: &str) -> Result { let kdf = make_cstr(kdf)?; let obj = botan_init!( botan_pk_op_key_agreement_create, key.handle(), kdf.as_ptr(), 0u32 )?; Ok(KeyAgreement { obj }) } /// Perform key agreement operation pub fn agree( &mut self, requested_output: usize, counterparty_key: &[u8], salt: &[u8], ) -> Result> { let mut ka_len = requested_output; if ka_len == 0 { ka_len = botan_usize!(botan_pk_op_key_agreement_size, self.obj)?; } call_botan_ffi_returning_vec_u8(ka_len, &|out_buf, out_len| unsafe { botan_pk_op_key_agreement( self.obj, out_buf, out_len, counterparty_key.as_ptr(), counterparty_key.len(), salt.as_ptr(), salt.len(), ) }) } } botan-0.10.7/src/pk_ops_kem.rs000064400000000000000000000076271046102023000143140ustar 00000000000000use crate::utils::*; use botan_sys::*; use crate::pubkey::{Privkey, Pubkey}; use crate::rng::RandomNumberGenerator; #[derive(Debug)] /// An object that can perform key encapsulation pub struct KeyEncapsulation { obj: botan_pk_op_kem_encrypt_t, encap_length: usize, } unsafe impl Sync for KeyEncapsulation {} unsafe impl Send for KeyEncapsulation {} botan_impl_drop!(KeyEncapsulation, botan_pk_op_kem_encrypt_destroy); impl KeyEncapsulation { /// Create a KeyEncapsulation operation pub fn new(key: &Pubkey, kdf: &str) -> Result { let kdf = make_cstr(kdf)?; let obj = botan_init!(botan_pk_op_kem_encrypt_create, key.handle(), kdf.as_ptr())?; let encap_length = botan_usize!(botan_pk_op_kem_encrypt_encapsulated_key_length, obj)?; Ok(Self { obj, encap_length }) } /// Return the shared key length pub fn shared_key_length(&self, desired_shared_key_length: usize) -> Result { let mut val = 0; let rc = unsafe { botan_pk_op_kem_encrypt_shared_key_length(self.obj, desired_shared_key_length, &mut val) }; if rc != 0 { Err(Error::from_rc(rc)) } else { Ok(val) } } /// Create a new encapsulated key pub fn create_shared_key( &self, rng: &mut RandomNumberGenerator, salt: &[u8], desired_key_len: usize, ) -> Result<(Vec, Vec)> { let mut shared_key_len = self.shared_key_length(desired_key_len)?; let mut shared_key = vec![0; shared_key_len]; let mut encap_key_len = self.encap_length; let mut encap_key = vec![0; encap_key_len]; let rc = unsafe { botan_pk_op_kem_encrypt_create_shared_key( self.obj, rng.handle(), salt.as_ptr(), salt.len(), desired_key_len, shared_key.as_mut_ptr(), &mut shared_key_len, encap_key.as_mut_ptr(), &mut encap_key_len, ) }; if rc != 0 { return Err(Error::from_rc(rc)); } Ok((shared_key, encap_key)) } } #[derive(Debug)] /// An object that can perform key decapsulation pub struct KeyDecapsulation { obj: botan_pk_op_kem_decrypt_t, } unsafe impl Sync for KeyDecapsulation {} unsafe impl Send for KeyDecapsulation {} botan_impl_drop!(KeyDecapsulation, botan_pk_op_kem_decrypt_destroy); impl KeyDecapsulation { /// Create a KeyDecapsulation operation pub fn new(key: &Privkey, kdf: &str) -> Result { let kdf = make_cstr(kdf)?; let obj = botan_init!(botan_pk_op_kem_decrypt_create, key.handle(), kdf.as_ptr())?; Ok(Self { obj }) } /// Return the shared key length pub fn shared_key_length(&self, desired_shared_key_length: usize) -> Result { let mut val = 0; let rc = unsafe { botan_pk_op_kem_decrypt_shared_key_length(self.obj, desired_shared_key_length, &mut val) }; if rc != 0 { Err(Error::from_rc(rc)) } else { Ok(val) } } /// Decrypt an encapsulated key pub fn decrypt_shared_key( &self, encapsulated_key: &[u8], salt: &[u8], desired_key_len: usize, ) -> Result> { let mut shared_key_len = self.shared_key_length(desired_key_len)?; let mut shared_key = vec![0; shared_key_len]; let rc = unsafe { botan_pk_op_kem_decrypt_shared_key( self.obj, salt.as_ptr(), salt.len(), encapsulated_key.as_ptr(), encapsulated_key.len(), desired_key_len, shared_key.as_mut_ptr(), &mut shared_key_len, ) }; if rc != 0 { return Err(Error::from_rc(rc)); } Ok(shared_key) } } botan-0.10.7/src/pubkey.rs000064400000000000000000000522361046102023000134600ustar 00000000000000use crate::utils::*; use botan_sys::*; use crate::mp::MPI; use crate::pk_ops::*; use crate::rng::RandomNumberGenerator; #[derive(Debug)] /// A public key object pub struct Pubkey { obj: botan_pubkey_t, } unsafe impl Sync for Pubkey {} unsafe impl Send for Pubkey {} botan_impl_drop!(Pubkey, botan_pubkey_destroy); #[derive(Debug)] /// A private key object pub struct Privkey { obj: botan_privkey_t, } unsafe impl Sync for Privkey {} unsafe impl Send for Privkey {} botan_impl_drop!(Privkey, botan_privkey_destroy); impl Privkey { pub(crate) fn handle(&self) -> botan_privkey_t { self.obj } /// Create a new private key /// pub fn create(alg: &str, params: &str, rng: &mut RandomNumberGenerator) -> Result { let obj = botan_init!( botan_privkey_create, make_cstr(alg)?.as_ptr(), make_cstr(params)?.as_ptr(), rng.handle() )?; Ok(Privkey { obj }) } /// Create a new ElGamal private key with a random group pub fn create_elgamal( p_bits: usize, q_bits: usize, rng: &mut RandomNumberGenerator, ) -> Result { let obj = botan_init!(botan_privkey_create_elgamal, rng.handle(), p_bits, q_bits)?; Ok(Self { obj }) } /// Create a new DSA private key with a random group pub fn create_dsa( p_bits: usize, q_bits: usize, rng: &mut RandomNumberGenerator, ) -> Result { let obj = botan_init!(botan_privkey_create_dsa, rng.handle(), p_bits, q_bits)?; Ok(Self { obj }) } /// Load an RSA private key (p,q,e) /// /// # Examples /// /// ``` /// use std::str::FromStr; /// let p = botan::MPI::from_str("289698020102256958291511331409682926199").unwrap(); /// let q = botan::MPI::from_str("293497288893125842977275290547344412783").unwrap(); /// let e = botan::MPI::from_str("65537").unwrap(); /// let rsa = botan::Privkey::load_rsa(&p, &q, &e).unwrap(); /// ``` pub fn load_rsa(p: &MPI, q: &MPI, e: &MPI) -> Result { let obj = botan_init!(botan_privkey_load_rsa, p.handle(), q.handle(), e.handle())?; Ok(Privkey { obj }) } /// Load an Ed25519 private key /// /// # Examples /// /// ``` /// let v = vec![0x42; 32]; /// let key = botan::Privkey::load_ed25519(&v).unwrap(); /// ``` pub fn load_ed25519(key: &[u8]) -> Result { let obj = botan_init!(botan_privkey_load_ed25519, key.as_ptr())?; Ok(Privkey { obj }) } /// Load an X25519 private key /// /// # Examples /// /// ``` /// let v = vec![0x42; 32]; /// let key = botan::Privkey::load_x25519(&v).unwrap(); /// ``` pub fn load_x25519(key: &[u8]) -> Result { let obj = botan_init!(botan_privkey_load_x25519, key.as_ptr())?; Ok(Privkey { obj }) } /// Load a PKCS#1 encoded RSA private key pub fn load_rsa_pkcs1(pkcs1: &[u8]) -> Result { let obj = botan_init!(botan_privkey_load_rsa_pkcs1, pkcs1.as_ptr(), pkcs1.len())?; Ok(Privkey { obj }) } /// Load an DH private key (p,g,x) pub fn load_dh(p: &MPI, g: &MPI, x: &MPI) -> Result { let obj = botan_init!(botan_privkey_load_dh, p.handle(), g.handle(), x.handle())?; Ok(Privkey { obj }) } /// Load an DSA private key (p,q,g,x) pub fn load_dsa(p: &MPI, q: &MPI, g: &MPI, x: &MPI) -> Result { let obj = botan_init!( botan_privkey_load_dsa, p.handle(), q.handle(), g.handle(), x.handle() )?; Ok(Privkey { obj }) } /// Load an ElGamal private key (p,g,x) pub fn load_elgamal(p: &MPI, g: &MPI, x: &MPI) -> Result { let obj = botan_init!( botan_privkey_load_elgamal, p.handle(), g.handle(), x.handle() )?; Ok(Privkey { obj }) } /// Load an ECDSA private key with specified curve and secret scalar pub fn load_ecdsa(s: &MPI, curve_name: &str) -> Result { let curve_name = make_cstr(curve_name)?; let obj = botan_init!(botan_privkey_load_ecdsa, s.handle(), curve_name.as_ptr())?; Ok(Privkey { obj }) } /// Load an ECDH private key with specified curve and secret scalar pub fn load_ecdh(s: &MPI, curve_name: &str) -> Result { let curve_name = make_cstr(curve_name)?; let obj = botan_init!(botan_privkey_load_ecdh, s.handle(), curve_name.as_ptr())?; Ok(Privkey { obj }) } /// Load DER bytes as an unencrypted PKCS#8 private key pub fn load_der(der: &[u8]) -> Result { let obj = botan_init!( botan_privkey_load, ptr::null_mut(), der.as_ptr(), der.len(), ptr::null() )?; Ok(Privkey { obj }) } /// Load PEM string as an unencrypted PKCS#8 private key pub fn load_pem(pem: &str) -> Result { let cpem = make_cstr(pem)?; let obj = botan_init!( botan_privkey_load, ptr::null_mut(), cpem.as_ptr() as *const u8, pem.len(), ptr::null() )?; Ok(Privkey { obj }) } /// Load DER bytes as an encrypted PKCS#8 private key pub fn load_encrypted_der(der: &[u8], passphrase: &str) -> Result { let passphrase = make_cstr(passphrase)?; let obj = botan_init!( botan_privkey_load, ptr::null_mut(), der.as_ptr(), der.len(), passphrase.as_ptr() )?; Ok(Privkey { obj }) } /// Load PEM string as an encrypted PKCS#8 private key pub fn load_encrypted_pem(pem: &str, passphrase: &str) -> Result { let passphrase = make_cstr(passphrase)?; let cpem = make_cstr(pem)?; let obj = botan_init!( botan_privkey_load, ptr::null_mut(), cpem.as_ptr() as *const u8, pem.len(), passphrase.as_ptr() )?; Ok(Privkey { obj }) } /// Check if the key seems to be valid pub fn check_key(&self, rng: &mut RandomNumberGenerator) -> Result { let flags = 1u32; let rc = unsafe { botan_privkey_check_key(self.obj, rng.handle(), flags) }; if rc == 0 { Ok(true) } else if rc == -1 { Ok(false) } else { Err(Error::from_rc(rc)) } } /// Return the public key associated with this private key pub fn pubkey(&self) -> Result { let obj = botan_init!(botan_privkey_export_pubkey, self.obj)?; Ok(Pubkey { obj }) } /// Return the name of the algorithm pub fn algo_name(&self) -> Result { call_botan_ffi_returning_string(32, &|out_buf, out_len| unsafe { botan_privkey_algo_name(self.obj, out_buf as *mut c_char, out_len) }) } /// DER encode the key (unencrypted) pub fn der_encode(&self) -> Result> { #[cfg(feature = "botan3")] { call_botan_ffi_viewing_vec_u8(&|ctx, cb| unsafe { botan_privkey_view_der(self.obj, ctx, cb) }) } #[cfg(not(feature = "botan3"))] { call_botan_ffi_returning_vec_u8(4096, &|out_buf, out_len| unsafe { botan_privkey_export(self.obj, out_buf, out_len, 0u32) }) } } /// PEM encode the private key (unencrypted) pub fn pem_encode(&self) -> Result { #[cfg(feature = "botan3")] { call_botan_ffi_viewing_str_fn(&|ctx, cb| unsafe { botan_privkey_view_pem(self.obj, ctx, cb) }) } #[cfg(not(feature = "botan3"))] { call_botan_ffi_returning_string(4096, &|out_buf, out_len| unsafe { botan_privkey_export(self.obj, out_buf, out_len, 1u32) }) } } /// DER encode the key (encrypted) pub fn der_encode_encrypted( &self, passphrase: &str, rng: &mut RandomNumberGenerator, ) -> Result> { let iterations = 150_000; self.der_encode_encrypted_with_options( passphrase, "AES-256/CBC", "SHA-512", iterations, rng, ) } /// DER encode the key (encrypted), specifying cipher/hash options pub fn der_encode_encrypted_with_options( &self, passphrase: &str, cipher: &str, pbkdf: &str, pbkdf_iter: usize, rng: &mut RandomNumberGenerator, ) -> Result> { let passphrase = make_cstr(passphrase)?; let cipher = make_cstr(cipher)?; let pbkdf = make_cstr(pbkdf)?; let rng_handle = rng.handle(); #[cfg(feature = "botan3")] { call_botan_ffi_viewing_vec_u8(&|ctx, cb| unsafe { botan_privkey_view_encrypted_der( self.obj, rng_handle, passphrase.as_ptr(), cipher.as_ptr(), pbkdf.as_ptr(), pbkdf_iter, ctx, cb, ) }) } #[cfg(not(feature = "botan3"))] { call_botan_ffi_returning_vec_u8(4096, &|out_buf, out_len| unsafe { botan_privkey_export_encrypted_pbkdf_iter( self.obj, out_buf, out_len, rng_handle, passphrase.as_ptr(), pbkdf_iter, cipher.as_ptr(), pbkdf.as_ptr(), 0u32, ) }) } } /// PEM encode the key (encrypted) pub fn pem_encode_encrypted( &self, passphrase: &str, rng: &mut RandomNumberGenerator, ) -> Result { let iterations = 150_000; self.pem_encode_encrypted_with_options( passphrase, "AES-256/CBC", "SHA-512", iterations, rng, ) } /// PEM encode the key (encrypted), specifying cipher/hash options pub fn pem_encode_encrypted_with_options( &self, passphrase: &str, cipher: &str, pbkdf: &str, pbkdf_iter: usize, rng: &mut RandomNumberGenerator, ) -> Result { let passphrase = make_cstr(passphrase)?; let cipher = make_cstr(cipher)?; let pbkdf = make_cstr(pbkdf)?; let rng_handle = rng.handle(); #[cfg(feature = "botan3")] { call_botan_ffi_viewing_str_fn(&|ctx, cb| unsafe { botan_privkey_view_encrypted_pem( self.obj, rng_handle, passphrase.as_ptr(), cipher.as_ptr(), pbkdf.as_ptr(), pbkdf_iter, ctx, cb, ) }) } #[cfg(not(feature = "botan3"))] { call_botan_ffi_returning_string(4096, &|out_buf, out_len| unsafe { botan_privkey_export_encrypted_pbkdf_iter( self.obj, out_buf, out_len, rng_handle, passphrase.as_ptr(), pbkdf_iter, cipher.as_ptr(), pbkdf.as_ptr(), 1u32, ) }) } } /// Return the key agrement key, only valid for DH/ECDH pub fn key_agreement_key(&self) -> Result> { #[cfg(feature = "botan3")] { call_botan_ffi_viewing_vec_u8(&|ctx, cb| unsafe { botan_pk_op_key_agreement_view_public(self.obj, ctx, cb) }) } #[cfg(not(feature = "botan3"))] { let ka_key_len = 512; // fixme call_botan_ffi_returning_vec_u8(ka_key_len, &|out_buf, out_len| unsafe { botan_pk_op_key_agreement_export_public(self.obj, out_buf, out_len) }) } } /// Get a value for the private key /// The which parameter selects a field which is algorithm specific pub fn get_field(&self, which: &str) -> Result { let which = make_cstr(which)?; let r = MPI::new()?; botan_call!( botan_privkey_get_field, r.handle(), self.obj, which.as_ptr() )?; Ok(r) } /// Get the public and private key associated with this key pub fn get_ed25519_key(&self) -> Result<(Vec, Vec)> { let mut out = vec![0; 64]; botan_call!( botan_privkey_ed25519_get_privkey, self.obj, out.as_mut_ptr() )?; let pubkey = out.split_off(32); Ok((pubkey, out)) } /// Get the X25519 private key pub fn get_x25519_key(&self) -> Result> { let mut out = vec![0; 32]; botan_call!(botan_privkey_x25519_get_privkey, self.obj, out.as_mut_ptr())?; Ok(out) } /// Sign a message using the specified padding method pub fn sign( &self, message: &[u8], padding: &str, rng: &mut RandomNumberGenerator, ) -> Result> { let mut signer = Signer::new(self, padding)?; signer.update(message)?; signer.finish(rng) } /// Decrypt a message that was encrypted using the specified padding method pub fn decrypt(&self, ctext: &[u8], padding: &str) -> Result> { let mut decryptor = Decryptor::new(self, padding)?; decryptor.decrypt(ctext) } /// Perform key agreement pub fn agree( &self, other_key: &[u8], output_len: usize, salt: &[u8], kdf: &str, ) -> Result> { let mut op = KeyAgreement::new(self, kdf)?; op.agree(output_len, other_key, salt) } } impl Pubkey { pub(crate) fn from_handle(obj: botan_pubkey_t) -> Pubkey { Pubkey { obj } } pub(crate) fn handle(&self) -> botan_pubkey_t { self.obj } /// Load a DER encoded public key pub fn load_der(der: &[u8]) -> Result { let obj = botan_init!(botan_pubkey_load, der.as_ptr(), der.len())?; Ok(Pubkey { obj }) } /// Load a PEM encoded public key pub fn load_pem(pem: &str) -> Result { let obj = botan_init!( botan_pubkey_load, make_cstr(pem)?.as_ptr() as *const u8, pem.len() )?; Ok(Pubkey { obj }) } /// Load an RSA public key (n,e) pub fn load_rsa(n: &MPI, e: &MPI) -> Result { let obj = botan_init!(botan_pubkey_load_rsa, n.handle(), e.handle())?; Ok(Pubkey { obj }) } /// Load an DH public key (p,g,y) pub fn load_dh(p: &MPI, g: &MPI, y: &MPI) -> Result { let obj = botan_init!(botan_pubkey_load_dh, p.handle(), g.handle(), y.handle())?; Ok(Pubkey { obj }) } /// Load an DSA public key (p,q,g,y) pub fn load_dsa(p: &MPI, q: &MPI, g: &MPI, y: &MPI) -> Result { let obj = botan_init!( botan_pubkey_load_dsa, p.handle(), q.handle(), g.handle(), y.handle() )?; Ok(Pubkey { obj }) } /// Load an ElGamal public key (p,g,y) pub fn load_elgamal(p: &MPI, g: &MPI, y: &MPI) -> Result { let obj = botan_init!( botan_pubkey_load_elgamal, p.handle(), g.handle(), y.handle() )?; Ok(Pubkey { obj }) } /// Load an ECDSA public key (x,y) for the specified curve pub fn load_ecdsa(pub_x: &MPI, pub_y: &MPI, curve_name: &str) -> Result { let curve_name = make_cstr(curve_name)?; let obj = botan_init!( botan_pubkey_load_ecdsa, pub_x.handle(), pub_y.handle(), curve_name.as_ptr() )?; Ok(Pubkey { obj }) } /// Load an ECDH public key (x,y) for the specified curve pub fn load_ecdh(pub_x: &MPI, pub_y: &MPI, curve_name: &str) -> Result { let curve_name = make_cstr(curve_name)?; let obj = botan_init!( botan_pubkey_load_ecdh, pub_x.handle(), pub_y.handle(), curve_name.as_ptr() )?; Ok(Pubkey { obj }) } /// Load an Ed25519 public key pub fn load_ed25519(key: &[u8]) -> Result { let obj = botan_init!(botan_pubkey_load_ed25519, key.as_ptr())?; Ok(Pubkey { obj }) } /// Load an X25519 key pub fn load_x25519(key: &[u8]) -> Result { let obj = botan_init!(botan_pubkey_load_x25519, key.as_ptr())?; Ok(Pubkey { obj }) } /// Return estimated bit strength of this key pub fn estimated_strength(&self) -> Result { botan_usize!(botan_pubkey_estimated_strength, self.obj) } /// Check key for problems pub fn check_key(&self, rng: &mut RandomNumberGenerator) -> Result { let flags = 1u32; let rc = unsafe { botan_pubkey_check_key(self.obj, rng.handle(), flags) }; if rc == 0 { Ok(true) } else if rc == -1 { Ok(false) } else { Err(Error::from_rc(rc)) } } /// Return hash of the public key data pub fn fingerprint(&self, hash: &str) -> Result> { let hash = make_cstr(hash)?; let fprint_len = 64; // hashes > 512 bits are rare call_botan_ffi_returning_vec_u8(fprint_len, &|out_buf, out_len| unsafe { botan_pubkey_fingerprint(self.obj, hash.as_ptr(), out_buf, out_len) }) } /// DER encode this public key pub fn der_encode(&self) -> Result> { #[cfg(feature = "botan3")] { call_botan_ffi_viewing_vec_u8(&|ctx, cb| unsafe { botan_pubkey_view_der(self.obj, ctx, cb) }) } #[cfg(not(feature = "botan3"))] { let der_len = 4096; // fixme call_botan_ffi_returning_vec_u8(der_len, &|out_buf, out_len| unsafe { botan_pubkey_export(self.obj, out_buf, out_len, 0u32) }) } } /// PEM encode this public key pub fn pem_encode(&self) -> Result { #[cfg(feature = "botan3")] { call_botan_ffi_viewing_str_fn(&|ctx, cb| unsafe { botan_pubkey_view_pem(self.obj, ctx, cb) }) } #[cfg(not(feature = "botan3"))] { let pem_len = 4096; // fixme call_botan_ffi_returning_string(pem_len, &|out_buf, out_len| unsafe { botan_pubkey_export(self.obj, out_buf, out_len, 1u32) }) } } #[cfg(feature = "botan3")] /// Return the encoded elliptic curve point associated with this key /// /// Only valid for EC based keys pub fn ec_public_point(&self) -> Result> { call_botan_ffi_viewing_vec_u8(&|ctx, cb| unsafe { botan_pubkey_view_ec_public_point(self.obj, ctx, cb) }) } /// Return the name of the algorithm pub fn algo_name(&self) -> Result { call_botan_ffi_returning_string(32, &|out_buf, out_len| unsafe { botan_pubkey_algo_name(self.obj, out_buf as *mut c_char, out_len) }) } /// Get a value for the public key /// The which parameter selects a field which is algorithm specific pub fn get_field(&self, which: &str) -> Result { let which = make_cstr(which)?; let r = MPI::new()?; botan_call!(botan_pubkey_get_field, r.handle(), self.obj, which.as_ptr())?; Ok(r) } /// Return the 32-byte Ed25519 public key pub fn get_ed25519_key(&self) -> Result> { let mut out = vec![0; 32]; botan_call!(botan_pubkey_ed25519_get_pubkey, self.obj, out.as_mut_ptr())?; Ok(out) } /// Get the X25519 public key pub fn get_x25519_key(&self) -> Result> { let mut out = vec![0; 32]; botan_call!(botan_pubkey_x25519_get_pubkey, self.obj, out.as_mut_ptr())?; Ok(out) } /// Encrypt a message using the specified padding method pub fn encrypt( &self, message: &[u8], padding: &str, rng: &mut RandomNumberGenerator, ) -> Result> { let mut op = Encryptor::new(self, padding)?; op.encrypt(message, rng) } /// Verify a message that was signed using the specified padding method pub fn verify(&self, message: &[u8], signature: &[u8], padding: &str) -> Result { let mut op = Verifier::new(self, padding)?; op.update(message)?; op.finish(signature) } } /// Return the identifier used for PKCS1 v1.5 signatures for the specified hash pub fn pkcs_hash_id(hash_algo: &str) -> Result> { let hash_algo = make_cstr(hash_algo)?; let id_len = 32; // largest currently is 20 bytes call_botan_ffi_returning_vec_u8(id_len, &|out_buf, out_len| unsafe { botan_pkcs_hash_id(hash_algo.as_ptr(), out_buf, out_len) }) } botan-0.10.7/src/rng.rs000064400000000000000000000070001046102023000127340ustar 00000000000000use crate::utils::*; use botan_sys::*; #[derive(Debug)] /// A cryptographic random number generator pub struct RandomNumberGenerator { obj: botan_rng_t, } unsafe impl Sync for RandomNumberGenerator {} unsafe impl Send for RandomNumberGenerator {} botan_impl_drop!(RandomNumberGenerator, botan_rng_destroy); impl RandomNumberGenerator { fn new_of_type(typ: &str) -> Result { let typ = make_cstr(typ)?; let obj = botan_init!(botan_rng_init, typ.as_ptr())?; Ok(RandomNumberGenerator { obj }) } pub(crate) fn handle(&mut self) -> botan_rng_t { self.obj } /// Create a new userspace RNG object /// /// # Examples /// ``` /// let userspace_rng = botan::RandomNumberGenerator::new_userspace().unwrap(); /// ``` pub fn new_userspace() -> Result { RandomNumberGenerator::new_of_type("user") } /// Create a new reference to the system PRNG /// /// # Examples /// ``` /// let system_rng = botan::RandomNumberGenerator::new_system().unwrap(); /// ``` pub fn new_system() -> Result { RandomNumberGenerator::new_of_type("system") } /// Create a new reference to an RNG of some arbitrary type /// /// # Examples /// ``` /// let a_rng = botan::RandomNumberGenerator::new().unwrap(); /// ``` pub fn new() -> Result { RandomNumberGenerator::new_userspace() } /// Read bytes from an RNG /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let output = rng.read(32).unwrap(); /// assert_eq!(output.len(), 32); /// ``` pub fn read(&mut self, len: usize) -> Result> { let mut result = vec![0; len]; self.fill(&mut result)?; Ok(result) } /// Store bytes from the RNG into the passed slice /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// let mut output = vec![0; 32]; /// rng.fill(&mut output).unwrap(); /// ``` pub fn fill(&mut self, out: &mut [u8]) -> Result<()> { botan_call!(botan_rng_get, self.obj, out.as_mut_ptr(), out.len()) } /// Attempt to reseed the RNG by unspecified means /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new().unwrap(); /// rng.reseed(256).unwrap(); /// ``` pub fn reseed(&mut self, bits: usize) -> Result<()> { botan_call!(botan_rng_reseed, self.obj, bits) } /// Attempt to reseed the RNG by getting data from source RNG /// /// # Examples /// ``` /// let mut system_rng = botan::RandomNumberGenerator::new_system().unwrap(); /// let mut rng = botan::RandomNumberGenerator::new_userspace().unwrap(); /// rng.reseed_from_rng(&mut system_rng, 256).unwrap(); /// ``` pub fn reseed_from_rng( &mut self, source: &mut RandomNumberGenerator, bits: usize, ) -> Result<()> { botan_call!(botan_rng_reseed_from_rng, self.obj, source.handle(), bits) } /// Add some seed material to the RNG /// /// # Examples /// ``` /// let mut rng = botan::RandomNumberGenerator::new_userspace().unwrap(); /// let my_seed = vec![0x42, 0x6F, 0x62]; /// rng.add_entropy(&my_seed); /// ``` pub fn add_entropy(&mut self, seed: &[u8]) -> Result<()> { botan_call!(botan_rng_add_entropy, self.obj, seed.as_ptr(), seed.len()) } } botan-0.10.7/src/utils.rs000064400000000000000000000311271046102023000133150ustar 00000000000000use botan_sys::*; use core::fmt; #[cfg(feature = "no-std")] pub(crate) use alloc::{borrow::ToOwned, string::String, string::ToString, vec::Vec}; #[cfg(feature = "no-std")] pub(crate) use alloc::ffi::CString; #[cfg(feature = "no-std")] pub(crate) use core::ffi::CStr; #[cfg(not(feature = "no-std"))] pub(crate) use std::ffi::{CStr, CString}; pub(crate) use botan_sys::ffi_types::{c_char, c_int, c_void}; pub(crate) use core::mem; pub(crate) use core::ptr; /// The result of calling an operation on the library pub type Result = ::core::result::Result; pub(crate) fn make_cstr(input: &str) -> Result { let cstr = CString::new(input).map_err(Error::conversion_error)?; Ok(cstr) } pub(crate) fn call_botan_ffi_returning_vec_u8( initial_size: usize, cb: &dyn Fn(*mut u8, *mut usize) -> c_int, ) -> Result> { let mut output = vec![0; initial_size]; let mut out_len = output.len(); let rc = cb(output.as_mut_ptr(), &mut out_len); if rc == 0 { assert!(out_len <= output.len()); output.resize(out_len, 0); return Ok(output); } else if rc != BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE { return Err(Error::from_rc(rc)); } output.resize(out_len, 0); let rc = cb(output.as_mut_ptr(), &mut out_len); if rc != 0 { return Err(Error::from_rc(rc)); } output.resize(out_len, 0); Ok(output) } #[cfg(feature = "botan3")] pub(crate) mod view { use super::*; type FfiViewBinaryFn = extern "C" fn(*mut c_void, *const u8, usize) -> c_int; extern "C" fn botan_ffi_view_u8_fn(ctx: *mut c_void, buf: *const u8, len: usize) -> c_int { if ctx == core::ptr::null_mut() || buf == core::ptr::null_mut() { return BOTAN_FFI_ERROR_NULL_POINTER; } let vec = ctx as *mut Vec; unsafe { let data = core::slice::from_raw_parts(buf, len); (*vec).clear(); (*vec).extend_from_slice(&data); } 0 } pub(crate) fn call_botan_ffi_viewing_vec_u8( cb: &dyn Fn(*mut c_void, FfiViewBinaryFn) -> c_int, ) -> Result> { let mut view_ctx: Vec = vec![]; let rc = cb( &mut view_ctx as *mut Vec as *mut _, botan_ffi_view_u8_fn, ); if rc != 0 { return Err(Error::from_rc(rc)); } Ok(view_ctx) } type FfiViewStrFn = extern "C" fn(*mut c_void, *const c_char, usize) -> c_int; extern "C" fn botan_ffi_view_str_fn(ctx: *mut c_void, buf: *const c_char, len: usize) -> c_int { if ctx == core::ptr::null_mut() || buf == core::ptr::null_mut() { return BOTAN_FFI_ERROR_NULL_POINTER; } if len == 0 { return BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR; } let str = ctx as *mut String; let data = unsafe { core::slice::from_raw_parts(buf as *const u8, len - 1) }; let mut vec = Vec::new(); vec.extend_from_slice(&data); match String::from_utf8(vec) { Ok(decoded) => { unsafe { *str = decoded; } 0 } Err(_) => BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR, } } pub(crate) fn call_botan_ffi_viewing_str_fn( cb: &dyn Fn(*mut c_void, FfiViewStrFn) -> c_int, ) -> Result { let mut view_ctx = String::new(); let rc = cb( &mut view_ctx as *mut String as *mut _, botan_ffi_view_str_fn, ); if rc != 0 { return Err(Error::from_rc(rc)); } Ok(view_ctx) } } #[cfg(feature = "botan3")] pub(crate) use crate::view::*; fn cstr_slice_to_str(raw_cstr: &[u8]) -> Result { let cstr = CStr::from_bytes_with_nul(raw_cstr).map_err(Error::conversion_error)?; Ok(cstr.to_str().map_err(Error::conversion_error)?.to_owned()) } #[cfg(feature = "botan3")] unsafe fn cstr_to_str(raw_cstr: *const c_char) -> Result { let cstr = CStr::from_ptr(raw_cstr); Ok(cstr.to_str().map_err(Error::conversion_error)?.to_owned()) } pub(crate) fn call_botan_ffi_returning_string( initial_size: usize, cb: &dyn Fn(*mut u8, *mut usize) -> c_int, ) -> Result { let v = call_botan_ffi_returning_vec_u8(initial_size, cb)?; cstr_slice_to_str(&v) } /// The library error type #[derive(Clone, Debug, PartialEq, Eq)] pub struct Error { err_type: ErrorType, message: Option, } impl Error { /// Return the general type of the error pub fn error_type(&self) -> ErrorType { self.err_type } /// Return an optional message specific to the error /// /// This is only available in Botan 3.x; with older versions /// it will always be None pub fn error_message(&self) -> Option<&str> { self.message.as_deref() } pub(crate) fn from_rc(rc: c_int) -> Self { let err_type = ErrorType::from(rc); #[cfg(feature = "botan3")] let message = { let cptr = unsafe { botan_sys::botan_error_last_exception_message() }; match unsafe { cstr_to_str(cptr) } { Err(_) => None, Ok(s) if s.len() > 0 => Some(s), Ok(_) => None, } }; #[cfg(not(feature = "botan3"))] let message = None; Self { err_type, message } } pub(crate) fn with_message(err_type: ErrorType, message: String) -> Self { Self { err_type, message: Some(message), } } #[cfg(not(feature = "no-std"))] pub(crate) fn conversion_error(e: T) -> Self { Self { err_type: ErrorType::ConversionError, message: Some(format!("{e}")), } } // Hack to deal with missing std::error::Error in no-std #[cfg(feature = "no-std")] pub(crate) fn conversion_error(e: T) -> Self { Self { err_type: ErrorType::ConversionError, message: Some(format!("{}", e)), } } } impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match &self.message { Some(m) => write!(f, "{} ({})", self.err_type, m), None => write!(f, "{}", self.err_type), } } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] /// Possible error categories pub enum ErrorType { /// A provided authentication code was incorrect BadAuthCode, /// A bad flag was passed to the library BadFlag, /// An invalid parameter was provided to the library BadParameter, /// An exception was thrown while processing this request ExceptionThrown, /// There was insufficient buffer space to write the output InsufficientBufferSpace, /// Converting a string to UTF8 failed StringConversionError, /// An internal error occurred (this is a bug in the library) InternalError, /// Something about the input was invalid InvalidInput, /// An invalid object was provided to the library InvalidObject, /// An object was invoked in a way that is invalid for its current state InvalidObjectState, /// A verifier was incorrect InvalidVerifier, /// An key of invalid length was provided InvalidKeyLength, /// An object was invoked without the key being set KeyNotSet, /// Some functionality is not implemented in the current library version NotImplemented, /// A null pointer was incorrectly provided NullPointer, /// Memory exhaustion OutOfMemory, /// An error occurred while invoking a system API SystemError, /// Some unknown error occurred UnknownError, /// An error occured while converting data to C ConversionError, /// An error occurred in TLS TlsError, /// An error occurred during an HTTP transaction HttpError, } impl fmt::Display for ErrorType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let msg = match self { Self::BadAuthCode => "A provided authentication code was incorrect", Self::BadFlag => "A bad flag was passed to the library", Self::BadParameter => "An invalid parameter was provided to the library", Self::ExceptionThrown => "An exception was thrown while processing this request", Self::StringConversionError => "Error converting a string into UTF-8", Self::InsufficientBufferSpace => { "There was insufficient buffer space to write the output" } Self::InternalError => "An internal error occurred (this is a bug in the library)", Self::InvalidInput => "Something about the input was invalid", Self::InvalidObject => "An invalid object was provided to the library", Self::InvalidObjectState => { "An object was invoked in a way that is invalid for its current state" } Self::InvalidVerifier => "A verifier was incorrect", Self::InvalidKeyLength => "An key of invalid length was provided", Self::KeyNotSet => "An object was invoked without the key being set", Self::NotImplemented => { "Some functionality is not implemented in the current library version" } Self::NullPointer => "A null pointer was incorrectly provided", Self::OutOfMemory => "Memory exhaustion", Self::SystemError => "An error occurred while invoking a system API", Self::UnknownError => "Some unknown error occurred", Self::ConversionError => "An error occured while converting data to C", Self::TlsError => "An error occurred in TLS", Self::HttpError => "An error occurred during an HTTP transaction", }; write!(f, "{msg}") } } #[cfg(not(feature = "no-std"))] impl std::error::Error for Error {} impl From for ErrorType { fn from(err: i32) -> Self { match err { BOTAN_FFI_ERROR_BAD_FLAG => Self::BadFlag, BOTAN_FFI_ERROR_BAD_MAC => Self::BadAuthCode, BOTAN_FFI_ERROR_BAD_PARAMETER => Self::BadParameter, BOTAN_FFI_ERROR_EXCEPTION_THROWN => Self::ExceptionThrown, BOTAN_FFI_ERROR_HTTP_ERROR => Self::HttpError, BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE => Self::InsufficientBufferSpace, BOTAN_FFI_ERROR_STRING_CONVERSION_ERROR => Self::StringConversionError, BOTAN_FFI_ERROR_INTERNAL_ERROR => Self::InternalError, BOTAN_FFI_ERROR_INVALID_INPUT => Self::InvalidInput, BOTAN_FFI_ERROR_INVALID_KEY_LENGTH => Self::InvalidKeyLength, BOTAN_FFI_ERROR_INVALID_OBJECT => Self::InvalidObject, BOTAN_FFI_ERROR_INVALID_OBJECT_STATE => Self::InvalidObjectState, BOTAN_FFI_ERROR_KEY_NOT_SET => Self::KeyNotSet, BOTAN_FFI_ERROR_NOT_IMPLEMENTED => Self::NotImplemented, BOTAN_FFI_ERROR_NULL_POINTER => Self::NullPointer, BOTAN_FFI_ERROR_OUT_OF_MEMORY => Self::OutOfMemory, BOTAN_FFI_ERROR_SYSTEM_ERROR => Self::SystemError, BOTAN_FFI_ERROR_TLS_ERROR => Self::TlsError, BOTAN_FFI_ERROR_UNKNOWN_ERROR => Self::UnknownError, BOTAN_FFI_INVALID_VERIFIER => Self::InvalidVerifier, _ => Self::UnknownError, } } } /// Specifies valid keylengths for symmetric ciphers/MACs pub struct KeySpec { min_keylen: usize, max_keylen: usize, mod_keylen: usize, } impl KeySpec { pub(crate) fn new(min_keylen: usize, max_keylen: usize, mod_keylen: usize) -> Result { if min_keylen > max_keylen || mod_keylen == 0 { return Err(Error::with_message( ErrorType::ConversionError, "Bad key spec".to_owned(), )); } Ok(KeySpec { min_keylen, max_keylen, mod_keylen, }) } /// Return true if the specified key length is valid for this object #[must_use] pub fn is_valid_keylength(&self, keylen: usize) -> bool { keylen >= self.min_keylen && keylen <= self.max_keylen && keylen % self.mod_keylen == 0 } /// Return the minimum supported keylength #[must_use] pub fn minimum_keylength(&self) -> usize { self.min_keylen } /// Return the maximum supported keylength #[must_use] pub fn maximum_keylength(&self) -> usize { self.max_keylen } /// Return the required multiple of the keylength /// /// That is each key must be N*keylength_multiple() for some N #[must_use] pub fn keylength_multiple(&self) -> usize { self.mod_keylen } } botan-0.10.7/src/version.rs000064400000000000000000000040501046102023000136350ustar 00000000000000use crate::utils::*; use botan_sys::*; #[derive(Debug)] /// Information about the library version pub struct Version { /// The major version of the library pub major: u32, /// The minor version of the library pub minor: u32, /// The patch version of the library pub patch: u32, /// The release date of the library, as YYYYMMDD, for example /// 2.7.0 has value 20180702. Will be 0 for unreleased versions. pub release_date: u32, /// The version of the FFI API, as a YYYYMMDD field. pub ffi_api: u32, /// A free-form string describing the library version pub string: String, } impl Version { pub(crate) fn major_version() -> u32 { unsafe { botan_version_major() } } /// Read the version information of the currently linked lib pub fn current() -> Result { unsafe { let version_str = CStr::from_ptr(botan_version_string()) .to_str() .map_err(Error::conversion_error)?; Ok(Version { major: botan_version_major(), minor: botan_version_minor(), patch: botan_version_patch(), release_date: botan_version_datestamp(), ffi_api: botan_ffi_api_version(), string: version_str.to_string(), }) } } #[must_use] /// Return true if the current version is at least as high as the /// major and minor numbers passed as arguments pub fn at_least(&self, major: u32, minor: u32) -> bool { self.major > major || (self.major == major && self.minor >= minor) } /// Return true if the specified API version is supported by this /// version of the library. /// /// # Examples /// /// ``` /// assert_eq!(botan::Version::supports_version(42), false); /// assert_eq!(botan::Version::supports_version(20180713), true); /// ``` #[must_use] pub fn supports_version(version: u32) -> bool { let rc = unsafe { botan_ffi_supports_api(version) }; rc == 0 } } botan-0.10.7/src/x509_cert.rs000064400000000000000000000233671046102023000137060ustar 00000000000000use crate::utils::*; use botan_sys::*; use crate::pubkey::Pubkey; #[derive(Debug)] /// X.509 certificate pub struct Certificate { obj: botan_x509_cert_t, } unsafe impl Sync for Certificate {} unsafe impl Send for Certificate {} botan_impl_drop!(Certificate, botan_x509_cert_destroy); impl Clone for Certificate { fn clone(&self) -> Certificate { self.duplicate() .expect("copying X509 cert object succeeded") } } /// Indicates if the certificate key is allowed for a particular usage #[derive(Debug, Copy, Clone)] pub enum CertUsage { /// No particular usage restrictions NoRestrictions, /// Allowed for digital signature DigitalSignature, /// Allowed for "non-repudiation" (whatever that means) NonRepudiation, /// Allowed for enciphering symmetric keys KeyEncipherment, /// Allowed for enciphering plaintext messages DataEncipherment, /// Allowed for key agreement KeyAgreement, /// Allowed for signing certificates CertificateSign, /// Allowed for signing CRLs CrlSign, /// Allowed only for encryption EncipherOnly, /// Allowed only for decryption DecipherOnly, } impl From for CertUsage { fn from(err: X509KeyConstraints) -> CertUsage { match err { X509KeyConstraints::NO_CONSTRAINTS => CertUsage::NoRestrictions, X509KeyConstraints::DIGITAL_SIGNATURE => CertUsage::DigitalSignature, X509KeyConstraints::NON_REPUDIATION => CertUsage::NonRepudiation, X509KeyConstraints::KEY_ENCIPHERMENT => CertUsage::KeyEncipherment, X509KeyConstraints::DATA_ENCIPHERMENT => CertUsage::DataEncipherment, X509KeyConstraints::KEY_AGREEMENT => CertUsage::KeyAgreement, X509KeyConstraints::KEY_CERT_SIGN => CertUsage::CertificateSign, X509KeyConstraints::CRL_SIGN => CertUsage::CrlSign, X509KeyConstraints::ENCIPHER_ONLY => CertUsage::EncipherOnly, X509KeyConstraints::DECIPHER_ONLY => CertUsage::DecipherOnly, } } } impl From for X509KeyConstraints { fn from(err: CertUsage) -> X509KeyConstraints { match err { CertUsage::NoRestrictions => X509KeyConstraints::NO_CONSTRAINTS, CertUsage::DigitalSignature => X509KeyConstraints::DIGITAL_SIGNATURE, CertUsage::NonRepudiation => X509KeyConstraints::NON_REPUDIATION, CertUsage::KeyEncipherment => X509KeyConstraints::KEY_ENCIPHERMENT, CertUsage::DataEncipherment => X509KeyConstraints::DATA_ENCIPHERMENT, CertUsage::KeyAgreement => X509KeyConstraints::KEY_AGREEMENT, CertUsage::CertificateSign => X509KeyConstraints::KEY_CERT_SIGN, CertUsage::CrlSign => X509KeyConstraints::CRL_SIGN, CertUsage::EncipherOnly => X509KeyConstraints::ENCIPHER_ONLY, CertUsage::DecipherOnly => X509KeyConstraints::DECIPHER_ONLY, } } } #[derive(Debug, Copy, Clone)] /// Represents result of cert validation pub enum CertValidationStatus { /// Successful validation, with possible detail code Success(i32), /// Failed validation, with reason code Failed(i32), } impl CertValidationStatus { /// Return true if the validation was successful #[must_use] pub fn success(&self) -> bool { match self { CertValidationStatus::Success(_) => true, CertValidationStatus::Failed(_) => false, } } } impl ToString for CertValidationStatus { fn to_string(&self) -> String { let code = match self { CertValidationStatus::Success(x) => x, CertValidationStatus::Failed(x) => x, }; unsafe { let result_str = botan_x509_cert_validation_status(*code); let cstr = CStr::from_ptr(result_str); cstr.to_str().unwrap().to_owned() } } } impl Certificate { pub(crate) fn handle(&self) -> botan_x509_cert_t { self.obj } /// Load a X.509 certificate from DER or PEM representation pub fn load(data: &[u8]) -> Result { let obj = botan_init!(botan_x509_cert_load, data.as_ptr(), data.len())?; Ok(Certificate { obj }) } /// Read an X.509 certificate from a file pub fn from_file(fsname: &str) -> Result { let fsname = make_cstr(fsname)?; let obj = botan_init!(botan_x509_cert_load_file, fsname.as_ptr())?; Ok(Certificate { obj }) } /// Return the serial number of this certificate pub fn serial_number(&self) -> Result> { let sn_len = 32; // PKIX upper bound is 20 call_botan_ffi_returning_vec_u8(sn_len, &|out_buf, out_len| unsafe { botan_x509_cert_get_serial_number(self.obj, out_buf, out_len) }) } /// Return the fingerprint of this certificate pub fn fingerprint(&self, hash: &str) -> Result> { let fprint_len = 128; let hash = make_cstr(hash)?; call_botan_ffi_returning_vec_u8(fprint_len, &|out_buf, out_len| unsafe { botan_x509_cert_get_fingerprint(self.obj, hash.as_ptr(), out_buf, out_len) }) } /// Duplicate the certificate object /// /// Since certificate objects are immutable, duplication just involves /// atomic incrementing a reference count, so is quite cheap pub fn duplicate(&self) -> Result { let obj = botan_init!(botan_x509_cert_dup, self.obj)?; Ok(Certificate { obj }) } /// Return the authority key id, if set pub fn authority_key_id(&self) -> Result> { let akid_len = 32; call_botan_ffi_returning_vec_u8(akid_len, &|out_buf, out_len| unsafe { botan_x509_cert_get_authority_key_id(self.obj, out_buf, out_len) }) } /// Return the subject key id, if set pub fn subject_key_id(&self) -> Result> { let skid_len = 32; call_botan_ffi_returning_vec_u8(skid_len, &|out_buf, out_len| unsafe { botan_x509_cert_get_subject_key_id(self.obj, out_buf, out_len) }) } /// Return the byte representation of the public key pub fn public_key_bits(&self) -> Result> { #[cfg(not(feature = "botan3"))] { let pk_len = 4096; // fixme call_botan_ffi_returning_vec_u8(pk_len, &|out_buf, out_len| unsafe { botan_x509_cert_get_public_key_bits(self.obj, out_buf, out_len) }) } #[cfg(feature = "botan3")] { call_botan_ffi_viewing_vec_u8(&|ctx, cb| unsafe { botan_x509_cert_view_public_key_bits(self.obj, ctx, cb) }) } } /// Return the public key included in this certificate pub fn public_key(&self) -> Result { let mut key = ptr::null_mut(); botan_call!(botan_x509_cert_get_public_key, self.obj, &mut key)?; Ok(Pubkey::from_handle(key)) } /// Return a free-form string representation of this certificate pub fn to_string(&self) -> Result { #[cfg(not(feature = "botan3"))] { let as_str_len = 4096; call_botan_ffi_returning_string(as_str_len, &|out_buf, out_len| unsafe { botan_x509_cert_to_string(self.obj, out_buf as *mut c_char, out_len) }) } #[cfg(feature = "botan3")] { call_botan_ffi_viewing_str_fn(&|ctx, cb| unsafe { botan_x509_cert_view_as_string(self.obj, ctx, cb) }) } } /// Test if the certificate is allowed for a particular usage pub fn allows_usage(&self, usage: CertUsage) -> Result { let usage_bit: X509KeyConstraints = X509KeyConstraints::from(usage); // Return logic is inverted for this function let r = botan_bool_in_rc!(botan_x509_cert_allowed_usage, self.obj, usage_bit as u32)?; Ok(!r) } /// Attempt to verify this certificate pub fn verify( &self, intermediates: &[&Certificate], trusted: &[&Certificate], trusted_path: Option<&str>, hostname: Option<&str>, reference_time: Option, ) -> Result { let required_key_strength = 110; let trusted_path = make_cstr(trusted_path.unwrap_or(""))?; let hostname = make_cstr(hostname.unwrap_or(""))?; // TODO: more idiomatic way to do this? let mut trusted_h = Vec::new(); for t in trusted { trusted_h.push(t.handle()); } let mut intermediates_h = Vec::new(); for t in intermediates { intermediates_h.push(t.handle()); } // TODO this information is lost :( let mut result = 0; let rc = unsafe { botan_x509_cert_verify( &mut result, self.obj, intermediates_h.as_ptr(), intermediates_h.len(), trusted_h.as_ptr(), trusted_h.len(), trusted_path.as_ptr(), required_key_strength, hostname.as_ptr(), reference_time.unwrap_or(0), ) }; if rc == 0 { Ok(CertValidationStatus::Success(result)) } else if rc == 1 { Ok(CertValidationStatus::Failed(result)) } else { Err(Error::from_rc(rc)) } } /// Return true if the provided hostname is valid for this certificate pub fn matches_hostname(&self, hostname: &str) -> Result { let hostname = make_cstr(hostname)?; let rc = unsafe { botan_x509_cert_hostname_match(self.obj, hostname.as_ptr()) }; if rc == 0 { Ok(true) } else if rc == -1 { Ok(false) } else { Err(Error::from_rc(rc)) } } } botan-0.10.7/src/x509_crl.rs000064400000000000000000000024111046102023000135140ustar 00000000000000use crate::utils::*; use botan_sys::*; #[derive(Debug)] /// X.509 certificate revocation list /// /// Warning: as of the current version you cannot do much useful /// operations with CRLs, due to limitations of the API currently /// exported by the C++ library pub struct CRL { obj: botan_x509_crl_t, } unsafe impl Sync for CRL {} unsafe impl Send for CRL {} botan_impl_drop!(CRL, botan_x509_crl_destroy); impl CRL { /// Load a X.509 CRL from DER or PEM representation pub fn load(data: &[u8]) -> Result { let obj = botan_init!(botan_x509_crl_load, data.as_ptr(), data.len())?; Ok(Self { obj }) } /// Read an X.509 CRL from a file pub fn from_file(fsname: &str) -> Result { let fsname = make_cstr(fsname)?; let obj = botan_init!(botan_x509_crl_load_file, fsname.as_ptr())?; Ok(Self { obj }) } /// Return true if the provided CRL is listed as revoked in the CRL pub fn is_revoked(&self, cert: &crate::Certificate) -> Result { let rc = unsafe { botan_x509_is_revoked(self.obj, cert.handle()) }; // Return value of this function is weird!! match rc { 0 => Ok(true), -1 => Ok(false), _ => Err(Error::from_rc(rc)), } } } botan-0.10.7/src/zfec.rs000064400000000000000000000033051046102023000131010ustar 00000000000000use crate::utils::*; use botan_sys::*; /// Forward Error Correction Encoding pub fn zfec_encode(k: usize, n: usize, input: &[u8]) -> Result>> { let share_size = input.len() / k; let mut outputs = Vec::with_capacity(n); let mut output_ptrs = Vec::with_capacity(n); for _ in 0..n { let mut share = vec![0u8; share_size]; output_ptrs.push(share.as_mut_ptr()); outputs.push(share); } botan_call!( botan_zfec_encode, k, n, input.as_ptr(), input.len(), output_ptrs.as_mut_ptr() )?; Ok(outputs) } /// Forward Error Correction Decoding pub fn zfec_decode( k: usize, n: usize, shares: &[(usize, &[u8])], share_size: usize, ) -> Result> { let mut share_ptrs = Vec::with_capacity(shares.len()); let mut indexes = Vec::with_capacity(shares.len()); for (share_index, share_slice) in shares { indexes.push(*share_index); share_ptrs.push(share_slice.as_ptr()); if share_slice.len() != share_size { return Err(Error::with_message( ErrorType::InvalidInput, "ZFEC decoding requires all shares be the same length".to_string(), )); } } let mut output_buf = vec![0u8; k * share_size]; let output_buf_ptr: *mut u8 = output_buf.as_mut_ptr(); let mut output_ptrs = Vec::with_capacity(k); for i in 0..k { output_ptrs.push(unsafe { output_buf_ptr.add(i * share_size) }); } botan_call!( botan_zfec_decode, k, n, indexes.as_ptr(), share_ptrs.as_ptr(), share_size, output_ptrs.as_mut_ptr() )?; Ok(output_buf) } botan-0.10.7/tests/tests.rs000064400000000000000000001156671046102023000137060ustar 00000000000000extern crate botan; use std::str::FromStr; #[test] fn test_version() -> Result<(), botan::Error> { let version = botan::Version::current()?; /* If we are running against a released version we know it must be at least 2.8 since we require APIs added after the 2.7 release. */ #[cfg(feature = "botan3")] { assert_eq!(version.major, 3); } #[cfg(feature = "vendored")] { assert_eq!(version.major, 3); } #[cfg(not(feature = "botan3"))] { assert!(version.major == 2 || version.major == 3); if version.major == 2 { assert!(version.minor >= 8); } } assert!(version.release_date == 0 || version.release_date >= 20181001); assert!(version.ffi_api >= 20180713); assert!(botan::Version::supports_version(version.ffi_api)); assert!(botan::Version::supports_version(20180713)); assert!(!botan::Version::supports_version(20180712)); assert!(version.at_least(2, 8)); assert!(version.at_least(2, 4)); assert!(version.at_least(1, 100)); Ok(()) } #[test] fn test_hash() -> Result<(), botan::Error> { let mut hash = botan::HashFunction::new("SHA-384")?; assert_eq!(hash.output_length()?, 48); assert_eq!(hash.block_size()?, 128); assert_eq!(hash.algo_name()?, "SHA-384"); assert!(hash.update(&[97, 98]).is_ok()); let mut hash_dup = hash.duplicate()?; assert!(hash.update(&[99]).is_ok()); assert!(hash_dup.update(&[100]).is_ok()); hash.clear()?; hash.update(&[97, 98, 99])?; let digest = hash.finish()?; assert_eq!(botan::hex_encode(&digest)?, "CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED1631A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7"); let digest_dup = hash_dup.finish()?; assert_eq!(botan::hex_encode(&digest_dup)?, "5D15BCEBB965FA77926C23471C96E3A326B363F5F105C3EF17CFD033B9734FA46556F81A26BB3044D2DDA50481325EF7"); let bad_hash = botan::HashFunction::new("BunnyHash9000"); assert_eq!(bad_hash.is_err(), true); assert_eq!( bad_hash.as_ref().unwrap_err().error_type(), botan::ErrorType::NotImplemented ); Ok(()) } #[test] fn test_mac() -> Result<(), botan::Error> { let mut mac = botan::MsgAuthCode::new("HMAC(SHA-384)")?; let key_spec = mac.key_spec()?; assert_eq!(mac.output_length()?, 48); assert_eq!(mac.algo_name()?, "HMAC(SHA-384)"); assert!(key_spec.is_valid_keylength(20)); mac.set_key(&[0xAA; 20])?; mac.update(&[0xDD; 1])?; mac.update(&[0xDD; 29])?; mac.update(&[0xDD; 20])?; let r = mac.finish()?; assert_eq!(botan::hex_encode(&r)?, "88062608D3E6AD8A0AA2ACE014C8A86F0AA635D947AC9FEBE83EF4E55966144B2A5AB39DC13814B94E3AB6E101A34F27"); Ok(()) } #[test] fn test_block_cipher() -> Result<(), botan::Error> { let mut bc = botan::BlockCipher::new("AES-128")?; assert_eq!(bc.algo_name()?, "AES-128"); assert_eq!(bc.block_size()?, 16); let key_spec = bc.key_spec()?; assert!(!key_spec.is_valid_keylength(20)); assert!(key_spec.is_valid_keylength(16)); assert_eq!( bc.set_key(&[0; 32]).unwrap_err().error_type(), botan::ErrorType::InvalidKeyLength ); bc.set_key(&[0; 16])?; let input = vec![0; 16]; let exp_ctext = "66E94BD4EF8A2C3B884CFA59CA342B2E"; let ctext = bc.encrypt_blocks(&input)?; assert_eq!(botan::hex_encode(&ctext)?, exp_ctext); let ptext = bc.decrypt_blocks(&ctext)?; assert_eq!(ptext, input); let mut buf = input.clone(); bc.encrypt_in_place(&mut buf)?; assert_eq!(botan::hex_encode(&buf)?, exp_ctext); bc.decrypt_in_place(&mut buf)?; assert_eq!(buf, input); Ok(()) } #[test] fn test_cipher() -> Result<(), botan::Error> { let mut cipher = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt)?; assert_eq!(cipher.tag_length(), 16); let zero16 = vec![0; 16]; let zero12 = vec![0; 12]; assert!(cipher.set_associated_data(&[1, 2, 3]).is_err()); // trying to set AD before key is set assert_eq!( cipher.set_key(&[0; 42]).unwrap_err().error_type(), botan::ErrorType::InvalidKeyLength ); cipher.set_key(&zero16)?; assert!(cipher.update_granularity() > 0); if let Some(i) = cipher.ideal_update_granularity() { assert!(i >= cipher.update_granularity()); } cipher.set_associated_data(&[1, 2, 3])?; cipher.set_associated_data(&[])?; let ctext = cipher.process(&zero12, &zero16)?; assert_eq!( botan::hex_encode(&ctext)?, "0388DACE60B6A392F328C2B971B2FE78AB6E47D42CEC13BDF53A67B21257BDDF" ); let mut cipher = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Decrypt)?; cipher.set_key(&zero16)?; let ptext = cipher.process(&zero12, &ctext)?; assert_eq!(ptext, zero16); Ok(()) } #[test] fn test_incremental_cipher() -> Result<(), botan::Error> { // This test requires Botan 2.9 or higher to work correctly if !botan::Version::current()?.at_least(2, 9) { return Ok(()); } // Key = 00000000000000000000000000000000 // Nonce = 0AAC82F3E53C2756034F7BD5827C9EDD // In = 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 // Out = 38C21B6430D9A3E4BC6749405765653AE91051E96CE0D076141DD7B515EC150FDB8A65EE988D206C9F64874664CDBF61257FFAE521B9A5EB5B35E3745F4232025B269A6CD7DCFE19153ECF7341CE2C6A6A87F95F2109841350DA3D24EEED4E4E32D2BED880737670FFE8ED76DB890FD72A0076300E50914984A777C9F2BC843977396C602B24E7A045F04D15CD2EAC01AD8808064CFE5A2DC1AE9FFFA4BF0A6F0C07668097DEEB9C5CA5EC1F9A52F96A403B73FEA2DBBF44473D355553EE7FB1B4D6630777DAF67804BE213089B9F78652CE970C582FD813F87FF0ECBACCE1CA46247E20D09F3E0B4EF6BFCD13244C6877F25E6646252CAD6EB7DBBA3476AAAC83BC3285FF70B50D6CDEDC8E5921944A let key = botan::hex_decode("00000000000000000000000000000000")?; let nonce = botan::hex_decode("0AAC82F3E53C2756034F7BD5827C9EDD")?; let input = botan::hex_decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")?; let output = botan::hex_decode("38C21B6430D9A3E4BC6749405765653AE91051E96CE0D076141DD7B515EC150FDB8A65EE988D206C9F64874664CDBF61257FFAE521B9A5EB5B35E3745F4232025B269A6CD7DCFE19153ECF7341CE2C6A6A87F95F2109841350DA3D24EEED4E4E32D2BED880737670FFE8ED76DB890FD72A0076300E50914984A777C9F2BC843977396C602B24E7A045F04D15CD2EAC01AD8808064CFE5A2DC1AE9FFFA4BF0A6F0C07668097DEEB9C5CA5EC1F9A52F96A403B73FEA2DBBF44473D355553EE7FB1B4D6630777DAF67804BE213089B9F78652CE970C582FD813F87FF0ECBACCE1CA46247E20D09F3E0B4EF6BFCD13244C6877F25E6646252CAD6EB7DBBA3476AAAC83BC3285FF70B50D6CDEDC8E5921944A")?; // encode let mut cipher = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Encrypt)?; cipher.set_key(&key)?; cipher.start(&nonce)?; let mut enc_iter = input.chunks(cipher.update_granularity()).enumerate(); let chunks = if input.len() % cipher.update_granularity() == 0 { input.len() / cipher.update_granularity() } else { input.len() / cipher.update_granularity() + 1 }; let mut enc_out = vec![0; 0]; while let Some((cnt, v)) = enc_iter.next() { let mut res = if (cnt + 1) < chunks { cipher.update(v)? } else { cipher.finish(v)? }; enc_out.append(&mut res); } assert_eq!(botan::hex_encode(&enc_out)?, "38C21B6430D9A3E4BC6749405765653AE91051E96CE0D076141DD7B515EC150FDB8A65EE988D206C9F64874664CDBF61257FFAE521B9A5EB5B35E3745F4232025B269A6CD7DCFE19153ECF7341CE2C6A6A87F95F2109841350DA3D24EEED4E4E32D2BED880737670FFE8ED76DB890FD72A0076300E50914984A777C9F2BC843977396C602B24E7A045F04D15CD2EAC01AD8808064CFE5A2DC1AE9FFFA4BF0A6F0C07668097DEEB9C5CA5EC1F9A52F96A403B73FEA2DBBF44473D355553EE7FB1B4D6630777DAF67804BE213089B9F78652CE970C582FD813F87FF0ECBACCE1CA46247E20D09F3E0B4EF6BFCD13244C6877F25E6646252CAD6EB7DBBA3476AAAC83BC3285FF70B50D6CDEDC8E5921944A"); // Try the same with the allocation-free interface. cipher.set_key(&key)?; cipher.start(&nonce)?; let mut enc_out_prealloc = vec![0; input.len() + cipher.tag_length()]; let mut written = 0; for (cnt, v) in input.chunks(cipher.update_granularity()).enumerate() { if (cnt + 1) < chunks { written += cipher.update_into(v, &mut enc_out_prealloc[written..])? } else { written += cipher.finish_into(v, &mut enc_out_prealloc[written..])? } } assert_eq!(enc_out, enc_out_prealloc); // decode let mut cipher = botan::Cipher::new("AES-128/GCM", botan::CipherDirection::Decrypt)?; cipher.set_key(&key)?; cipher.start(&nonce)?; let chunk_size = cipher.update_granularity(); let mut dec_iter = output.chunks(chunk_size).enumerate(); let chunks = if output.len() % chunk_size == 0 { output.len() / chunk_size } else { output.len() / chunk_size + 1 }; let mut dec_out = vec![0; 0]; while let Some((cnt, v)) = dec_iter.next() { let mut res = cipher.update(v)?; dec_out.append(&mut res); if (cnt + 3) == chunks { break; } } let mut remain = vec![0; 0]; let (_, v) = dec_iter.next().unwrap(); // the one before last one remain.append(v.to_vec().as_mut()); let (_, v) = dec_iter.next().unwrap(); // last one remain.append(v.to_vec().as_mut()); let mut res = cipher.finish(&remain)?; dec_out.append(&mut res); assert_eq!(botan::hex_encode(&dec_out)?, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); // Try the same with the allocation-free interface. cipher.set_key(&key)?; cipher.start(&nonce)?; let mut dec_out_prealloc = vec![0; output.len() - cipher.tag_length()]; let mut written = 0; for (cnt, v) in output.chunks(chunk_size).enumerate() { if (cnt + 1) < chunks { written += cipher.update_into(v, &mut dec_out_prealloc[written..])? } else { written += cipher.finish_into(v, &mut dec_out_prealloc[written..])? } } assert_eq!(dec_out, dec_out_prealloc); Ok(()) } #[test] fn test_chacha() -> Result<(), botan::Error> { let mut cipher = botan::Cipher::new("ChaCha20", botan::CipherDirection::Encrypt)?; assert_eq!(cipher.tag_length(), 0); let key_spec = cipher.key_spec()?; assert!(!key_spec.is_valid_keylength(0)); assert!(key_spec.is_valid_keylength(16)); assert!(key_spec.is_valid_keylength(32)); assert!(!key_spec.is_valid_keylength(48)); let key = vec![0; 32]; let expected = botan::hex_decode("76B8E0ADA0F13D90405D6AE55386BD28BDD219B8A08DED1AA836EFCC8B770DC7DA41597C5157488D7724E03FB8D84A376A43B8F41518A11CC387B669")?; cipher.set_key(&key)?; assert!(cipher.set_associated_data(&[1, 2, 3]).is_err()); // not an AEAD assert!(cipher.set_associated_data(&[]).is_err()); let iv = vec![]; let input = vec![0; expected.len()]; let ctext = cipher.process(&iv, &input)?; assert_eq!(ctext, expected); Ok(()) } #[test] fn test_kdf() -> Result<(), botan::Error> { let salt = botan::hex_decode("000102030405060708090A0B0C")?; let label = botan::hex_decode("F0F1F2F3F4F5F6F7F8F9")?; let secret = botan::hex_decode("0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B0B")?; let expected_output = botan::hex_decode( "3CB25F25FAACD57A90434F64D0362F2A2D2D0A90CF1A5A4C5DB02D56ECC4C5BF34007208D5B887185865", )?; let output = botan::kdf( "HKDF(SHA-256)", expected_output.len(), &secret, &salt, &label, )?; assert_eq!(output, expected_output); Ok(()) } #[test] fn test_pbkdf() -> Result<(), botan::Error> { let salt = botan::hex_decode("0001020304050607")?; let iterations = 10000; let passphrase = "xyz"; let expected_output = botan::hex_decode("DEFD2987FA26A4672F4D16D98398432AD95E896BF619F6A6B8D4ED")?; let output = botan::pbkdf( "PBKDF2(SHA-256)", expected_output.len(), passphrase, &salt, iterations, )?; assert_eq!(output, expected_output); Ok(()) } #[test] fn test_scrypt() -> Result<(), botan::Error> { let salt = botan::hex_decode("4E61436C")?; let n = 1024; let r = 8; let p = 16; let passphrase = "password"; let expected_output = botan::hex_decode("fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622e")?; let output = botan::scrypt(expected_output.len(), passphrase, &salt, n, r, p)?; assert_eq!(output, expected_output); Ok(()) } #[test] fn test_pwdhash() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new()?; let salt = rng.read(10)?; let msec = 30; let (key, r, p, n) = botan::derive_key_from_password_timed("Scrypt", 32, "passphrase", &salt, msec)?; assert_eq!(key.len(), 32); let key2 = botan::derive_key_from_password("Scrypt", 32, "passphrase", &salt, n, r, p)?; assert_eq!(key, key2); Ok(()) } #[test] fn test_hex() -> Result<(), botan::Error> { let raw = vec![1, 2, 3, 255, 42, 23]; assert_eq!(botan::hex_encode(&raw)?, "010203FF2A17"); assert_eq!(botan::hex_decode("010203FF2A17")?, raw); Ok(()) } #[test] fn test_rng() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new_system()?; let read1 = rng.read(10)?; let read2 = rng.read(10)?; assert!(read1 != read2); Ok(()) } #[test] fn test_crl() -> Result<(), botan::Error> { let crl_pem = r"-----BEGIN X509 CRL----- MIIBszCBnAIBATANBgkqhkiG9w0BAQsFADAjMRQwEgYDVQQDDAtUZXN0IFN1YiBD QTELMAkGA1UEBhMCREUXDTE3MDcyNjEzMjQ0N1oXDTE3MTAyNTEzMjQ0N1owFDAS AgEBFw0xNzA3MjYwNzI0NDdaoC8wLTAKBgNVHRQEAwIBAjAfBgNVHSMEGDAWgBTg jBmqaSWaGiUA9rMouhv3DfnPwDANBgkqhkiG9w0BAQsFAAOCAQEASVKeNF4ozBHs jKss2P4Wor9/yvCi6PH+f3fr774nbpW4hK7BqMDfLt1lyPMMRil/Z0FGsTF4wy9f CSnI/NNIqDLdPfL/Wq40swJvuR3p7CjGwEZjfYJ3Zbz+JZJBws7Eg6JtBLHAc9JQ uw9odU3oBt9w9DP0Oh3idfXAQp1Ho/nK+ssXOEo1ADETFjaVooXSpeJ7Khi/6Asq L8A/gDTbuT6K3bEusSXhMo1juAN3oDj7Ruev+CWx0EkxtM9AUBzw707kL7ELxm3g 8FsPC0ejfaQIPluaEBgTuaHtGCUCKnUeUYc2MHPJZZWSaNpqv9OLoAn/25tvsyvI pIKYuraHXw== -----END X509 CRL-----"; let cert_pem = r"-----BEGIN CERTIFICATE----- MIIDbzCCAlegAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMRQwEgYDVQQDDAtUZXN0 IFN1YiBDQTELMAkGA1UEBhMCREUwHhcNMTcwNzI3MDUyNDQ2WhcNMTgwNzI3MTMy NDQ2WjAfMRAwDgYDVQQDDAdUZXN0IEVFMQswCQYDVQQGEwJERTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAJWomlxlqcdd/t7IQjIGy2M0nEP8Bah1ooLr Iq73gzeVFM4qIjKL2OyQYbJDLkMXWUmzmX5P5cGG3pjAJ+Van0ch+OL6Utj8RijJ Ufc10Oo/TIVzIXbMxIa1oLZ8gQ73nhFkNxQZxzgsKop8wPPTdo41p3DSw/+a9cD0 bmqgjSMDYydfuo/42bLeSmlLYhF18T5C1gUn+JXMvQJSI6kLPczsi+mQ0N0GtV0C 1whWBo1atAUK3OeYGRDzIXnE591vXICYg1JBjjEYe6VNK/B3vCEu/QFRLtF3IYHc loU4uurI8HfgeEbJ/H3/uDrUwgahuoIqRXtxNIQv1qGXwbAnPBcCAwEAAaOBsTCB rjA1BgNVHR8ELjAsMCqgKKAmhiRodHRwOi8vbG9jYWxob3N0L3N1YmNhL2NybGRw L2NybC5jcmwwHwYDVR0jBBgwFoAU4IwZqmklmholAPazKLob9w35z8AwHQYDVR0O BBYEFITwNLOf7HFKC/d5A2X3c8Qdd3sQMA4GA1UdDwEB/wQEAwIHgDAPBgNVHRMB Af8EBTADAQEAMBQGA1UdEQQNMAuCCWR1bW15aG9zdDANBgkqhkiG9w0BAQsFAAOC AQEABaLToWh/aCEOlWbi810W1FeAvkwJYSqyYK6hyFYiQ6dn0x4OnqytfVxYKaj6 wp1OOy/aAG9sSak2/Yp18J7U9Nnl/fz66TNBwjsipAo2auHsqfvrxQl4xEyovdzp OhJY4gLUPrmbecsmI4UTU6Xl9IGMDh9LPAn1ErYUvvolJp9Y6XlSb/jHT/7BDMmq JwToSRDikiaCKtrjQvtgw5vFUfcBqKlQmy70ZxIHW92E4cq1twugzc1RpO/c0mxj zjd8FaHJYT9q6z5fRhloqN6w46mS9nbt8xa4As9ULoMcpeVglDXXLh+A8HLLudWD ZB6LDkS9rU3WAqYfPzNZ5AR06A== -----END CERTIFICATE-----"; let crl = botan::CRL::load(crl_pem.as_bytes())?; let cert = botan::Certificate::load(cert_pem.as_bytes())?; assert!(crl.is_revoked(&cert)?); Ok(()) } #[test] fn test_certs() -> Result<(), botan::Error> { let cert_bits = botan::hex_decode("3082035A30820305A003020102020101300C06082A8648CE3D04030105003050310B3009060355040613024445310D300B060355040A0C0462756E64310C300A060355040B0C03627369310D300B06035504051304343536373115301306035504030C0C637363612D6765726D616E79301E170D3037303731393135323731385A170D3238303131393135313830305A3050310B3009060355040613024445310D300B060355040A0C0462756E64310C300A060355040B0C03627369310D300B06035504051304343536373115301306035504030C0C637363612D6765726D616E79308201133081D406072A8648CE3D02013081C8020101302806072A8648CE3D0101021D00D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF303C041C68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43041C2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B0439040D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD021D00D7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F020101033A000401364A4B0F0102E9502AB9DC6855D90B065A6F5E5E48395F8309D57C11ABAFF21756607EF6757EC9886CA222D83CA04B1A99FA43C5A9BCE1A38201103082010C30360603551D11042F302D8118637363612D6765726D616E79406273692E62756E642E646586116661783A2B343932323839353832373232300E0603551D0F0101FF040403020106301D0603551D0E041604140096452DE588F966C4CCDF161DD1F3F5341B71E7301F0603551D230418301680140096452DE588F966C4CCDF161DD1F3F5341B71E730410603551D20043A30383036060904007F0007030101013029302706082B06010505070201161B687474703A2F2F7777772E6273692E62756E642E64652F6373636130120603551D130101FF040830060101FF020100302B0603551D1004243022800F32303037303731393135323731385A810F32303237313131393135313830305A300C06082A8648CE3D0403010500034100303E021D00C6B41E830217FD4C93B59E9E2B13734E09C182FA63FAEE4115A8EDD5021D00D27938DA01B8951A9064A1B696AEDF181B74968829C138F0EB2F623B")?; let cert = botan::Certificate::load(&cert_bits)?; let key_id = botan::hex_decode("0096452DE588F966C4CCDF161DD1F3F5341B71E7")?; assert_eq!(cert.serial_number()?, vec![1]); assert_eq!(cert.authority_key_id()?, key_id); assert_eq!(cert.subject_key_id()?, key_id); assert_eq!(cert.allows_usage(botan::CertUsage::CertificateSign)?, true); assert_eq!(cert.allows_usage(botan::CertUsage::CrlSign)?, true); assert_eq!(cert.allows_usage(botan::CertUsage::KeyEncipherment)?, false); let pubkey = cert.public_key()?; assert_eq!(pubkey.algo_name()?, "ECDSA"); assert_eq!( botan::hex_encode(&pubkey.fingerprint("SHA-256")?)?, "110467922A582F7F35E55DF1C787709A6D27B4F581C02586E9076F1A385404B3" ); Ok(()) } #[test] fn test_cert_verify() -> Result<(), botan::Error> { let ca = b"-----BEGIN CERTIFICATE----- MIIBkDCCATegAwIBAgIRANQudMcHu/SmX8470nbNlj0wCgYIKoZIzj0EAwIwEjEQ MA4GA1UEAxMHVGVzdCBDQTAeFw0xODA4MTYyMjMyNDFaFw00NjAxMDEyMjMyNDFa MBIxEDAOBgNVBAMTB1Rlc3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASN +LHr9ZN72sxZqi4zcYDIg4xzN3DOF3epvlpGHLnju5ogp8dJ46YydTi3g/SfBGOp j9jrYP5Jgkkmpo0lMh7ho24wbDAhBgNVHQ4EGgQYLg/lfneWJ36rZdGMoVyKD6Zl mHkST7ZNMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMCMGA1Ud IwQcMBqAGC4P5X53lid+q2XRjKFcig+mZZh5Ek+2TTAKBggqhkjOPQQDAgNHADBE AiB30ZIFV1cZbknu5lt1fWrM9tNSgCbj5BN9CI+Q9aq1LQIgD9o/8oGmFgvWLjsx b39VOu00+Vy9kpNO1Sgx7wSWoIU= -----END CERTIFICATE-----"; let ee = b"-----BEGIN CERTIFICATE----- MIIBoDCCAUagAwIBAgIRAK27a2NlSYEH63xIsAbBA1wwCgYIKoZIzj0EAwIwEjEQ MA4GA1UEAxMHVGVzdCBDQTAeFw0xODA4MTYyMjMzNDBaFw00NjAxMDEyMjMzNDBa MBoxGDAWBgNVBAMTD1Rlc3QgRW5kIEVudGl0eTBZMBMGByqGSM49AgEGCCqGSM49 AwEHA0IABDykQMvlV7GyIJeANLWEs5bXReqpvTEFu3zYPBjOhyx784VPVl84h8c5 ycru3Hk8N/SIITSWzpbjPMp9jRbyDy+jdTBzMCEGA1UdDgQaBBjkPzL+BXHtQJDR ciwvzeHQKuQZOstyM2swGwYDVR0RBBQwEoIQdGVzdC5leGFtcGxlLmNvbTAMBgNV HRMBAf8EAjAAMCMGA1UdIwQcMBqAGC4P5X53lid+q2XRjKFcig+mZZh5Ek+2TTAK BggqhkjOPQQDAgNIADBFAiEAowK8jGhosOxQpOCjlRg0nFceQ0ETITQC43fk0CZA AzMCIEJSRDmXjX8TMTbSfoTLmhaYJnCL+AfHLZLdHlSLDIzh -----END CERTIFICATE-----"; // Bit flipped from ee let bad_ee = b"-----BEGIN CERTIFICATE----- MIIBoDCCAUagAwIBAgIRAK27a2NlSYEH63xIsAbBA1wwCgYIKoZIzj0EAwIwEjEQ MA4GA1UEAxMHVGVzdCBDQTAeFw0xODA4MTYyMjMzNDBaFw00NjAxMDEyMjMzNDBa MBoxGDAWBgNVBAMTD1Rlc3QgrW5kIEVudGl0eTBZMBMGByqGSM49AgEGCCqGSM49 AwEHA0IABDykQMvlV7GyIJeANLWEs5bXReqpvTEFu3zYPBjOhyx784VPVl84h8c5 ycru3Hk8N/SIITSWzpbjPMp9jRbyDy+jdTBzMCEGA1UdDgQaBBjkPzL+BXHtQJDR ciwvzeHQKuQZOstyM2swGwYDVR0RBBQwEoIQdGVzdC5leGFtcGxlLmNvbTAMBgNV HRMBAf8EAjAAMCMGA1UdIwQcMBqAGC4P5X53lid+q2XRjKFcig+mZZh5Ek+2TTAK BggqhkjOPQQDAgNIADBFAiEAowK8jGhosOxQpOCjlRg0nFceQ0ETITQC43fk0CZA AzMCIEJSRDmXjX8TMTbSfoTLmhaYJnCL+AfHLZLdHlSLDIzh -----END CERTIFICATE-----"; let ca = botan::Certificate::load(ca)?; assert!(ca.to_string()?.starts_with("Version: 3")); assert_eq!(ca.public_key_bits()?.len(), 89); let ee = botan::Certificate::load(ee)?; let bad_ee = botan::Certificate::load(bad_ee)?; let ca_dup = ca.clone(); let result = ee.verify(&[], &[&ca], None, None, None)?; assert_eq!(result.success(), true); assert_eq!(result.to_string(), "Verified"); let result = ee.verify(&[], &[&ca], None, None, Some(300))?; assert_eq!(result.success(), false); assert_eq!(result.to_string(), "Certificate is not yet valid"); let result = ee.verify(&[], &[&ca], None, Some("no.hostname.com"), None)?; assert_eq!(result.success(), false); assert_eq!( result.to_string(), "Certificate does not match provided name" ); let result = ee.verify(&[], &[], None, None, None)?; assert_eq!(result.success(), false); assert_eq!(result.to_string(), "Certificate issuer not found"); let result = bad_ee.verify(&[], &[&ca_dup], None, None, None)?; assert_eq!(result.success(), false); assert_eq!(result.to_string(), "Signature error"); Ok(()) } #[test] fn test_bcrypt() -> Result<(), botan::Error> { let pass = "password"; let mut rng = botan::RandomNumberGenerator::new_system()?; let bcrypt1 = botan::bcrypt_hash(pass, &mut rng, 10)?; assert_eq!(bcrypt1.len(), 60); let bcrypt2 = botan::bcrypt_hash(pass, &mut rng, 10)?; assert_eq!(bcrypt2.len(), 60); assert!(bcrypt1 != bcrypt2); assert!(botan::bcrypt_verify(pass, &bcrypt1)?); assert!(botan::bcrypt_verify(pass, &bcrypt2)?); assert_eq!(botan::bcrypt_verify("passwurd", &bcrypt2)?, false); Ok(()) } #[test] fn test_pubkey() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new_system()?; let ecdsa_key = botan::Privkey::create("ECDSA", "secp256r1", &mut rng)?; assert_eq!(ecdsa_key.check_key(&mut rng)?, true); assert_eq!(ecdsa_key.algo_name()?, "ECDSA"); assert!(ecdsa_key.get_field("n").is_err()); assert_eq!( ecdsa_key.get_field("order"), botan::MPI::from_str("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551") ); let pub_key = ecdsa_key.pubkey()?; assert_eq!(pub_key.algo_name()?, "ECDSA"); let bits = ecdsa_key.der_encode()?; let pem = ecdsa_key.pem_encode()?; assert!(pem.starts_with("-----BEGIN PRIVATE KEY-----\n")); assert!(pem.ends_with("-----END PRIVATE KEY-----\n")); let pub_bits = pub_key.der_encode()?; let pub_pem = pub_key.pem_encode()?; assert!(pub_pem.starts_with("-----BEGIN PUBLIC KEY-----\n")); assert!(pub_pem.ends_with("-----END PUBLIC KEY-----\n")); let loaded_key = botan::Privkey::load_der(&bits)?; assert_eq!(loaded_key.check_key(&mut rng)?, true); let loaded_pem_key = botan::Pubkey::load_pem(&pub_pem)?; assert_eq!(loaded_pem_key.check_key(&mut rng)?, true); let loaded_bits = loaded_key.der_encode()?; let loaded_pub_key = loaded_key.pubkey()?; assert_eq!(loaded_pub_key.algo_name()?, "ECDSA"); let loaded_pub_bits = loaded_pub_key.der_encode()?; assert_eq!(bits, loaded_bits); assert_eq!(pub_bits, loaded_pub_bits); Ok(()) } #[test] fn test_x25519() -> Result<(), botan::Error> { // Test from RFC 8037 let a_pub_bits = botan::hex_decode("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f")?; let b_priv_bits = botan::hex_decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")?; let b_pub_bits = botan::hex_decode("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")?; let expected_shared = botan::hex_decode("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742")?; let a_pub = botan::Pubkey::load_x25519(&a_pub_bits)?; assert_eq!(a_pub.get_x25519_key()?, a_pub_bits); let b_priv = botan::Privkey::load_x25519(&b_priv_bits)?; assert_eq!(b_priv.get_x25519_key()?, b_priv_bits); assert_eq!(b_priv.key_agreement_key()?, b_pub_bits); assert_eq!(b_priv.pubkey()?.get_x25519_key()?, b_pub_bits); let shared = b_priv.agree(&a_pub_bits, 0, &[], "Raw")?; assert_eq!(shared, expected_shared); Ok(()) } #[test] fn test_ed25519() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new_system()?; let msg = vec![23, 42, 69, 6, 66]; let padding = "Pure"; let ed_priv = botan::Privkey::create("Ed25519", "", &mut rng)?; let signature1 = ed_priv.sign(&msg, padding, &mut rng)?; let ed_bits = ed_priv.get_ed25519_key()?; let ed_loaded = botan::Privkey::load_ed25519(&ed_bits.1)?; let signature2 = ed_loaded.sign(&msg, padding, &mut rng)?; let ed_pub = ed_priv.pubkey()?; assert!(ed_pub.verify(&msg, &signature1, padding)?); assert!(ed_pub.verify(&msg, &signature2, padding)?); let ed_loaded = botan::Pubkey::load_ed25519(&ed_bits.0)?; assert!(ed_loaded.verify(&msg, &signature1, padding)?); assert!(ed_loaded.verify(&msg, &signature2, padding)?); assert_eq!(ed_loaded.get_ed25519_key()?, ed_pub.get_ed25519_key()?); assert_eq!(signature1, signature2); Ok(()) } #[test] fn test_rsa() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new_system()?; let padding = "EMSA-PKCS1-v1_5(SHA-256)"; let msg = rng.read(32)?; let privkey = botan::Privkey::create("RSA", "1024", &mut rng)?; let pubkey = privkey.pubkey()?; assert_eq!(privkey.get_field("e"), botan::MPI::from_str("65537")); assert_eq!(privkey.get_field("n")?.bit_count()?, 1024); assert_eq!(pubkey.get_field("n"), privkey.get_field("n")); let p = privkey.get_field("p")?; let q = privkey.get_field("q")?; assert_eq!(&p * &q, privkey.get_field("n")?); let signature = privkey.sign(&msg, padding, &mut rng)?; assert!(pubkey.verify(&msg, &signature, padding)?); let pubkey = botan::Pubkey::load_rsa(&privkey.get_field("n")?, &privkey.get_field("e")?)?; assert!(pubkey.verify(&msg, &signature, padding)?); Ok(()) } #[test] fn test_pubkey_encryption() -> Result<(), botan::Error> { let padding = "EMSA-PKCS1-v1_5(SHA-256)"; let msg = [1, 2, 3]; let mut rng = botan::RandomNumberGenerator::new_system()?; let key = botan::Privkey::create("RSA", "1024", &mut rng)?; let der = key.der_encode_encrypted("passphrase", &mut rng)?; let pem = key.pem_encode_encrypted("pemword", &mut rng)?; assert!(pem.starts_with("-----BEGIN ENCRYPTED PRIVATE KEY-----\n")); assert!(pem.ends_with("-----END ENCRYPTED PRIVATE KEY-----\n")); let sig1 = key.sign(&msg, padding, &mut rng)?; //assert!(botan::Privkey::load_encrypted_der(&der, "i forget").is_err()); let load = botan::Privkey::load_encrypted_der(&der, "passphrase")?; let sig2 = load.sign(&msg, padding, &mut rng)?; assert_eq!(sig1, sig2); let load = botan::Privkey::load_encrypted_pem(&pem, "pemword")?; let sig3 = load.sign(&msg, padding, &mut rng)?; assert_eq!(sig1, sig3); Ok(()) } #[test] fn test_pubkey_sign() -> Result<(), botan::Error> { let msg = vec![1, 23, 42]; let mut rng = botan::RandomNumberGenerator::new_system()?; let ecdsa_key = botan::Privkey::create("ECDSA", "secp256r1", &mut rng)?; assert!(ecdsa_key.key_agreement_key().is_err()); let signature = ecdsa_key.sign(&msg, "EMSA1(SHA-256)", &mut rng)?; let pub_key = ecdsa_key.pubkey()?; let mut verifier = botan::Verifier::new(&pub_key, "EMSA1(SHA-256)")?; verifier.update(&[1])?; verifier.update(&[23, 42])?; assert_eq!(verifier.finish(&signature)?, true); verifier.update(&[1])?; assert_eq!(verifier.finish(&signature)?, false); verifier.update(&[1])?; verifier.update(&[23, 42])?; assert_eq!(verifier.finish(&signature)?, true); Ok(()) } #[test] fn test_pubkey_encrypt() -> Result<(), botan::Error> { let msg = vec![1, 23, 42]; let mut rng = botan::RandomNumberGenerator::new_system()?; let priv_key = botan::Privkey::create("RSA", "2048", &mut rng)?; assert!(priv_key.key_agreement_key().is_err()); let pub_key = priv_key.pubkey()?; let mut encryptor = botan::Encryptor::new(&pub_key, "OAEP(SHA-256)")?; let ctext = encryptor.encrypt(&msg, &mut rng)?; assert_eq!(ctext.len(), 2048 / 8); let mut decryptor = botan::Decryptor::new(&priv_key, "OAEP(SHA-256)")?; let ptext = decryptor.decrypt(&ctext)?; assert_eq!(ptext, msg); Ok(()) } #[test] fn test_pubkey_key_agreement() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new_system()?; let a_priv = botan::Privkey::create("ECDH", "secp384r1", &mut rng)?; let b_priv = botan::Privkey::create("ECDH", "secp384r1", &mut rng)?; let a_pub = a_priv.key_agreement_key()?; let b_pub = b_priv.key_agreement_key()?; let mut a_ka = botan::KeyAgreement::new(&a_priv, "KDF2(SHA-384)")?; let mut b_ka = botan::KeyAgreement::new(&b_priv, "KDF2(SHA-384)")?; let salt = rng.read(16)?; let a_key = a_ka.agree(32, &b_pub, &salt)?; let b_key = b_ka.agree(32, &a_pub, &salt)?; assert_eq!(a_key, b_key); let mut a_ka = botan::KeyAgreement::new(&a_priv, "Raw")?; let mut b_ka = botan::KeyAgreement::new(&b_priv, "Raw")?; let a_key = a_ka.agree(0, &b_pub, &[])?; let b_key = b_ka.agree(0, &a_pub, &[])?; assert_eq!(a_key, b_key); assert_eq!(a_key.len(), 384 / 8); Ok(()) } #[test] fn test_rfc3394_aes_key_wrap() -> Result<(), botan::Error> { let kek = botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")?; let key = botan::hex_decode("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F")?; let wrapped = botan::rfc3394_key_wrap(&kek, &key)?; assert_eq!( botan::hex_encode(&wrapped)?, "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21" ); let unwrapped = botan::rfc3394_key_unwrap(&kek, &wrapped)?; assert_eq!(unwrapped, key); Ok(()) } #[cfg(feature = "botan3")] #[test] fn test_aes_key_wrap() -> Result<(), botan::Error> { let kek = botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")?; let key = botan::hex_decode("00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F")?; let wrapped = botan::nist_kw_enc("AES-256", false, &kek, &key)?; assert_eq!( botan::hex_encode(&wrapped)?, "28C9F404C4B810F4CBCCB35CFB87F8263F5786E2D80ED326CBC7F0E71A99F43BFB988B9B7A02DD21" ); let unwrapped = botan::nist_kw_dec("AES-256", false, &kek, &wrapped)?; assert_eq!(unwrapped, key); Ok(()) } #[test] fn test_pkcs_hash_id() -> Result<(), botan::Error> { assert!(botan::pkcs_hash_id("SHA-192").is_err()); let id = botan::pkcs_hash_id("SHA-384")?; assert_eq!( botan::hex_encode(&id)?, "3041300D060960864801650304020205000430" ); Ok(()) } #[test] fn test_ct_compare() -> Result<(), botan::Error> { let a = vec![1, 2, 3]; assert_eq!(botan::const_time_compare(&a, &[1, 2, 3]), true); assert_eq!(botan::const_time_compare(&a, &[1, 2, 3, 4]), false); assert_eq!(botan::const_time_compare(&a, &[1, 2, 4]), false); assert_eq!(botan::const_time_compare(&a, &a), true); assert_eq!(botan::const_time_compare(&a, &[1, 2, 3]), true); Ok(()) } #[test] fn test_scrub_mem() -> Result<(), botan::Error> { let mut v = vec![1, 2, 3]; botan::scrub_mem(&mut v); assert_eq!(v, vec![0, 0, 0]); let mut a = [1u32, 2u32, 3u32, 2049903u32]; botan::scrub_mem(&mut a); assert_eq!(a, [0, 0, 0, 0]); Ok(()) } #[test] fn test_mp() -> Result<(), botan::Error> { let mut a = botan::MPI::new()?; let mut b = botan::MPI::new()?; assert_eq!(a.to_u32()?, 0); assert_eq!(b.to_u32()?, 0); a.set_i32(9)?; b.set_i32(81)?; assert_eq!(a.get_bit(0), Ok(true)); assert_eq!(a.get_bit(1), Ok(false)); assert_eq!(a.to_u32()?, 9); assert_eq!(b.to_u32()?, 81); let mut c = &a + &b; assert_eq!(c.to_u32()?, 90); let d = botan::MPI::from_str("0x5A")?; assert_eq!(c, d); c *= &botan::MPI::from_str("1030")?; assert_eq!(c.to_string()?, "92700"); assert_eq!(format!("{c}"), "92700"); assert_eq!(format!("{c:x}"), "016a1c"); assert_eq!(format!("{c:X}"), "016A1C"); assert_eq!(format!("{c:#x}"), "0x016a1c"); assert_eq!(format!("{c:#X}"), "0x016A1C"); assert_eq!(c.to_bin()?, vec![0x01, 0x6a, 0x1c]); let mut s = &c << 32; assert_eq!(s.to_hex()?, "016A1C00000000"); s <<= 4; s += 5; assert_eq!(s.to_hex()?, "16A1C000000005"); let mut s = s - 19; assert_eq!(s.to_hex()?, "16A1BFFFFFFFF2"); s += 14; s >>= 8; assert_eq!(s.to_hex()?, "16A1C0000000"); let mut t = &s >> 28; assert_eq!(t, c); t += &s; t <<= 4; assert_eq!(t.to_hex()?, "016A1C0016A1C0"); let ten = botan::MPI::new_from_u32(10)?; let d = &t / &ten; assert_eq!(d.to_hex()?, "243600024360"); t /= &ten; assert_eq!(d, t); t /= &ten; let r = &t % &ten; assert_eq!(r.to_string()?, "4"); let t = -t * &ten; assert_eq!(t.is_negative()?, true); assert_eq!(format!("{t}"), "-39814346982240"); Ok(()) } #[test] fn test_fpe() -> Result<(), botan::Error> { let modulus = botan::MPI::from_str("1000000000")?; let input = botan::MPI::from_str("939210311")?; let key = vec![0; 32]; let tweak = vec![0; 8]; let fpe = botan::FPE::new_fe1(&modulus, &key, 8, false)?; let ctext = fpe.encrypt(&input, &tweak)?; assert_ne!(ctext, input); let ptext = fpe.decrypt(&ctext, &tweak)?; assert_eq!(ptext, input); Ok(()) } #[test] fn test_hotp() -> Result<(), botan::Error> { let hotp = botan::HOTP::new(&[0xFF], "SHA-1", 6)?; assert_eq!(hotp.generate(23)?, 330795); assert!(hotp.check(330795, 23)?); assert!(!hotp.check(330795, 22)?); assert!(!hotp.check(330796, 23)?); Ok(()) } #[test] fn test_totp() -> Result<(), botan::Error> { let totp = botan::TOTP::new( b"1234567890123456789012345678901234567890123456789012345678901234", "SHA-512", 8, 30, )?; assert_eq!(totp.generate(59)?, 90693936); assert_eq!(totp.generate(1111111109)?, 25091201); assert_eq!(totp.generate(1111111111)?, 99943326); assert!(totp.check(90693936, 59, 0)?); assert!(!totp.check(90693936, 60, 0)?); assert!(totp.check(90693936, 59 + 30, 1)?); assert!(!totp.check(90693936, 59 + 31, 1)?); Ok(()) } #[test] fn test_elgamal() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new()?; let p_bits = 1024; let q_bits = 256; let elg = botan::Privkey::create_elgamal(p_bits, q_bits, &mut rng)?; // extract the elements: let p = elg.get_field("p")?; let q = elg.get_field("q")?; let g = elg.get_field("g")?; let x = elg.get_field("x")?; let y = elg.get_field("y")?; // check the lengths: assert_eq!(p.bit_count()?, p_bits); assert_eq!(q.bit_count()?, q_bits); assert!(x.bit_count()? <= q_bits); assert!(y.bit_count()? <= p_bits); // create a public key: let elgp = botan::Pubkey::load_elgamal(&p, &g, &y)?; // encrypt a message: let padding = "PKCS1v15"; let ptext = rng.read(16)?; let ctext = elgp.encrypt(&ptext, padding, &mut rng)?; // decrypt with the private key: let recovered = elg.decrypt(&ctext, padding)?; assert_eq!(recovered, ptext); Ok(()) } #[test] fn test_dsa() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new()?; let p_bits = 1024; let q_bits = 256; let dsa = botan::Privkey::create_dsa(p_bits, q_bits, &mut rng)?; // extract the elements: let p = dsa.get_field("p")?; let q = dsa.get_field("q")?; let g = dsa.get_field("g")?; let x = dsa.get_field("x")?; let y = dsa.get_field("y")?; // check the lengths: assert_eq!(p.bit_count()?, p_bits); assert_eq!(q.bit_count()?, q_bits); assert!(x.bit_count()? <= q_bits); assert!(y.bit_count()? <= p_bits); // create a public key: let dsap = botan::Pubkey::load_dsa(&p, &q, &g, &y)?; // sign a message: let padding = "EMSA1(SHA-256)"; let message = rng.read(16)?; let signature = dsa.sign(&message, &padding, &mut rng)?; // verify the signature: assert!(dsap.verify(&message, &signature, &padding)?); Ok(()) } #[cfg(feature = "botan3")] #[test] fn test_zfec() -> Result<(), botan::Error> { let k = 2; let n = 3; let input_bytes = b"abcdefghijklmnop"; let output_shares = botan::zfec_encode(k, n, input_bytes)?; assert_eq!(output_shares.len(), n); assert_eq!(output_shares[0], b"abcdefgh"); assert_eq!(output_shares[1], b"ijklmnop"); assert_eq!(output_shares[2], b"qrstuvwX"); let mut shares_for_decoding = Vec::new(); shares_for_decoding.push((2, output_shares[2].as_ref())); shares_for_decoding.push((0, output_shares[0].as_ref())); let share_size = output_shares[0].len(); let recovered = botan::zfec_decode(k, n, &shares_for_decoding, share_size)?; assert_eq!(recovered, input_bytes); Ok(()) } #[cfg(feature = "botan3")] #[test] fn test_kyber() -> Result<(), botan::Error> { let mut rng = botan::RandomNumberGenerator::new()?; let kyber_priv = botan::Privkey::create("Kyber", "Kyber-1024-r3", &mut rng)?; let kyber_pub = kyber_priv.pubkey()?; let salt = rng.read(12)?; let shared_key_len = 32; let kdf = "KDF2(SHA-256)"; let kem_e = botan::KeyEncapsulation::new(&kyber_pub, kdf)?; let (shared_key, encap_key) = kem_e.create_shared_key(&mut rng, &salt, shared_key_len)?; assert_eq!(shared_key.len(), shared_key_len); assert_eq!(encap_key.len(), 1568); let kem_d = botan::KeyDecapsulation::new(&kyber_priv, kdf)?; let shared_key_d = kem_d.decrypt_shared_key(&encap_key, &salt, shared_key_len)?; assert_eq!(shared_key, shared_key_d); Ok(()) } botan-0.10.7/tests/wycheproof.rs000064400000000000000000001050371046102023000147170ustar 00000000000000use wycheproof::{EllipticCurve, HashFunction, TestResult}; fn hash_id_to_str(hash: HashFunction) -> Option<&'static str> { match hash { HashFunction::Sha1 => Some("SHA-1"), HashFunction::Sha2_224 => Some("SHA-224"), HashFunction::Sha2_256 => Some("SHA-256"), HashFunction::Sha2_384 => Some("SHA-384"), HashFunction::Sha2_512 => Some("SHA-512"), HashFunction::Sha2_512_224 => None, HashFunction::Sha2_512_256 => Some("SHA-512-256"), HashFunction::Sha3_224 => Some("SHA-3(224)"), HashFunction::Sha3_256 => Some("SHA-3(256)"), HashFunction::Sha3_384 => Some("SHA-3(384)"), HashFunction::Sha3_512 => Some("SHA-3(512)"), HashFunction::Shake128 => Some("SHAKE-128(256)"), HashFunction::Shake256 => Some("SHAKE-256(512)"), } } #[test] fn wycheproof_hkdf_tests() -> Result<(), botan::Error> { use wycheproof::hkdf::*; for test_set_name in TestName::all() { let test_set = TestSet::load(test_set_name).expect("Loading tests failed"); for test_group in test_set.test_groups { let hkdf_name = match test_set.algorithm { Algorithm::HkdfSha1 => "HKDF(SHA-1)", Algorithm::HkdfSha256 => "HKDF(SHA-256)", Algorithm::HkdfSha384 => "HKDF(SHA-384)", Algorithm::HkdfSha512 => "HKDF(SHA-512)", }; for test in &test_group.tests { if test.flags.contains(&TestFlag::SizeTooLarge) { continue; } let output = botan::kdf(hkdf_name, test.size, &test.ikm, &test.salt, &test.info)?; assert_eq!(output, test.okm.as_ref()); } } } Ok(()) } #[test] fn wycheproof_keywrap_tests() -> Result<(), botan::Error> { use wycheproof::keywrap::*; let is_botan2 = botan::Version::current()?.major == 2; for test_set_name in TestName::all() { let test_set = TestSet::load(test_set_name).unwrap(); if test_set.algorithm != Algorithm::AesKeyWrap { continue; } for test_group in &test_set.test_groups { for test in &test_group.tests { if is_botan2 && test.pt.len() == 8 { continue; } if !test.result.must_fail() { let wrapped = botan::rfc3394_key_wrap(&test.key, &test.pt)?; assert_eq!(wrapped, test.ct.as_ref()); let unwrapped = botan::rfc3394_key_unwrap(&test.key, &test.ct)?; assert_eq!(unwrapped, test.pt.as_ref()); } } } } Ok(()) } #[cfg(feature = "botan3")] #[test] fn wycheproof_nist_kw_tests() -> Result<(), botan::Error> { use wycheproof::keywrap::*; for test_set_name in TestName::all() { let test_set = TestSet::load(test_set_name).unwrap(); for test_group in &test_set.test_groups { let (cipher, padding) = match (test_set.algorithm, test_group.key_size) { (Algorithm::AesKeyWrap, 128) => ("AES-128", false), (Algorithm::AesKeyWrap, 192) => ("AES-192", false), (Algorithm::AesKeyWrap, 256) => ("AES-256", false), (Algorithm::AesKeyWrapWithPadding, 128) => ("AES-128", true), (Algorithm::AesKeyWrapWithPadding, 192) => ("AES-192", true), (Algorithm::AesKeyWrapWithPadding, 256) => ("AES-256", true), (Algorithm::AriaKeyWrap, 128) => ("ARIA-128", false), (Algorithm::AriaKeyWrap, 192) => ("ARIA-192", false), (Algorithm::AriaKeyWrap, 256) => ("ARIA-256", false), (Algorithm::AriaKeyWrapWithPadding, 128) => ("ARIA-128", true), (Algorithm::AriaKeyWrapWithPadding, 192) => ("ARIA-192", true), (Algorithm::AriaKeyWrapWithPadding, 256) => ("ARIA-256", true), (Algorithm::CamelliaKeyWrap, 128) => ("Camellia-128", false), (Algorithm::CamelliaKeyWrap, 192) => ("Camellia-192", false), (Algorithm::CamelliaKeyWrap, 256) => ("Camellia-256", false), (Algorithm::SeedKeyWrap, 128) => ("SEED", false), (bc, kl) => panic!("Unhandled block cipher {:?}/{}", bc, kl), }; for test in &test_group.tests { if test.result.must_fail() { assert!(botan::nist_kw_dec(cipher, padding, &test.key, &test.ct).is_err()); } else { let wrapped = botan::nist_kw_enc(cipher, padding, &test.key, &test.pt)?; assert_eq!(wrapped, test.ct.as_ref()); let unwrapped = botan::nist_kw_dec(cipher, padding, &test.key, &test.ct)?; assert_eq!(unwrapped, test.pt.as_ref()); } } } } Ok(()) } #[test] fn wycheproof_cipher_tests() -> Result<(), botan::Error> { use wycheproof::cipher::*; let is_botan2 = botan::Version::current()?.major == 2; for test_set_name in TestName::all() { let test_set = TestSet::load(test_set_name).expect("Loading tests failed"); let is_xts = test_set.algorithm == Algorithm::AesXts; for test_group in test_set.test_groups { if is_botan2 && is_xts && test_group.nonce_size != 128 { // Botan2 does not support short nonces with XTS continue; } let cipher_name = match (test_set.algorithm, test_group.key_size) { (Algorithm::AesCbcPkcs5, 128) => "AES-128/CBC", (Algorithm::AesCbcPkcs5, 192) => "AES-192/CBC", (Algorithm::AesCbcPkcs5, 256) => "AES-256/CBC", (Algorithm::AesXts, 256) => "AES-128/XTS", (Algorithm::AesXts, 384) => "AES-192/XTS", (Algorithm::AesXts, 512) => "AES-256/XTS", (Algorithm::AriaCbcPkcs5, 128) => "ARIA-128/CBC", (Algorithm::AriaCbcPkcs5, 192) => "ARIA-192/CBC", (Algorithm::AriaCbcPkcs5, 256) => "ARIA-256/CBC", (Algorithm::CamelliaCbcPkcs5, 128) => "Camellia-128/CBC", (Algorithm::CamelliaCbcPkcs5, 192) => "Camellia-192/CBC", (Algorithm::CamelliaCbcPkcs5, 256) => "Camellia-256/CBC", (_, _) => panic!("Unhandled cipher"), }; let mut enc = botan::Cipher::new(cipher_name, botan::CipherDirection::Encrypt)?; let mut dec = botan::Cipher::new(cipher_name, botan::CipherDirection::Decrypt)?; for test in &test_group.tests { if !test.result.must_fail() { enc.set_key(&test.key)?; let ct = enc.process(&test.nonce, &test.pt)?; assert_eq!(ct, test.ct.as_ref()); } dec.set_key(&test.key)?; match dec.process(&test.nonce, &test.ct) { Ok(pt) => { assert_eq!(pt, test.pt.as_ref()); assert!(!test.result.must_fail()); } Err(_) => assert!(test.result.must_fail()), } } } } Ok(()) } #[test] fn wycheproof_aead_gcm_tests() -> Result<(), botan::Error> { fn aes_gcm_name(ks: usize, ts: usize, _ns: usize) -> Option { assert!(ks == 128 || ks == 192 || ks == 256); assert!(ts == 128); Some(format!("AES-{ks}/GCM")) } fn aria_gcm_name(ks: usize, ts: usize, _ns: usize) -> Option { assert!(ks == 128 || ks == 192 || ks == 256); assert!(ts == 128); Some(format!("ARIA-{ks}/GCM")) } fn seed_gcm_name(ks: usize, ts: usize, _ns: usize) -> Option { assert!(ks == 128 && ts == 128); Some(format!("SEED/GCM")) } fn sm4_gcm_name(ks: usize, ts: usize, _ns: usize) -> Option { assert!(ks == 128 && ts == 128); Some(format!("SM4/GCM")) } wycheproof_aead_test(wycheproof::aead::TestName::AesGcm, aes_gcm_name)?; wycheproof_aead_test(wycheproof::aead::TestName::AriaGcm, aria_gcm_name)?; wycheproof_aead_test(wycheproof::aead::TestName::SeedGcm, seed_gcm_name)?; wycheproof_aead_test(wycheproof::aead::TestName::Sm4Gcm, sm4_gcm_name)?; Ok(()) } #[test] fn wycheproof_aead_ccm_tests() -> Result<(), botan::Error> { fn aes_ccm_name(ks: usize, ts: usize, ns: usize) -> Option { assert!(ks == 128 || ks == 192 || ks == 256); assert!(ts % 8 == 0); let tag_bytes = ts / 8; if tag_bytes < 4 || tag_bytes % 2 == 1 { return None; } if ns >= 128 { return None; } let ccm_l = 15 - (ns / 8); if ccm_l < 2 || ccm_l > 8 { return None; } Some(format!("AES-{ks}/CCM({tag_bytes},{ccm_l})")) } wycheproof_aead_test(wycheproof::aead::TestName::AesCcm, aes_ccm_name)?; Ok(()) } #[test] fn wycheproof_aead_eax_tests() -> Result<(), botan::Error> { fn aes_eax_name(ks: usize, ts: usize, _ns: usize) -> Option { assert!(ks == 128 || ks == 192 || ks == 256); assert!(ts == 128); Some(format!("AES-{ks}/EAX")) } wycheproof_aead_test(wycheproof::aead::TestName::AesEax, aes_eax_name)?; Ok(()) } #[test] fn wycheproof_aead_siv_tests() -> Result<(), botan::Error> { fn aes_siv_name(ks: usize, ts: usize, _ns: usize) -> Option { assert!(ks == 2 * 128 || ks == 2 * 192 || ks == 2 * 256); assert!(ts == 128); Some(format!("AES-{}/SIV", ks / 2)) } wycheproof_aead_test(wycheproof::aead::TestName::AesSivCmac, aes_siv_name)?; Ok(()) } #[test] fn wycheproof_aead_chacha20poly1305_tests() -> Result<(), botan::Error> { fn chacha_name(ks: usize, ts: usize, _ns: usize) -> Option { assert_eq!(ks, 256); assert_eq!(ts, 128); Some("ChaCha20Poly1305".to_string()) } wycheproof_aead_test(wycheproof::aead::TestName::ChaCha20Poly1305, chacha_name)?; wycheproof_aead_test(wycheproof::aead::TestName::XChaCha20Poly1305, chacha_name)?; Ok(()) } fn wycheproof_aead_test( test_set_name: wycheproof::aead::TestName, botan_cipher_name: impl Fn(usize, usize, usize) -> Option, ) -> Result<(), botan::Error> { let test_set = wycheproof::aead::TestSet::load(test_set_name).expect("Loading tests failed"); let is_botan2 = botan::Version::current()?.major == 2; for test_group in test_set.test_groups { let cipher_name = botan_cipher_name( test_group.key_size, test_group.tag_size, test_group.nonce_size, ); let cipher_name = match cipher_name { Some(name) => name, None => continue, }; let tag_first = cipher_name.contains("/SIV"); let mut enc = botan::Cipher::new(&cipher_name, botan::CipherDirection::Encrypt)?; let mut dec = botan::Cipher::new(&cipher_name, botan::CipherDirection::Decrypt)?; for test in &test_group.tests { if is_botan2 && cipher_name.contains("/EAX") { // Cipher object must be cleared each time to avoid a bug in EAX encryption in Botan 2 enc.clear()?; dec.clear()?; } enc.set_key(&test.key)?; enc.set_associated_data(&test.aad)?; if test.result == wycheproof::TestResult::Invalid && test .flags .contains(&wycheproof::aead::TestFlag::ZeroLengthIv) { assert!(enc.process(&test.nonce, &test.pt).is_err()); continue; } if !enc.valid_nonce_length(test.nonce.len())? { assert!(test.result.must_fail()); continue; } let ctext = enc.process(&test.nonce, &test.pt)?; let expected_ctext = if tag_first { format!( "{}{}", hex::encode(test.tag.as_ref()), hex::encode(test.ct.as_ref()) ) } else { format!( "{}{}", hex::encode(test.ct.as_ref()), hex::encode(test.tag.as_ref()) ) }; if test.result.must_fail() { assert_ne!(hex::encode(ctext), expected_ctext); } else { assert_eq!(hex::encode(ctext), expected_ctext); } dec.set_key(&test.key)?; dec.set_associated_data(&test.aad)?; if test.result == wycheproof::TestResult::Invalid && test .flags .contains(&wycheproof::aead::TestFlag::ZeroLengthIv) { assert!(dec.process(&test.nonce, &test.pt).is_err()); continue; } let ct_and_tag = if tag_first { let mut tag_and_ct = test.tag.to_vec(); tag_and_ct.extend_from_slice(&test.ct); tag_and_ct } else { let mut ct_and_tag = test.ct.to_vec(); ct_and_tag.extend_from_slice(&test.tag); ct_and_tag }; match dec.process(&test.nonce, &ct_and_tag) { Ok(ptext) => { assert!(!test.result.must_fail()); assert_eq!(hex::encode(ptext), hex::encode(&test.pt)); } Err(_) => { assert!(test.result.must_fail()); } } } } Ok(()) } #[test] fn wycheproof_mac_tests() -> Result<(), botan::Error> { use wycheproof::mac::*; fn mac_test_simple( test_set_name: TestName, mac_name: &'static str, ) -> Result<(), botan::Error> { wycheproof_mac_test(test_set_name, |_ks: usize| Some(mac_name.to_string())) } mac_test_simple(TestName::HmacSha1, "HMAC(SHA-1)")?; mac_test_simple(TestName::HmacSha224, "HMAC(SHA-224)")?; mac_test_simple(TestName::HmacSha256, "HMAC(SHA-256)")?; mac_test_simple(TestName::HmacSha384, "HMAC(SHA-384)")?; mac_test_simple(TestName::HmacSha512, "HMAC(SHA-512)")?; mac_test_simple(TestName::HmacSha512_256, "HMAC(SHA-512-256)")?; mac_test_simple(TestName::HmacSha3_224, "HMAC(SHA-3(224))")?; mac_test_simple(TestName::HmacSha3_256, "HMAC(SHA-3(256))")?; mac_test_simple(TestName::HmacSha3_384, "HMAC(SHA-3(384))")?; mac_test_simple(TestName::HmacSha3_512, "HMAC(SHA-3(512))")?; mac_test_simple(TestName::HmacSm3, "HMAC(SM3)")?; mac_test_simple(TestName::SipHash_1_3, "SipHash(1,3)")?; mac_test_simple(TestName::SipHash_2_4, "SipHash(2,4)")?; mac_test_simple(TestName::SipHash_4_8, "SipHash(4,8)")?; wycheproof_mac_test(TestName::AesCmac, |ks: usize| { if ks == 128 || ks == 192 || ks == 256 { Some(format!("CMAC(AES-{})", ks)) } else { None } })?; wycheproof_mac_test(TestName::AriaCmac, |ks: usize| { if ks == 128 || ks == 192 || ks == 256 { Some(format!("CMAC(ARIA-{})", ks)) } else { None } })?; wycheproof_mac_test(TestName::CamelliaCmac, |ks: usize| { if ks == 128 || ks == 192 || ks == 256 { Some(format!("CMAC(Camellia-{})", ks)) } else { None } })?; Ok(()) } fn wycheproof_mac_test( test_set_name: wycheproof::mac::TestName, mac_name_fn: impl Fn(usize) -> Option, ) -> Result<(), botan::Error> { use wycheproof::mac::*; let test_set = TestSet::load(test_set_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let mac_name = match mac_name_fn(test_group.key_size) { Some(n) => n, None => continue, }; let mut mac = botan::MsgAuthCode::new(&mac_name)?; for test in &test_group.tests { mac.set_key(&test.key)?; mac.update(&test.msg)?; let mut computed_tag = mac.finish()?; computed_tag.truncate(test_group.tag_size / 8); if !test.result.must_fail() { assert_eq!(computed_tag, test.tag.as_ref()); } else { assert_ne!(computed_tag, test.tag.as_ref()); } } } Ok(()) } #[cfg(feature = "botan3")] #[test] fn wycheproof_mac_with_nonce_tests() -> Result<(), botan::Error> { use wycheproof::mac_with_nonce::*; let test_set = TestSet::load(TestName::Gmac).expect("Loading tests failed"); for test_group in &test_set.test_groups { let mac_name = format!("GMAC(AES-{})", test_group.key_size); let mut mac = botan::MsgAuthCode::new(&mac_name)?; for test in &test_group.tests { mac.set_key(&test.key)?; mac.set_nonce(&test.nonce)?; mac.update(&test.msg)?; let mut computed_tag = mac.finish()?; computed_tag.truncate(test_group.tag_size / 8); if !test.result.must_fail() { assert_eq!(computed_tag, test.tag.as_ref()); } else { assert_ne!(computed_tag, test.tag.as_ref()); } } } Ok(()) } #[test] fn wycheproof_primality_tests() -> Result<(), botan::Error> { use wycheproof::{primality::*, TestResult}; let mut rng = botan::RandomNumberGenerator::new_system()?; for test_name in TestName::all() { let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { for test in &test_group.tests { if test.flags.contains(&TestFlag::NegativeOfPrime) { continue; } // The primality test data encodes negative numbers using // twos complement encoding let mpi = if test.value.len() > 0 && (test.value[0] & 0x80 == 0x80) { let mut flipped: Vec = test.value.to_vec(); for i in 0..flipped.len() { flipped[i] = !flipped[i]; } let one = botan::MPI::new_from_u32(1)?; botan::MPI::new_from_bytes(&flipped)? + &one } else { botan::MPI::new_from_bytes(&test.value)? }; let is_prime = mpi.is_prime(&mut rng, 128)?; assert_eq!(is_prime, test.result == TestResult::Valid); } } } Ok(()) } #[test] fn wycheproof_rsa_pkcs1_decrypt_tests() -> Result<(), botan::Error> { use wycheproof::rsa_pkcs1_decrypt::*; let is_botan2 = botan::Version::current()?.major == 2; for test_name in TestName::all() { let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let key = botan::Privkey::load_der(&test_group.pkcs8)?; let mut decryptor = botan::Decryptor::new(&key, "PKCS1v15")?; for test in &test_group.tests { if is_botan2 && test.comment == "Prepended bytes to ciphertext" { continue; } match decryptor.decrypt(&test.ct) { Ok(pt) => { assert_eq!(pt, test.pt.as_ref()); assert!(!test.result.must_fail()); } Err(_) => { assert!(test.result.must_fail()); } } } } } Ok(()) } #[test] fn wycheproof_rsa_oaep_decrypt_tests() -> Result<(), botan::Error> { use wycheproof::rsa_oaep::*; fn gen_oaep_string(group: &TestGroup) -> Option { if group.mgf != wycheproof::Mgf::Mgf1 { return None; } let label_hash = match hash_id_to_str(group.hash) { Some(h) => h, None => return None, }; let mgf_hash = match hash_id_to_str(group.mgf_hash) { Some(h) => h, None => return None, }; Some(format!("OAEP({},MGF1({}))", label_hash, mgf_hash)) } let is_botan2 = botan::Version::current()?.major == 2; for test_name in TestName::all() { let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let oaep_string = match gen_oaep_string(&test_group) { Some(s) => s, None => continue, }; let key = botan::Privkey::load_der(&test_group.pkcs8)?; let mut decryptor = botan::Decryptor::new(&key, &oaep_string)?; for test in &test_group.tests { if !test.label.is_empty() { continue; } if is_botan2 && test.comment == "prepended bytes to ciphertext" { continue; } match decryptor.decrypt(&test.ct) { Ok(pt) => { assert_eq!(pt, test.pt.as_ref()); assert!(!test.result.must_fail()); } Err(_) => { assert!(test.result.must_fail()); } } } } } Ok(()) } #[test] fn wycheproof_rsa_pkcs1_verify_tests() -> Result<(), botan::Error> { use wycheproof::rsa_pkcs1_verify::*; for test_name in TestName::all() { let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let hash = match hash_id_to_str(test_group.hash) { Some(hash) => hash, None => continue, }; let key = botan::Pubkey::load_der(&test_group.der)?; let mut verifier = botan::Verifier::new(&key, &format!("EMSA_PKCS1({})", hash))?; for test in &test_group.tests { verifier.update(&test.msg)?; let accept = verifier.finish(&test.sig)?; match (accept, test.result) { (true, TestResult::Valid) => {} (true, TestResult::Acceptable) => {} (true, TestResult::Invalid) => { panic!("Accepted an invalid signature"); } (false, TestResult::Valid) => { panic!("Rejected a valid signature"); } (false, TestResult::Acceptable) => {} (false, TestResult::Invalid) => {} } } } } Ok(()) } #[test] fn wycheproof_rsa_pss_verify_tests() -> Result<(), botan::Error> { use wycheproof::rsa_pss_verify::*; let is_botan2 = botan::Version::current()?.major == 2; fn form_pssr_format(group: &TestGroup) -> Option { // MGF hash != hash -> not supported // Something other than MGF1 -> not supported match (group.hash, group.mgf, group.mgf_hash) { (h1, wycheproof::Mgf::Mgf1, Some(h2)) if h1 == h2 => {} (_, _, _) => return None, } let hash = match hash_id_to_str(group.hash) { Some(hash) => hash, None => return None, }; Some(format!("EMSA4({},MGF1,{})", hash, group.salt_size)) } for test_name in TestName::all() { let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let key = botan::Pubkey::load_der(&test_group.der)?; let pssr_config = match form_pssr_format(test_group) { Some(config) => config, None => continue, }; let mut verifier = botan::Verifier::new(&key, &pssr_config)?; for test in &test_group.tests { if is_botan2 && test.comment == "prepending 0's to signature" { continue; } verifier.update(&test.msg)?; let accept = verifier.finish(&test.sig)?; match (accept, test.result) { (true, TestResult::Valid) => {} (true, TestResult::Acceptable) => {} (true, TestResult::Invalid) => { panic!("Accepted an invalid signature ({})", test.comment); } (false, TestResult::Valid) => { panic!("Rejected a valid signature ({})", test.comment); } (false, TestResult::Acceptable) => {} (false, TestResult::Invalid) => {} } } } } Ok(()) } #[test] fn wycheproof_dsa_verify_tests() -> Result<(), botan::Error> { use wycheproof::dsa::*; for test_name in TestName::all() { let is_ieee = format!("{:?}", test_name).contains("P1363"); let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let hash = match hash_id_to_str(test_group.hash) { Some(hash) => hash, None => continue, }; let key = botan::Pubkey::load_der(&test_group.der)?; for test in &test_group.tests { // Has to be inside the loop to work around the bug addressed in // https://github.com/randombit/botan/pull/3333 let mut verifier = if is_ieee { botan::Verifier::new(&key, &format!("EMSA1({})", hash))? } else { botan::Verifier::new_with_der_formatted_signatures( &key, &format!("EMSA1({})", hash), )? }; verifier.update(&test.msg)?; let accept = verifier.finish(&test.sig)?; match (accept, test.result) { (true, TestResult::Valid) => {} (true, TestResult::Acceptable) => { panic!("Accepted an 'acceptable' signature"); } (true, TestResult::Invalid) => { panic!("Accepted an invalid signature"); } (false, TestResult::Valid) => { panic!("Rejected a valid signature"); } (false, TestResult::Acceptable) => {} (false, TestResult::Invalid) => {} } } } } Ok(()) } fn curve_id_to_str(curve: EllipticCurve) -> Option<&'static str> { match curve { EllipticCurve::Secp160r1 => Some("secp160r1"), EllipticCurve::Secp160r2 => Some("secp160r2"), EllipticCurve::Secp160k1 => Some("secp160k1"), EllipticCurve::Secp192r1 => Some("secp192r1"), EllipticCurve::Secp192k1 => Some("secp192k1"), EllipticCurve::Secp224r1 => Some("secp224r1"), EllipticCurve::Secp256r1 => Some("secp256r1"), EllipticCurve::Secp384r1 => Some("secp384r1"), EllipticCurve::Secp521r1 => Some("secp521r1"), EllipticCurve::Secp224k1 => Some("secp224k1"), EllipticCurve::Secp256k1 => Some("secp256k1"), EllipticCurve::Brainpool224r1 => Some("brainpool224r1"), EllipticCurve::Brainpool256r1 => Some("brainpool256r1"), EllipticCurve::Brainpool320r1 => Some("brainpool320r1"), EllipticCurve::Brainpool384r1 => Some("brainpool384r1"), EllipticCurve::Brainpool512r1 => Some("brainpool512r1"), EllipticCurve::Brainpool224t1 => None, EllipticCurve::Brainpool256t1 => None, EllipticCurve::Brainpool320t1 => None, EllipticCurve::Brainpool384t1 => None, EllipticCurve::Brainpool512t1 => None, } } #[test] fn wycheproof_ecdsa_verify_tests() -> Result<(), botan::Error> { use wycheproof::ecdsa::*; let is_botan2 = botan::Version::current()?.major == 2; for test_name in TestName::all() { if test_name == TestName::EcdsaSecp256k1Sha256Bitcoin { continue; } let is_ieee = format!("{:?}", test_name).contains("P1363") || format!("{:?}", test_name).contains("Webcrypto"); let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { if curve_id_to_str(test_group.key.curve) == None { continue; } let hash = match hash_id_to_str(test_group.hash) { Some(hash) => hash, None => continue, }; if is_botan2 { // https://github.com/randombit/botan/issues/2841 match (test_group.key.curve, hash) { (EllipticCurve::Secp160k1, "SHA-256") => continue, (EllipticCurve::Secp160r1, "SHA-256") => continue, (EllipticCurve::Secp160r2, "SHA-256") => continue, (EllipticCurve::Secp224k1, "SHA-256") => continue, (_, _) => {} } } let key = botan::Pubkey::load_der(&test_group.der)?; let mut verifier_ieee = botan::Verifier::new(&key, &format!("EMSA1({})", hash))?; for test in &test_group.tests { let accept = if is_ieee { verifier_ieee.update(&test.msg)?; verifier_ieee.finish(&test.sig)? } else { // Has to be inside the loop to work around the bug addressed in // https://github.com/randombit/botan/pull/3333 let mut verifier_der = botan::Verifier::new_with_der_formatted_signatures( &key, &format!("EMSA1({})", hash), )?; verifier_der.update(&test.msg)?; verifier_der.finish(&test.sig)? }; match (accept, test.result) { (true, TestResult::Valid) => {} (true, TestResult::Acceptable) => { panic!("Accepted an acceptable signature"); } (true, TestResult::Invalid) => { panic!("Accepted an invalid signature"); } (false, TestResult::Valid) => { panic!("Rejected a valid signature"); } (false, TestResult::Acceptable) => {} (false, TestResult::Invalid) => {} } } } } Ok(()) } #[test] fn wycheproof_eddsa_verify_tests() -> Result<(), botan::Error> { use wycheproof::eddsa::*; let is_botan2 = botan::Version::current()?.major == 2; for test_name in TestName::all() { if test_name == TestName::Ed448 { continue; } let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let key = botan::Pubkey::load_der(&test_group.der)?; let mut verifier = botan::Verifier::new(&key, "Pure")?; for test in &test_group.tests { if is_botan2 { if test.flags.contains(&TestFlag::SignatureMalleability) { continue; } if test.comment == "Signature with S just above the bound. [David Benjamin]" { continue; } } verifier.update(&test.msg)?; let accept = verifier.finish(&test.sig)?; match (accept, test.result) { (true, TestResult::Valid) => {} (true, TestResult::Acceptable) => { panic!("Accepted an acceptable signature"); } (true, TestResult::Invalid) => { panic!("Accepted an invalid signature"); } (false, TestResult::Valid) => { panic!("Rejected a valid signature"); } (false, TestResult::Acceptable) => {} (false, TestResult::Invalid) => {} } } } } Ok(()) } #[test] fn wycheproof_ecdh_tests() -> Result<(), botan::Error> { use wycheproof::ecdh::*; for test_name in TestName::all() { let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { let curve_id = match curve_id_to_str(test_group.curve) { Some(curve_id) => curve_id, None => continue, }; if test_group.encoding != EcdhEncoding::EcPoint { continue; } for test in &test_group.tests { let s = botan::MPI::new_from_bytes(&test.private_key)?; let priv_key = botan::Privkey::load_ecdh(&s, &curve_id)?; let mut ka = botan::KeyAgreement::new(&priv_key, "Raw")?; let shared_secret = ka.agree(0, &test.public_key, &[]); if test.result == TestResult::Valid || test.result == TestResult::Acceptable { match shared_secret { Ok(shared_secret) => assert_eq!(shared_secret, test.shared_secret.as_ref()), Err(e) => panic!("Unable to compute shared secret ({:?})", e), } } else { assert!(shared_secret.is_err()); } } } } Ok(()) } #[test] fn wycheproof_xdh_tests() -> Result<(), botan::Error> { use wycheproof::xdh::*; for test_name in TestName::all() { if test_name == TestName::X448 { continue; } let test_set = TestSet::load(test_name).expect("Loading tests failed"); for test_group in &test_set.test_groups { for test in &test_group.tests { let priv_key = botan::Privkey::load_x25519(&test.private_key)?; let mut ka = botan::KeyAgreement::new(&priv_key, "Raw")?; let shared_secret = ka.agree(0, &test.public_key, &[]); if test.result == TestResult::Valid || test.result == TestResult::Acceptable { match shared_secret { Ok(shared_secret) => assert_eq!(shared_secret, test.shared_secret.as_ref()), Err(e) => panic!("Unable to compute shared secret ({:?})", e), } } else { assert!(shared_secret.is_err()); } } } } Ok(()) }