rust-libp2p/protocols/secio/src/algo_support.rs
Pierre Krieger e3efc2dc9a
Make secio almost compile for asmjs/wasm (#519)
* Use the sha2 crate in the handshake

* Return a Digest in algo_support instead of a ring ref

* Switch to ed25519-dalek for keys

* Make ring more or less optional

* Switch to ed25519_dalek for the verification

* Extract the key exchange to its own module

* Remove the ring RNG from the handshake

* Some warning fixes and forgot file

* Move key exchange to own module

* Remove usage of ring::digest

* Remove ring from handshake entirely

* Implement ECDH for WebCrypto

* Remove the libp2p-secio feature

* Fix ring being included

* Address some concerns

* Provde some panics in WebCrypto

* Prove the Hmac panic

* Prove more panics
2018-10-01 15:42:40 +02:00

217 lines
6.9 KiB
Rust

// 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 error::SecioError;
#[cfg(all(feature = "ring", not(target_os = "emscripten")))]
use ring::digest;
use std::cmp::Ordering;
use stream_cipher::Cipher;
use 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<Item=&'a KeyAgreement>
{
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<KeyAgreement, SecioError> {
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<Item=&'a Cipher>
{
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<Cipher, SecioError> {
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<Item=&'a Digest>
{
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<Digest, SecioError> {
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(all(feature = "ring", not(target_os = "emscripten")))]
impl Into<&'static digest::Algorithm> for Digest {
#[inline]
fn into(self) -> &'static digest::Algorithm {
match self {
Digest::Sha256 => &digest::SHA256,
Digest::Sha512 => &digest::SHA512,
}
}
}