From 6b2742d9092d299385321a4eb2a412bc06abe599 Mon Sep 17 00:00:00 2001 From: yngrtc Date: Tue, 19 Dec 2023 09:29:19 -0800 Subject: [PATCH] fix rtc with retty pipelines --- Cargo.toml | 1 + dtls/Cargo.toml | 53 ++--- dtls/examples/dtls_client_selfsign.rs | 206 ++++++++++++++++++ dtls/src/config.rs | 22 +- dtls/src/crypto/crypto_cbc.rs | 26 +-- dtls/src/crypto/crypto_ccm.rs | 17 +- dtls/src/crypto/crypto_gcm.rs | 13 +- dtls/src/crypto/crypto_test.rs | 12 +- dtls/src/crypto/mod.rs | 120 +++++----- dtls/src/crypto/padding.rs | 123 ----------- .../dtls_handlers/dtls_endpoint_handler.rs | 2 +- dtls/src/endpoint.rs | 2 +- dtls/src/flight/flight5.rs | 1 - dtls/src/fragment_buffer/mod.rs | 2 +- .../receiver_estimated_maximum_bitrate/mod.rs | 2 +- rtp/Cargo.toml | 2 +- rtp/src/codecs/vp8/mod.rs | 2 +- rtp/src/codecs/vp9/mod.rs | 2 +- .../abs_send_time_extension_test.rs | 4 +- rtp/src/packetizer/packetizer_test.rs | 2 +- sctp/Cargo.toml | 1 + sctp/src/endpoint/endpoint_test.rs | 11 +- sctp/src/endpoint/mod.rs | 20 +- sctp/src/lib.rs | 3 +- sctp/src/shared.rs | 27 --- shared/Cargo.toml | 10 +- shared/src/error.rs | 16 +- stun/src/attributes.rs | 1 + stun/src/integrity.rs | 2 +- stun/src/xoraddr.rs | 2 +- 30 files changed, 361 insertions(+), 346 deletions(-) create mode 100644 dtls/examples/dtls_client_selfsign.rs delete mode 100644 dtls/src/crypto/padding.rs diff --git a/Cargo.toml b/Cargo.toml index 7dd9936..6540fa9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "stun", "turn", ] +resolver = "2" [profile.dev] opt-level = 0 diff --git a/dtls/Cargo.toml b/dtls/Cargo.toml index b4a46c6..f865e30 100644 --- a/dtls/Cargo.toml +++ b/dtls/Cargo.toml @@ -11,32 +11,31 @@ shared = { path = "../shared", package = "shared", default-features = false, fea retty = "0.24.0" bytes = "1.4.0" byteorder = "1" -rand_core = "0.6.3" -hkdf = "~0.12.1" -p256 = { version = "0.11.1", features = ["default", "ecdh", "ecdsa"] } -p384 = "0.11.2" -rand = "0.8.5" -hmac = "0.12.1" -sec1 = { version = "0.3.0", features = [ "std" ] } -sha1 = "0.10.5" -sha2 = "0.10.6" -aes = "0.6.0" -block-modes = "0.7.0" -aes-gcm = "0.10.1" -ccm = "0.3.0" -x25519-dalek = { version = "2.0.0-rc.2", features = ["static_secrets"] } -x509-parser = "0.13.2" +rand_core = "0.6" +hkdf = "0.12" +p256 = { version = "0.13", features = ["default", "ecdh", "ecdsa"] } +p384 = "0.13" +rand = "0.8" +hmac = "0.12" +sec1 = { version = "0.7", features = [ "std" ] } +sha1 = "0.10" +sha2 = "0.10" +aes = "0.8" +cbc = { version = "0.1", features = [ "block-padding", "alloc"] } +aes-gcm = "0.10" +ccm = "0.5" +x25519-dalek = { version = "2", features = ["static_secrets"] } +x509-parser = "0.15" der-parser = "8.1" -rcgen = "0.10.0" -ring = "0.16.19" -webpki = "0.21.4" -rustls = { version = "0.19.0", features = ["dangerous_configuration"]} -bincode = "1.3" -serde = { version = "1.0.110", features = ["derive"] } -subtle = "2.4" -log = "0.4.16" -thiserror = "1.0" -pem = { version = "1", optional = true } +rcgen = "0.11" +ring = "0.17" +rustls = { version = "0.21", features = ["dangerous_configuration"]} +bincode = "1" +serde = { version = "1", features = ["derive"] } +subtle = "2" +log = "0.4" +thiserror = "1" +pem = { version = "3", optional = true } [dev-dependencies] local-sync = "0.1.0" @@ -63,6 +62,10 @@ path = "examples/dtls_echo_server.rs" name = "dtls_client" path = "examples/dtls_client.rs" +[[example]] +name = "dtls_client_selfsign" +path = "examples/dtls_client_selfsign.rs" + #[[example]] #name = "dial_psk" #path = "examples/dial/psk/dial_psk.rs" diff --git a/dtls/examples/dtls_client_selfsign.rs b/dtls/examples/dtls_client_selfsign.rs new file mode 100644 index 0000000..a8841be --- /dev/null +++ b/dtls/examples/dtls_client_selfsign.rs @@ -0,0 +1,206 @@ +use bytes::BytesMut; +use clap::Parser; +use futures::StreamExt; +use std::{io::Write, net::SocketAddr, str::FromStr, time::Instant}; + +use dtls::config::{ConfigBuilder, ExtendedMasterSecretType}; +use dtls::crypto::Certificate; +use dtls::dtls_handlers::dtls_endpoint_handler::DtlsEndpointHandler; + +use retty::bootstrap::BootstrapUdpClient; +use retty::channel::{ + Handler, InboundContext, InboundHandler, OutboundContext, OutboundHandler, Pipeline, +}; +use retty::codec::string_codec::TaggedString; +use retty::executor::{yield_local, LocalExecutorBuilder}; +use retty::transport::{AsyncTransport, AsyncTransportWrite, TaggedBytesMut, TransportContext}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +struct EchoDecoder; +struct EchoEncoder; +struct EchoHandler { + decoder: EchoDecoder, + encoder: EchoEncoder, +} + +impl EchoHandler { + fn new() -> Self { + EchoHandler { + decoder: EchoDecoder, + encoder: EchoEncoder, + } + } +} + +impl InboundHandler for EchoDecoder { + type Rin = TaggedBytesMut; + type Rout = TaggedString; + + fn read(&mut self, _ctx: &InboundContext, msg: Self::Rin) { + let message = String::from_utf8(msg.message.to_vec()).unwrap(); + println!( + "received back: {} from {:?}", + message, msg.transport.peer_addr + ); + } + fn poll_timeout(&mut self, _ctx: &InboundContext, _eto: &mut Instant) { + //last handler, no need to fire_poll_timeout + } +} + +impl OutboundHandler for EchoEncoder { + type Win = TaggedString; + type Wout = TaggedBytesMut; + + fn write(&mut self, ctx: &OutboundContext, msg: Self::Win) { + ctx.fire_write(TaggedBytesMut { + now: msg.now, + transport: msg.transport, + message: BytesMut::from(msg.message.as_bytes()), + }); + } +} + +impl Handler for EchoHandler { + type Rin = TaggedBytesMut; + type Rout = TaggedString; + type Win = TaggedString; + type Wout = TaggedBytesMut; + + fn name(&self) -> &str { + "EchoHandler" + } + + fn split( + self, + ) -> ( + Box>, + Box>, + ) { + (Box::new(self.decoder), Box::new(self.encoder)) + } +} + +#[derive(Parser)] +#[command(name = "DTLS Echo Client")] +#[command(author = "Rusty Rain ")] +#[command(version = "0.1.0")] +#[command(about = "An example of dtls client", long_about = None)] +struct Cli { + #[arg(short, long)] + debug: bool, + #[arg(long, default_value_t = format!("127.0.0.1"))] + host: String, + #[arg(long, default_value_t = 3489)] + port: u16, + #[arg(long, default_value_t = format!("DEBUG"))] + log_level: String, +} + +fn main() -> anyhow::Result<()> { + let cli = Cli::parse(); + let host = cli.host; + let port = cli.port; + let log_level = log::LevelFilter::from_str(&cli.log_level)?; + if cli.debug { + env_logger::Builder::new() + .format(|buf, record| { + writeln!( + buf, + "{}:{} [{}] {} - {}", + record.file().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.level(), + chrono::Local::now().format("%H:%M:%S.%6f"), + record.args() + ) + }) + .filter(None, log_level) + .init(); + } + + println!("Connecting {}:{}...", host, port); + + let transport = TransportContext { + local_addr: SocketAddr::from_str("0.0.0.0:0")?, + peer_addr: SocketAddr::from_str(&format!("{}:{}", host, port))?, + ecn: None, + }; + + LocalExecutorBuilder::default().run(async move { + let certificate = Certificate::generate_self_signed(vec!["localhost".to_owned()]).unwrap(); + + let handshake_config = ConfigBuilder::default() + .with_certificates(vec![certificate]) + .with_insecure_skip_verify(true) + .with_extended_master_secret(ExtendedMasterSecretType::Require) + .build(true, Some(transport.peer_addr)) + .unwrap(); + + let mut bootstrap = BootstrapUdpClient::new(); + bootstrap.pipeline(Box::new( + move |writer: AsyncTransportWrite| { + let pipeline: Pipeline = Pipeline::new(); + + let local_addr = writer.get_local_addr(); + let peer_addr = writer.get_peer_addr(); + + let async_transport_handler = AsyncTransport::new(writer); + let dtls_handler = DtlsEndpointHandler::new( + local_addr, + handshake_config.clone(), + true, + peer_addr, + None, + ); + let echo_handler = EchoHandler::new(); + + pipeline.add_back(async_transport_handler); + pipeline.add_back(dtls_handler); + pipeline.add_back(echo_handler); + pipeline.finalize() + }, + )); + + bootstrap.bind(transport.local_addr).await.unwrap(); + + let pipeline = bootstrap.connect(transport.peer_addr).await.unwrap(); + yield_local(); + + println!("Enter bye to stop"); + let (mut tx, mut rx) = futures::channel::mpsc::channel(8); + std::thread::spawn(move || { + let mut buffer = String::new(); + while std::io::stdin().read_line(&mut buffer).is_ok() { + match buffer.trim_end() { + "" => break, + line => { + if tx.try_send(line.to_string()).is_err() { + break; + } + if line == "bye" { + break; + } + } + }; + buffer.clear(); + } + }); + while let Some(line) = rx.next().await { + pipeline.write(TaggedString { + now: Instant::now(), + transport, + message: format!("{}\r\n", line), + }); + if line == "bye" { + pipeline.close(); + break; + } + } + + bootstrap.graceful_stop().await; + }); + + Ok(()) +} diff --git a/dtls/src/config.rs b/dtls/src/config.rs index 1bb4bfe..e3c49d4 100644 --- a/dtls/src/config.rs +++ b/dtls/src/config.rs @@ -10,7 +10,6 @@ use std::collections::HashMap; use std::fmt; use std::net::SocketAddr; use std::rc::Rc; -use std::sync::Arc; use std::time::Duration; /// Config is used to configure a DTLS client or server. @@ -334,13 +333,11 @@ impl ConfigBuilder { insecure_verification: self.insecure_verification, verify_peer_certificate: self.verify_peer_certificate.take(), roots_cas: self.roots_cas, - client_cert_verifier: if self.client_auth as u8 - >= ClientAuthType::VerifyClientCertIfGiven as u8 - { - Some(rustls::AllowAnyAuthenticatedClient::new(self.client_cas)) - } else { - None - }, + server_cert_verifier: Rc::new(rustls::client::WebPkiVerifier::new( + rustls::RootCertStore::empty(), + None, + )), + client_cert_verifier: None, retransmit_interval, initial_epoch: 0, maximum_transmission_unit, @@ -369,8 +366,8 @@ pub struct HandshakeConfig { pub(crate) insecure_verification: bool, pub(crate) verify_peer_certificate: Option, pub(crate) roots_cas: rustls::RootCertStore, - pub(crate) server_cert_verifier: Rc, - pub(crate) client_cert_verifier: Option>, + pub(crate) server_cert_verifier: Rc, + pub(crate) client_cert_verifier: Option>, pub(crate) retransmit_interval: std::time::Duration, pub(crate) initial_epoch: u16, pub(crate) maximum_transmission_unit: usize, @@ -420,7 +417,10 @@ impl Default for HandshakeConfig { insecure_verification: false, verify_peer_certificate: None, roots_cas: rustls::RootCertStore::empty(), - server_cert_verifier: Rc::new(rustls::WebPKIVerifier::new()), + server_cert_verifier: Rc::new(rustls::client::WebPkiVerifier::new( + rustls::RootCertStore::empty(), + None, + )), client_cert_verifier: None, retransmit_interval: std::time::Duration::from_secs(0), initial_epoch: 0, diff --git a/dtls/src/crypto/crypto_cbc.rs b/dtls/src/crypto/crypto_cbc.rs index d1fe935..a9a79bd 100644 --- a/dtls/src/crypto/crypto_cbc.rs +++ b/dtls/src/crypto/crypto_cbc.rs @@ -8,6 +8,9 @@ // https://github.com/RustCrypto/block-ciphers +use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +use p256::elliptic_curve::subtle::ConstantTimeEq; +use rand::Rng; use std::io::Cursor; use std::ops::Not; @@ -15,15 +18,8 @@ use crate::content::*; use crate::prf::*; use crate::record_layer::record_layer_header::*; use shared::error::*; - -use aes::Aes256; -use block_modes::BlockModeError; -use block_modes::{BlockMode, Cbc}; -use rand::Rng; -use subtle::ConstantTimeEq; - -use super::padding::DtlsPadding; -type Aes256Cbc = Cbc; +type Aes256CbcEnc = cbc::Encryptor; +type Aes256CbcDec = cbc::Decryptor; // State needed to handle encrypted input/output #[derive(Clone)] @@ -73,8 +69,8 @@ impl CryptoCbc { let mut iv: Vec = vec![0; Self::BLOCK_SIZE]; rand::thread_rng().fill(iv.as_mut_slice()); - let write_cbc = Aes256Cbc::new_var(&self.local_key, &iv)?; - let encrypted = write_cbc.encrypt_vec(&payload); + let write_cbc = Aes256CbcEnc::new_from_slices(&self.local_key, &iv)?; + let encrypted = write_cbc.encrypt_padded_vec_mut::(&payload); // Prepend unencrypte header with encrypted payload let mut r = vec![]; @@ -102,9 +98,11 @@ impl CryptoCbc { let body = &body[Self::BLOCK_SIZE..]; //TODO: add body.len() check - let read_cbc = Aes256Cbc::new_var(&self.remote_key, iv)?; + let read_cbc = Aes256CbcDec::new_from_slices(&self.remote_key, iv)?; - let decrypted = read_cbc.decrypt_vec(body)?; + let decrypted = read_cbc + .decrypt_padded_vec_mut::(body) + .map_err(|_| Error::ErrInvalidPacketLength)?; let recv_mac = &decrypted[decrypted.len() - Self::MAC_SIZE..]; let decrypted = &decrypted[0..decrypted.len() - Self::MAC_SIZE]; @@ -118,7 +116,7 @@ impl CryptoCbc { )?; if recv_mac.ct_eq(&mac).not().into() { - return Err(BlockModeError.into()); + return Err(Error::ErrInvalidMac); } let mut d = Vec::with_capacity(RECORD_LAYER_HEADER_SIZE + decrypted.len()); diff --git a/dtls/src/crypto/crypto_ccm.rs b/dtls/src/crypto/crypto_ccm.rs index e59eef2..77269f4 100644 --- a/dtls/src/crypto/crypto_ccm.rs +++ b/dtls/src/crypto/crypto_ccm.rs @@ -8,22 +8,21 @@ // https://github.com/RustCrypto/AEADs // https://docs.rs/ccm/0.3.0/ccm/ Or https://crates.io/crates/aes-ccm? -use rand::Rng; - use std::io::Cursor; +use aes::Aes128; +use ccm::aead::generic_array::GenericArray; +use ccm::aead::AeadInPlace; +use ccm::consts::{U12, U16, U8}; +use ccm::Ccm; +use ccm::KeyInit; +use rand::Rng; + use super::*; use crate::content::*; use crate::record_layer::record_layer_header::*; use shared::error::*; -use aes::Aes128; -use ccm::aead::{generic_array::GenericArray, AeadInPlace, NewAead}; -use ccm::{ - consts::{U12, U16, U8}, - Ccm, -}; - const CRYPTO_CCM_8_TAG_LENGTH: usize = 8; const CRYPTO_CCM_TAG_LENGTH: usize = 16; const CRYPTO_CCM_NONCE_LENGTH: usize = 12; diff --git a/dtls/src/crypto/crypto_gcm.rs b/dtls/src/crypto/crypto_gcm.rs index cd62140..3ecad51 100644 --- a/dtls/src/crypto/crypto_gcm.rs +++ b/dtls/src/crypto/crypto_gcm.rs @@ -6,18 +6,17 @@ // https://github.com/RustCrypto/AEADs // https://docs.rs/aes-gcm/0.8.0/aes_gcm/ -use aes_gcm::KeyInit; -use rand::Rng; - use std::io::Cursor; +use aes_gcm::aead::generic_array::GenericArray; +use aes_gcm::aead::AeadInPlace; +use aes_gcm::{Aes128Gcm, KeyInit}; +use rand::Rng; + use super::*; use crate::content::*; use crate::record_layer::record_layer_header::*; -use shared::error::*; - -use aes_gcm::aead::{generic_array::GenericArray, AeadInPlace}; -use aes_gcm::Aes128Gcm; // what about Aes256Gcm? +use shared::error::*; // what about Aes256Gcm? const CRYPTO_GCM_TAG_LENGTH: usize = 16; const CRYPTO_GCM_NONCE_LENGTH: usize = 12; diff --git a/dtls/src/crypto/crypto_test.rs b/dtls/src/crypto/crypto_test.rs index 515772f..b47c4dc 100644 --- a/dtls/src/crypto/crypto_test.rs +++ b/dtls/src/crypto/crypto_test.rs @@ -1,13 +1,12 @@ +use std::io::Cursor; + +use x509_parser::pem::Pem; + use super::crypto_ccm::*; use super::*; - use crate::content::ContentType; use crate::record_layer::record_layer_header::{ProtocolVersion, RECORD_LAYER_HEADER_SIZE}; -use std::io::Cursor; - -use x509_parser::pem::Pem; - const RAW_PRIVATE_KEY: &str = " -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAxIA2BrrnR2sIlATsp7aRBD/3krwZ7vt9dNeoDQAee0s6SuYP @@ -90,7 +89,8 @@ fn test_generate_key_signature() -> Result<()> { NamedCurve::X25519, &CryptoPrivateKey { kind: CryptoPrivateKeyKind::Rsa256( - RsaKeyPair::from_der(&pem.contents).map_err(|e| Error::Other(e.to_string()))?, + ring::rsa::KeyPair::from_der(&pem.contents) + .map_err(|e| Error::Other(e.to_string()))?, ), serialized_der: pem.contents.clone(), }, //hashAlgorithmSHA256, diff --git a/dtls/src/crypto/mod.rs b/dtls/src/crypto/mod.rs index fc86b75..9929bbe 100644 --- a/dtls/src/crypto/mod.rs +++ b/dtls/src/crypto/mod.rs @@ -4,21 +4,21 @@ mod crypto_test; pub mod crypto_cbc; pub mod crypto_ccm; pub mod crypto_gcm; -pub mod padding; + +use std::convert::TryFrom; +use std::rc::Rc; + +use der_parser::oid; +use der_parser::oid::Oid; +use rcgen::KeyPair; +use ring::rand::SystemRandom; +use ring::signature::{EcdsaKeyPair, Ed25519KeyPair}; use crate::curve::named_curve::*; use crate::record_layer::record_layer_header::*; use crate::signature_hash_algorithm::{HashAlgorithm, SignatureAlgorithm, SignatureHashAlgorithm}; use shared::error::*; -use der_parser::{oid, oid::Oid}; -use rcgen::KeyPair; -use ring::rand::SystemRandom; -use ring::signature::{EcdsaKeyPair, Ed25519KeyPair, RsaKeyPair}; -use std::convert::TryFrom; -use std::rc::Rc; -use std::sync::Arc; - /// A X.509 certificate(s) used to authenticate a DTLS connection. #[derive(Clone, PartialEq, Debug)] pub struct Certificate { @@ -70,25 +70,25 @@ impl Certificate { pems.len() ))); } - if pems[0].tag != "PRIVATE_KEY" { + if pems[0].tag() != "PRIVATE_KEY" { return Err(Error::InvalidPEM(format!( "invalid tag (expected: 'PRIVATE_KEY', got: '{}')", - pems[0].tag + pems[0].tag() ))); } - let keypair = rcgen::KeyPair::from_der(&pems[0].contents) + let keypair = rcgen::KeyPair::from_der(pems[0].contents()) .map_err(|e| Error::InvalidPEM(format!("can't decode keypair: {e}")))?; let mut rustls_certs = Vec::new(); for p in pems.drain(1..) { - if p.tag != "CERTIFICATE" { + if p.tag() != "CERTIFICATE" { return Err(Error::InvalidPEM(format!( "invalid tag (expected: 'CERTIFICATE', got: '{}')", - p.tag + p.tag() ))); } - rustls_certs.push(rustls::Certificate(p.contents)); + rustls_certs.push(rustls::Certificate(p.contents().to_vec())); } Ok(Certificate { @@ -100,15 +100,15 @@ impl Certificate { /// Serializes the certificate (including the private key) in PKCS#8 format in PEM. #[cfg(feature = "pem")] pub fn serialize_pem(&self) -> String { - let mut data = vec![pem::Pem { - tag: "PRIVATE_KEY".to_string(), - contents: self.private_key.serialized_der.clone(), - }]; + let mut data = vec![pem::Pem::new( + "PRIVATE_KEY".to_string(), + self.private_key.serialized_der.clone(), + )]; for rustls_cert in &self.certificate { - data.push(pem::Pem { - tag: "CERTIFICATE".to_string(), - contents: rustls_cert.0.clone(), - }); + data.push(pem::Pem::new( + "CERTIFICATE".to_string(), + rustls_cert.0.clone(), + )); } pem::encode_many(&data) } @@ -139,7 +139,7 @@ pub(crate) fn value_key_message( pub enum CryptoPrivateKeyKind { Ed25519(Ed25519KeyPair), Ecdsa256(EcdsaKeyPair), - Rsa256(RsaKeyPair), + Rsa256(ring::rsa::KeyPair), } /// Private key. @@ -187,6 +187,7 @@ impl Clone for CryptoPrivateKey { EcdsaKeyPair::from_pkcs8( &ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING, &self.serialized_der, + &SystemRandom::new(), ) .unwrap(), ), @@ -194,7 +195,7 @@ impl Clone for CryptoPrivateKey { }, CryptoPrivateKeyKind::Rsa256(_) => CryptoPrivateKey { kind: CryptoPrivateKeyKind::Rsa256( - RsaKeyPair::from_pkcs8(&self.serialized_der).unwrap(), + ring::rsa::KeyPair::from_pkcs8(&self.serialized_der).unwrap(), ), serialized_der: self.serialized_der.clone(), }, @@ -206,37 +207,7 @@ impl TryFrom<&KeyPair> for CryptoPrivateKey { type Error = Error; fn try_from(key_pair: &KeyPair) -> Result { - let serialized_der = key_pair.serialize_der(); - if key_pair.is_compatible(&rcgen::PKCS_ED25519) { - Ok(CryptoPrivateKey { - kind: CryptoPrivateKeyKind::Ed25519( - Ed25519KeyPair::from_pkcs8(&serialized_der) - .map_err(|e| Error::Other(e.to_string()))?, - ), - serialized_der, - }) - } else if key_pair.is_compatible(&rcgen::PKCS_ECDSA_P256_SHA256) { - Ok(CryptoPrivateKey { - kind: CryptoPrivateKeyKind::Ecdsa256( - EcdsaKeyPair::from_pkcs8( - &ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING, - &serialized_der, - ) - .map_err(|e| Error::Other(e.to_string()))?, - ), - serialized_der, - }) - } else if key_pair.is_compatible(&rcgen::PKCS_RSA_SHA256) { - Ok(CryptoPrivateKey { - kind: CryptoPrivateKeyKind::Rsa256( - RsaKeyPair::from_pkcs8(&serialized_der) - .map_err(|e| Error::Other(e.to_string()))?, - ), - serialized_der, - }) - } else { - Err(Error::Other("Unsupported key_pair".to_owned())) - } + Self::from_key_pair(key_pair) } } @@ -257,6 +228,7 @@ impl CryptoPrivateKey { EcdsaKeyPair::from_pkcs8( &ring::signature::ECDSA_P256_SHA256_ASN1_SIGNING, &serialized_der, + &SystemRandom::new(), ) .map_err(|e| Error::Other(e.to_string()))?, ), @@ -265,7 +237,7 @@ impl CryptoPrivateKey { } else if key_pair.is_compatible(&rcgen::PKCS_RSA_SHA256) { Ok(CryptoPrivateKey { kind: CryptoPrivateKeyKind::Rsa256( - RsaKeyPair::from_pkcs8(&serialized_der) + ring::rsa::KeyPair::from_pkcs8(&serialized_der) .map_err(|e| Error::Other(e.to_string()))?, ), serialized_der, @@ -300,7 +272,7 @@ pub(crate) fn generate_key_signature( } CryptoPrivateKeyKind::Rsa256(kp) => { let system_random = SystemRandom::new(); - let mut signature = vec![0; kp.public_modulus_len()]; + let mut signature = vec![0; kp.public().modulus_len()]; kp.sign( &ring::signature::RSA_PKCS1_SHA256, &system_random, @@ -365,7 +337,7 @@ fn verify_signature( _ => return Err(Error::ErrKeySignatureVerifyUnimplemented), }; - log::debug!("Picked an algorithm {:?}", verify_alg); + log::trace!("Picked an algorithm {:?}", verify_alg); let public_key = ring::signature::UnparsedPublicKey::new( verify_alg, @@ -422,7 +394,7 @@ pub(crate) fn generate_certificate_verify( } CryptoPrivateKeyKind::Rsa256(kp) => { let system_random = SystemRandom::new(); - let mut signature = vec![0; kp.public_modulus_len()]; + let mut signature = vec![0; kp.public().modulus_len()]; kp.sign( &ring::signature::RSA_PKCS1_SHA256, &system_random, @@ -470,11 +442,16 @@ pub(crate) fn load_certs(raw_certificates: &[Vec]) -> Result], - cert_verifier: &Arc, + cert_verifier: &Rc, ) -> Result> { let chains = load_certs(raw_certificates)?; - match cert_verifier.verify_client_cert(&chains, None) { + let (end_entity, intermediates) = chains + .split_first() + .ok_or(Error::ErrClientCertificateRequired)?; + + match cert_verifier.verify_client_cert(end_entity, intermediates, std::time::SystemTime::now()) + { Ok(_) => {} Err(err) => return Err(Error::Other(err.to_string())), }; @@ -484,17 +461,26 @@ pub(crate) fn verify_client_cert( pub(crate) fn verify_server_cert( raw_certificates: &[Vec], - cert_verifier: &Rc, - roots: &rustls::RootCertStore, + cert_verifier: &Rc, server_name: &str, ) -> Result> { let chains = load_certs(raw_certificates)?; - let dns_name = match webpki::DNSNameRef::try_from_ascii_str(server_name) { + let dns_name = match rustls::server::DnsName::try_from_ascii(server_name.as_ref()) { Ok(dns_name) => dns_name, Err(err) => return Err(Error::Other(err.to_string())), }; - match cert_verifier.verify_server_cert(roots, &chains, dns_name, &[]) { + let (end_entity, intermediates) = chains + .split_first() + .ok_or(Error::ErrServerMustHaveCertificate)?; + match cert_verifier.verify_server_cert( + end_entity, + intermediates, + &rustls::ServerName::DnsName(dns_name.to_owned()), + &mut [].into_iter(), + &[], + std::time::SystemTime::now(), + ) { Ok(_) => {} Err(err) => return Err(Error::Other(err.to_string())), }; @@ -523,7 +509,7 @@ mod test { #[cfg(feature = "pem")] #[test] - fn test_certificate_serialize_pem_and_from_pem() -> Result<()> { + fn test_certificate_serialize_pem_and_from_pem() -> crate::error::Result<()> { let cert = Certificate::generate_self_signed(vec!["webrtc.rs".to_owned()])?; let pem = cert.serialize_pem(); diff --git a/dtls/src/crypto/padding.rs b/dtls/src/crypto/padding.rs deleted file mode 100644 index 509c979..0000000 --- a/dtls/src/crypto/padding.rs +++ /dev/null @@ -1,123 +0,0 @@ -use block_modes::block_padding::{PadError, Padding, UnpadError}; - -pub enum DtlsPadding {} -/// Reference: RFC5246, 6.2.3.2 -impl Padding for DtlsPadding { - fn pad_block(block: &mut [u8], pos: usize) -> Result<(), PadError> { - if pos == block.len() { - return Err(PadError); - } - - let padding_length = block.len() - pos - 1; - if padding_length > 255 { - return Err(PadError); - } - - set(&mut block[pos..], padding_length as u8); - - Ok(()) - } - - fn unpad(data: &[u8]) -> Result<&[u8], UnpadError> { - let padding_length = data.last().copied().unwrap_or(1) as usize; - if padding_length + 1 > data.len() { - return Err(UnpadError); - } - - let padding_begin = data.len() - padding_length - 1; - - if data[padding_begin..data.len() - 1] - .iter() - .any(|&byte| byte as usize != padding_length) - { - return Err(UnpadError); - } - - Ok(&data[0..padding_begin]) - } -} - -/// Sets all bytes in `dst` equal to `value` -#[inline(always)] -fn set(dst: &mut [u8], value: u8) { - // SAFETY: we overwrite valid memory behind `dst` - // note: loop is not used here because it produces - // unnecessary branch which tests for zero-length slices - unsafe { - core::ptr::write_bytes(dst.as_mut_ptr(), value, dst.len()); - } -} - -#[cfg(test)] -pub mod tests { - use rand::Rng; - - use super::*; - - #[test] - fn padding_length_is_amount_of_bytes_excluding_the_padding_length_itself( - ) -> Result<(), PadError> { - for original_length in 0..128 { - for padding_length in 0..(256 - original_length) { - let mut block = vec![0; original_length + padding_length + 1]; - rand::thread_rng().fill(&mut block[0..original_length]); - let original = block[0..original_length].to_vec(); - DtlsPadding::pad_block(&mut block, original_length)?; - - for byte in block[original_length..].iter() { - assert_eq!(*byte as usize, padding_length); - } - assert_eq!(block[0..original_length], original); - } - } - - Ok(()) - } - - #[test] - fn full_block_is_padding_error() { - for original_length in 0..256 { - let mut block = vec![0; original_length]; - let r = DtlsPadding::pad_block(&mut block, original_length); - assert!(r.is_err()); - } - } - - #[test] - fn padding_length_bigger_than_255_is_a_pad_error() { - let padding_length = 256; - for original_length in 0..128 { - let mut block = vec![0; original_length + padding_length + 1]; - let r = DtlsPadding::pad_block(&mut block, original_length); - - assert!(r.is_err()); - } - } - - #[test] - fn empty_block_is_unpadding_error() { - let r = DtlsPadding::unpad(&[]); - assert!(r.is_err()); - } - - #[test] - fn padding_too_big_for_block_is_unpadding_error() { - let r = DtlsPadding::unpad(&[1]); - assert!(r.is_err()); - } - - #[test] - fn one_of_the_padding_bytes_with_value_different_than_padding_length_is_unpadding_error() { - for padding_length in 0..16 { - for invalid_byte in 0..padding_length { - let mut block = vec![0; padding_length + 1]; - DtlsPadding::pad_block(&mut block, 0).unwrap(); - - assert_eq!(DtlsPadding::unpad(&block).ok(), Some(&[][..])); - block[invalid_byte] = (padding_length - 1) as u8; - let r = DtlsPadding::unpad(&block); - assert!(r.is_err()); - } - } - } -} diff --git a/dtls/src/dtls_handlers/dtls_endpoint_handler.rs b/dtls/src/dtls_handlers/dtls_endpoint_handler.rs index 648f63a..4b9d062 100644 --- a/dtls/src/dtls_handlers/dtls_endpoint_handler.rs +++ b/dtls/src/dtls_handlers/dtls_endpoint_handler.rs @@ -98,8 +98,8 @@ impl InboundHandler for DtlsEndpointInboundHandler { let try_dtls_read = || -> Result> { let mut endpoint = self.endpoint.borrow_mut(); let messages = endpoint.read( - msg.transport.peer_addr, msg.now, + msg.transport.peer_addr, Some(msg.transport.local_addr.ip()), msg.transport.ecn, msg.message, diff --git a/dtls/src/endpoint.rs b/dtls/src/endpoint.rs index f86d46a..6e27b20 100644 --- a/dtls/src/endpoint.rs +++ b/dtls/src/endpoint.rs @@ -101,8 +101,8 @@ impl Endpoint { /// Process an incoming UDP datagram pub fn read( &mut self, - remote: SocketAddr, now: Instant, + remote: SocketAddr, local_ip: Option, ecn: Option, data: BytesMut, diff --git a/dtls/src/flight/flight5.rs b/dtls/src/flight/flight5.rs index 3e7d0da..bc259f0 100644 --- a/dtls/src/flight/flight5.rs +++ b/dtls/src/flight/flight5.rs @@ -712,7 +712,6 @@ fn initalize_cipher_suite( chains = match verify_server_cert( &state.peer_certificates, &cfg.server_cert_verifier, - &cfg.roots_cas, &cfg.server_name, ) { Ok(chains) => chains, diff --git a/dtls/src/fragment_buffer/mod.rs b/dtls/src/fragment_buffer/mod.rs index fdfb924..b59169d 100644 --- a/dtls/src/fragment_buffer/mod.rs +++ b/dtls/src/fragment_buffer/mod.rs @@ -60,7 +60,7 @@ impl FragmentBuffer { self.cache .entry(handshake_header.message_sequence) - .or_insert_with(Vec::new); + .or_default(); // end index should be the length of handshake header but if the handshake // was fragmented, we should keep them all diff --git a/rtcp/src/payload_feedbacks/receiver_estimated_maximum_bitrate/mod.rs b/rtcp/src/payload_feedbacks/receiver_estimated_maximum_bitrate/mod.rs index ffb8df4..a6096b3 100644 --- a/rtcp/src/payload_feedbacks/receiver_estimated_maximum_bitrate/mod.rs +++ b/rtcp/src/payload_feedbacks/receiver_estimated_maximum_bitrate/mod.rs @@ -225,7 +225,7 @@ impl Unmarshal for ReceiverEstimatedMaximumBitrate { } // REMB rules all around me - let mut unique_identifier = vec![0; 4]; + let mut unique_identifier = [0; 4]; unique_identifier[0] = raw_packet.get_u8(); unique_identifier[1] = raw_packet.get_u8(); unique_identifier[2] = raw_packet.get_u8(); diff --git a/rtp/Cargo.toml b/rtp/Cargo.toml index e216054..1af8985 100644 --- a/rtp/Cargo.toml +++ b/rtp/Cargo.toml @@ -15,7 +15,7 @@ thiserror = "1.0" serde = { version = "1.0.102", features = ["derive"] } [dev-dependencies] -chrono = "0.4.23" +chrono = "0.4.31" criterion = "0.4.0" [[bench]] diff --git a/rtp/src/codecs/vp8/mod.rs b/rtp/src/codecs/vp8/mod.rs index 5468f99..229da84 100644 --- a/rtp/src/codecs/vp8/mod.rs +++ b/rtp/src/codecs/vp8/mod.rs @@ -67,7 +67,7 @@ impl Payloader for Vp8Payloader { let current_fragment_size = std::cmp::min(max_fragment_size, payload_data_remaining) as usize; let mut out = BytesMut::with_capacity(using_header_size + current_fragment_size); - let mut buf = vec![0u8; 4]; + let mut buf = [0u8; 4]; if first { buf[0] = 0x10; first = false; diff --git a/rtp/src/codecs/vp9/mod.rs b/rtp/src/codecs/vp9/mod.rs index b6dd6da..deec629 100644 --- a/rtp/src/codecs/vp9/mod.rs +++ b/rtp/src/codecs/vp9/mod.rs @@ -105,7 +105,7 @@ impl Payloader for Vp9Payloader { let current_fragment_size = std::cmp::min(max_fragment_size as usize, payload_data_remaining); let mut out = BytesMut::with_capacity(VP9HEADER_SIZE + current_fragment_size); - let mut buf = vec![0u8; VP9HEADER_SIZE]; + let mut buf = [0u8; VP9HEADER_SIZE]; buf[0] = 0x90; // F=1 I=1 if payload_data_index == 0 { buf[0] |= 0x08; // B=1 diff --git a/rtp/src/extension/abs_send_time_extension/abs_send_time_extension_test.rs b/rtp/src/extension/abs_send_time_extension/abs_send_time_extension_test.rs index af660a1..23c8911 100644 --- a/rtp/src/extension/abs_send_time_extension/abs_send_time_extension_test.rs +++ b/rtp/src/extension/abs_send_time_extension/abs_send_time_extension_test.rs @@ -35,7 +35,7 @@ fn test_ntp_conversion() -> Result<()> { for (t, n) in &tests { let st = UNIX_EPOCH - .checked_add(Duration::from_nanos(t.timestamp_nanos() as u64)) + .checked_add(Duration::from_nanos(t.timestamp_nanos_opt().unwrap() as u64)) .unwrap_or(UNIX_EPOCH); let ntp = unix2ntp(st); @@ -54,7 +54,7 @@ fn test_ntp_conversion() -> Result<()> { for (t, n) in &tests { let output = ntp2unix(*n); let input = UNIX_EPOCH - .checked_add(Duration::from_nanos(t.timestamp_nanos() as u64)) + .checked_add(Duration::from_nanos(t.timestamp_nanos_opt().unwrap() as u64)) .unwrap_or(UNIX_EPOCH); let diff = input.duration_since(output).unwrap().as_nanos() as i128; if !(-ABS_SEND_TIME_RESOLUTION..=ABS_SEND_TIME_RESOLUTION).contains(&diff) { diff --git a/rtp/src/packetizer/packetizer_test.rs b/rtp/src/packetizer/packetizer_test.rs index 0ccd6b9..ed29a78 100644 --- a/rtp/src/packetizer/packetizer_test.rs +++ b/rtp/src/packetizer/packetizer_test.rs @@ -40,7 +40,7 @@ fn test_packetizer_abs_send_time() -> Result<()> { let loc = FixedOffset::west_opt(5 * 60 * 60).unwrap(); // UTC-5 let t = loc.with_ymd_and_hms(1985, 6, 23, 4, 0, 0).unwrap(); UNIX_EPOCH - .checked_add(Duration::from_nanos(t.timestamp_nanos() as u64)) + .checked_add(Duration::from_nanos(t.timestamp_nanos_opt().unwrap() as u64)) .unwrap_or(UNIX_EPOCH) })); diff --git a/sctp/Cargo.toml b/sctp/Cargo.toml index 2806ddb..6babc66 100644 --- a/sctp/Cargo.toml +++ b/sctp/Cargo.toml @@ -9,6 +9,7 @@ license = "MIT/Apache-2.0" [dependencies] shared = { path = "../shared", package = "shared", default-features = false, features = [] } +retty = "0.24.0" bytes = "1.4.0" fxhash = "0.2.1" rand = "0.8.5" diff --git a/sctp/src/endpoint/endpoint_test.rs b/sctp/src/endpoint/endpoint_test.rs index be16328..2612121 100644 --- a/sctp/src/endpoint/endpoint_test.rs +++ b/sctp/src/endpoint/endpoint_test.rs @@ -23,6 +23,7 @@ use crate::param::param_reconfig_response::ParamReconfigResponse; use assert_matches::assert_matches; use lazy_static::lazy_static; use log::{info, trace}; +use std::collections::VecDeque; use std::net::Ipv6Addr; use std::ops::RangeFrom; use std::str::FromStr; @@ -136,10 +137,6 @@ impl TestEndpoint { } } - while let Some(x) = self.poll_transmit() { - self.outbound.extend(split_transmit(x)); - } - let mut endpoint_events: Vec<(AssociationHandle, EndpointEvent)> = vec![]; for (ch, conn) in self.associations.iter_mut() { if self.timeout.map_or(false, |x| x <= now) { @@ -164,11 +161,7 @@ impl TestEndpoint { } for (ch, event) in endpoint_events { - if let Some(event) = self.handle_event(ch, event) { - if let Some(conn) = self.associations.get_mut(&ch) { - conn.handle_event(event); - } - } + self.handle_event(ch, event); } } diff --git a/sctp/src/endpoint/mod.rs b/sctp/src/endpoint/mod.rs index a8df4fd..0677fe8 100644 --- a/sctp/src/endpoint/mod.rs +++ b/sctp/src/endpoint/mod.rs @@ -2,7 +2,7 @@ mod endpoint_test; use std::{ - collections::{HashMap, VecDeque}, + collections::HashMap, fmt, iter, net::{IpAddr, SocketAddr}, ops::{Index, IndexMut}, @@ -34,7 +34,6 @@ use thiserror::Error; /// `handle_event`. pub struct Endpoint { rng: StdRng, - transmits: VecDeque, /// Identifies associations based on the INIT Dst AID the peer utilized /// /// Uses a standard `HashMap` to protect against hash collision attacks. @@ -58,7 +57,6 @@ impl fmt::Debug for Endpoint { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("Endpoint") .field("rng", &self.rng) - .field("transmits", &self.transmits) .field("association_ids_initial", &self.association_ids_init) .field("association_ids", &self.association_ids) .field("associations", &self.associations) @@ -76,7 +74,6 @@ impl Endpoint { pub fn new(config: Arc, server_config: Option>) -> Self { Self { rng: StdRng::from_entropy(), - transmits: VecDeque::new(), association_ids_init: HashMap::default(), association_ids: FxHashMap::default(), associations: Slab::new(), @@ -87,25 +84,13 @@ impl Endpoint { } } - /// Get the next packet to transmit - #[must_use] - pub fn poll_transmit(&mut self) -> Option { - self.transmits.pop_front() - } - /// Replace the server configuration, affecting new incoming associations only pub fn set_server_config(&mut self, server_config: Option>) { self.server_config = server_config; } /// Process `EndpointEvent`s emitted from related `Association`s - /// - /// In turn, processing this event may return a `AssociationEvent` for the same `Association`. - pub fn handle_event( - &mut self, - ch: AssociationHandle, - event: EndpointEvent, - ) -> Option { + pub fn handle_event(&mut self, ch: AssociationHandle, event: EndpointEvent) { match event.0 { EndpointEventInner::Drained => { let conn = self.associations.remove(ch.0); @@ -115,7 +100,6 @@ impl Endpoint { } } } - None } /// Process an incoming UDP datagram diff --git a/sctp/src/lib.rs b/sctp/src/lib.rs index d333f66..1e9c338 100644 --- a/sctp/src/lib.rs +++ b/sctp/src/lib.rs @@ -17,6 +17,7 @@ #![allow(clippy::bool_to_int_with_if)] use bytes::Bytes; +use retty::transport::EcnCodepoint; use std::time::Instant; use std::{ fmt, @@ -46,7 +47,7 @@ pub use crate::endpoint::{AssociationHandle, ConnectError, DatagramEvent, Endpoi mod packet; mod shared; -pub use crate::shared::{AssociationEvent, AssociationId, EcnCodepoint, EndpointEvent}; +pub use crate::shared::{AssociationEvent, AssociationId, EndpointEvent}; pub(crate) mod param; diff --git a/sctp/src/shared.rs b/sctp/src/shared.rs index c378502..8f79bfd 100644 --- a/sctp/src/shared.rs +++ b/sctp/src/shared.rs @@ -49,33 +49,6 @@ pub(crate) enum EndpointEventInner { /// Mainly useful for identifying this Association's packets on the wire with tools like Wireshark. pub type AssociationId = u32; -/// Explicit congestion notification codepoint -#[repr(u8)] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum EcnCodepoint { - #[doc(hidden)] - Ect0 = 0b10, - #[doc(hidden)] - Ect1 = 0b01, - #[doc(hidden)] - Ce = 0b11, -} - -impl EcnCodepoint { - /// Create new object from the given bits - pub fn from_bits(x: u8) -> Option { - use self::EcnCodepoint::*; - Some(match x & 0b11 { - 0b10 => Ect0, - 0b01 => Ect1, - 0b11 => Ce, - _ => { - return None; - } - }) - } -} - #[derive(Debug, Copy, Clone)] pub struct IssuedAid { pub sequence: u64, diff --git a/shared/Cargo.toml b/shared/Cargo.toml index bfb8c5b..0fc953d 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -15,9 +15,9 @@ replay = [] [dependencies] thiserror = "~1.0.2" bytes = "1.4.0" -aes-gcm = { version = "0.10.1", features = ["std"] } +aes-gcm = { version = "0.10", features = ["std"] } url = "2.2" -rcgen = "0.10.0" -sec1 = { version = "0.3.0", features = [ "std" ] } -p256 = { version = "0.11.1", features = ["default", "ecdh", "ecdsa"] } -block-modes = "0.7.0" \ No newline at end of file +rcgen = "0.11" +sec1 = { version = "0.7", features = [ "std" ] } +p256 = { version = "0.13", features = ["default", "ecdh", "ecdsa"] } +aes = "0.8" \ No newline at end of file diff --git a/shared/src/error.rs b/shared/src/error.rs index 26c2d57..6fc828d 100644 --- a/shared/src/error.rs +++ b/shared/src/error.rs @@ -909,6 +909,8 @@ pub enum Error { ErrStreamClosed, #[error("Stream not existed")] ErrStreamNotExisted, + #[error("Association not existed")] + ErrAssociationNotExisted, #[error("Io EOF")] ErrEof, #[error("Invalid SystemTime")] @@ -938,6 +940,9 @@ pub enum Error { #[error("{0}")] Std(#[source] StdError), + #[error("{0}")] + Aes(#[from] aes::cipher::InvalidLength), + #[error("{0}")] Other(String), } @@ -1022,17 +1027,6 @@ impl From for Error { } } -impl From for Error { - fn from(e: block_modes::InvalidKeyIvLength) -> Self { - Error::Other(e.to_string()) - } -} -impl From for Error { - fn from(e: block_modes::BlockModeError) -> Self { - Error::Other(e.to_string()) - } -} - impl From for Error { fn from(e: SystemTimeError) -> Self { Error::Other(e.to_string()) diff --git a/stun/src/attributes.rs b/stun/src/attributes.rs index c688f8a..906b0c1 100644 --- a/stun/src/attributes.rs +++ b/stun/src/attributes.rs @@ -110,6 +110,7 @@ pub const ATTR_PRIORITY: AttrType = AttrType(0x0024); // PRIORITY pub const ATTR_USE_CANDIDATE: AttrType = AttrType(0x0025); // USE-CANDIDATE pub const ATTR_ICE_CONTROLLED: AttrType = AttrType(0x8029); // ICE-CONTROLLED pub const ATTR_ICE_CONTROLLING: AttrType = AttrType(0x802A); // ICE-CONTROLLING +pub const ATTR_NETWORK_COST: AttrType = AttrType(0xC057); // NETWORK-COST /// Attributes from RFC 5766 TURN. pub const ATTR_CHANNEL_NUMBER: AttrType = AttrType(0x000C); // CHANNEL-NUMBER diff --git a/stun/src/integrity.rs b/stun/src/integrity.rs index 77510f8..e56e5ae 100644 --- a/stun/src/integrity.rs +++ b/stun/src/integrity.rs @@ -67,7 +67,7 @@ impl MessageIntegrity { // new_long_term_integrity returns new MessageIntegrity with key for long-term // credentials. Password, username, and realm must be SASL-prepared. pub fn new_long_term_integrity(username: String, realm: String, password: String) -> Self { - let s = vec![username, realm, password].join(CREDENTIALS_SEP); + let s = [username, realm, password].join(CREDENTIALS_SEP); let mut h = Md5::new(); h.update(s.as_bytes()); diff --git a/stun/src/xoraddr.rs b/stun/src/xoraddr.rs index 5711897..98f07b3 100644 --- a/stun/src/xoraddr.rs +++ b/stun/src/xoraddr.rs @@ -120,7 +120,7 @@ impl XorMappedAddress { IpAddr::V6(ipv6) => (FAMILY_IPV6, IPV6LEN, ipv6.octets().to_vec()), }; - let mut value = vec![0; 32 + 128]; + let mut value = [0; 32 + 128]; //value[0] = 0 // first 8 bits are zeroes let mut xor_value = vec![0; IPV6LEN]; xor_value[4..].copy_from_slice(&m.transaction_id.0);