mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-03 21:21:23 +00:00
* 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
217 lines
6.9 KiB
Rust
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,
|
|
}
|
|
}
|
|
}
|