// Copyright 2017 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //! This module contains some utilities for algorithm support exchange. //! //! One important part of the SECIO handshake is negotiating algorithms. This is what this module //! helps you with. use crate::error::SecioError; #[cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))] use ring::digest; use std::cmp::Ordering; use crate::stream_cipher::Cipher; use crate::KeyAgreement; const ECDH_P256: &str = "P-256"; const ECDH_P384: &str = "P-384"; const AES_128: &str = "AES-128"; const AES_256: &str = "AES-256"; const TWOFISH_CTR: &str = "TwofishCTR"; const NULL: &str = "NULL"; const SHA_256: &str = "SHA256"; const SHA_512: &str = "SHA512"; pub(crate) const DEFAULT_AGREEMENTS_PROPOSITION: &str = "P-256,P-384"; pub(crate) const DEFAULT_CIPHERS_PROPOSITION: &str = "AES-128,AES-256,TwofishCTR"; pub(crate) const DEFAULT_DIGESTS_PROPOSITION: &str = "SHA256,SHA512"; /// Return a proposition string from the given sequence of `KeyAgreement` values. pub fn key_agreements_proposition<'a, I>(xchgs: I) -> String where I: IntoIterator { let mut s = String::new(); for x in xchgs { match x { KeyAgreement::EcdhP256 => { s.push_str(ECDH_P256); s.push(',') } KeyAgreement::EcdhP384 => { s.push_str(ECDH_P384); s.push(',') } } } s.pop(); // remove trailing comma if any s } /// Given two key agreement proposition strings try to figure out a match. /// /// The `Ordering` parameter determines which argument is preferred. If `Less` or `Equal` we /// try for each of `theirs` every one of `ours`, for `Greater` it's the other way around. pub fn select_agreement(r: Ordering, ours: &str, theirs: &str) -> Result { let (a, b) = match r { Ordering::Less | Ordering::Equal => (theirs, ours), Ordering::Greater => (ours, theirs) }; for x in a.split(',') { if b.split(',').any(|y| x == y) { match x { ECDH_P256 => return Ok(KeyAgreement::EcdhP256), ECDH_P384 => return Ok(KeyAgreement::EcdhP384), _ => continue } } } Err(SecioError::NoSupportIntersection) } /// Return a proposition string from the given sequence of `Cipher` values. pub fn ciphers_proposition<'a, I>(ciphers: I) -> String where I: IntoIterator { let mut s = String::new(); for c in ciphers { match c { Cipher::Aes128 => { s.push_str(AES_128); s.push(',') } Cipher::Aes256 => { s.push_str(AES_256); s.push(',') } Cipher::TwofishCtr => { s.push_str(TWOFISH_CTR); s.push(',') } Cipher::Null => { s.push_str(NULL); s.push(',') } } } s.pop(); // remove trailing comma if any s } /// Given two cipher proposition strings try to figure out a match. /// /// The `Ordering` parameter determines which argument is preferred. If `Less` or `Equal` we /// try for each of `theirs` every one of `ours`, for `Greater` it's the other way around. pub fn select_cipher(r: Ordering, ours: &str, theirs: &str) -> Result { let (a, b) = match r { Ordering::Less | Ordering::Equal => (theirs, ours), Ordering::Greater => (ours, theirs) }; for x in a.split(',') { if b.split(',').any(|y| x == y) { match x { AES_128 => return Ok(Cipher::Aes128), AES_256 => return Ok(Cipher::Aes256), TWOFISH_CTR => return Ok(Cipher::TwofishCtr), NULL => return Ok(Cipher::Null), _ => continue } } } Err(SecioError::NoSupportIntersection) } /// Possible digest algorithms. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Digest { Sha256, Sha512 } impl Digest { /// Returns the size in bytes of a digest of this kind. #[inline] pub fn num_bytes(&self) -> usize { match *self { Digest::Sha256 => 256 / 8, Digest::Sha512 => 512 / 8, } } } /// Return a proposition string from the given sequence of `Digest` values. pub fn digests_proposition<'a, I>(digests: I) -> String where I: IntoIterator { let mut s = String::new(); for d in digests { match d { Digest::Sha256 => { s.push_str(SHA_256); s.push(',') } Digest::Sha512 => { s.push_str(SHA_512); s.push(',') } } } s.pop(); // remove trailing comma if any s } /// Given two digest proposition strings try to figure out a match. /// /// The `Ordering` parameter determines which argument is preferred. If `Less` or `Equal` we /// try for each of `theirs` every one of `ours`, for `Greater` it's the other way around. pub fn select_digest(r: Ordering, ours: &str, theirs: &str) -> Result { let (a, b) = match r { Ordering::Less | Ordering::Equal => (theirs, ours), Ordering::Greater => (ours, theirs) }; for x in a.split(',') { if b.split(',').any(|y| x == y) { match x { SHA_256 => return Ok(Digest::Sha256), SHA_512 => return Ok(Digest::Sha512), _ => continue } } } Err(SecioError::NoSupportIntersection) } #[cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))] impl Into<&'static digest::Algorithm> for Digest { #[inline] fn into(self) -> &'static digest::Algorithm { match self { Digest::Sha256 => &digest::SHA256, Digest::Sha512 => &digest::SHA512, } } } #[cfg(test)] mod tests { #[test] fn cipher_non_null() { // This test serves as a safe-guard against accidentally pushing to master a commit that // sets this constant to `NULL`. assert!(!super::DEFAULT_CIPHERS_PROPOSITION.contains("NULL")); } }