diff --git a/sm2/src/pke.rs b/sm2/src/pke.rs index b8ee5e3b..f0148e60 100644 --- a/sm2/src/pke.rs +++ b/sm2/src/pke.rs @@ -1,23 +1,20 @@ use core::cmp::min; use crate::AffinePoint; -use crate::U32; + #[cfg(feature = "alloc")] use alloc::vec; -use alloc::vec::Vec; -use elliptic_curve::bigint::Integer; -use elliptic_curve::bigint::Uint; -use elliptic_curve::bigint::U256; + use elliptic_curve::pkcs8::der::asn1::UintRef; use elliptic_curve::pkcs8::der::Decode; +use elliptic_curve::pkcs8::der::DecodeValue; use elliptic_curve::pkcs8::der::Encode; use elliptic_curve::pkcs8::der::Length; use elliptic_curve::pkcs8::der::Reader; +use elliptic_curve::pkcs8::der::Sequence; use elliptic_curve::pkcs8::der::Writer; -use elliptic_curve::pkcs8::Version; -use elliptic_curve::FieldBytesEncoding; + use elliptic_curve::{ - array::Array, pkcs8::der::{asn1::OctetStringRef, EncodeValue}, sec1::ToEncodedPoint, Result, @@ -36,31 +33,49 @@ pub use self::{decrypting::DecryptingKey, encrypting::EncryptingKey}; pub struct Cipher<'a> { x: &'a [u8], y: &'a [u8], - sm3: &'a [u8], - secret: &'a [u8], + digest: &'a [u8], + cipher: &'a [u8], } -impl<'a> Encode for Cipher<'a> { - fn encoded_len(&self) -> elliptic_curve::pkcs8::der::Result { - Length::new((self.x.len() + self.y.len() + self.sm3.len() + self.secret.len()) as u16) - .encoded_len() - } +impl<'a> Sequence<'a> for Cipher<'a> {} - fn encode(&self, writer: &mut impl Writer) -> elliptic_curve::pkcs8::der::Result<()> { - UintRef::new(self.x)?.encode(writer)?; - UintRef::new(self.y)?.encode(writer)?; - OctetStringRef::new(self.sm3)?.encode(writer)?; - OctetStringRef::new(self.secret)?.encode(writer)?; +impl<'a> EncodeValue for Cipher<'a> { + fn value_len(&self) -> elliptic_curve::pkcs8::der::Result { + UintRef::new(&self.x)?.encoded_len()? + + UintRef::new(&self.y)?.encoded_len()? + + OctetStringRef::new(&self.digest)?.encoded_len()? + + OctetStringRef::new(&self.cipher)?.encoded_len()? + } + fn encode_value(&self, writer: &mut impl Writer) -> elliptic_curve::pkcs8::der::Result<()> { + UintRef::new(&self.x)?.encode(writer)?; + UintRef::new(&self.y)?.encode(writer)?; + OctetStringRef::new(&self.digest)?.encode(writer)?; + OctetStringRef::new(&self.cipher)?.encode(writer)?; Ok(()) } } -impl<'a> Decode<'a> for Cipher<'a> { +impl<'a> DecodeValue<'a> for Cipher<'a> { type Error = elliptic_curve::pkcs8::der::Error; - fn decode>(decoder: &mut R) -> core::result::Result {} + fn decode_value>( + decoder: &mut R, + header: elliptic_curve::pkcs8::der::Header, + ) -> core::result::Result { + decoder.read_nested(header.length, |nr| { + let x = UintRef::decode(nr)?.as_bytes(); + let y = UintRef::decode(nr)?.as_bytes(); + let digest = OctetStringRef::decode(nr)?.into(); + let cipher = OctetStringRef::decode(nr)?.into(); + Ok(Cipher { + x, + y, + digest, + cipher, + }) + }) + } } - #[derive(Clone, Copy, Debug)] pub enum Mode { C1C2C3, @@ -72,13 +87,14 @@ fn kdf(hasher: &mut dyn DynDigest, kpb: AffinePoint, c2: &mut [u8]) -> Result<() let mut ct: i32 = 0x00000001; let mut offset = 0; let digest_size = hasher.output_size(); - let mut ha = vec![0u8; hasher.output_size()]; + let mut ha = vec![0u8; digest_size]; let encode_point = kpb.to_encoded_point(false); while offset < klen { - hasher.update(&encode_point.x().unwrap()); - hasher.update(&encode_point.y().unwrap()); + hasher.update(encode_point.x().unwrap()); + hasher.update(encode_point.y().unwrap()); hasher.update(&ct.to_be_bytes()); + hasher .finalize_into_reset(&mut ha) .map_err(|_e| elliptic_curve::Error)?; diff --git a/sm2/src/pke/decrypting.rs b/sm2/src/pke/decrypting.rs index fe9cda64..fb5886ef 100644 --- a/sm2/src/pke/decrypting.rs +++ b/sm2/src/pke/decrypting.rs @@ -1,50 +1,43 @@ +use core::fmt::{self, Debug}; + use crate::arithmetic::field::FieldElement; use crate::{AffinePoint, EncodedPoint, FieldBytes, NonZeroScalar, PublicKey, Scalar, SecretKey}; -use alloc::boxed::Box; + use alloc::vec::Vec; use elliptic_curve::ops::Reduce; +use elliptic_curve::pkcs8::der::Decode; use elliptic_curve::sec1::FromEncodedPoint; +use elliptic_curve::subtle::{Choice, ConstantTimeEq}; use elliptic_curve::Error; -use elliptic_curve::{ - bigint::U256, - sec1::{ToEncodedPoint, UncompressedPoint}, - Group, Result, -}; +use elliptic_curve::{bigint::U256, sec1::ToEncodedPoint, Group, Result}; use primeorder::PrimeField; + use sm3::digest::DynDigest; use sm3::{Digest, Sm3}; use super::encrypting::EncryptingKey; use super::{kdf, vec, Cipher, Mode}; +#[derive(Clone)] pub struct DecryptingKey { secret_scalar: NonZeroScalar, - encrytingKey: EncryptingKey, + encryting_key: EncryptingKey, mode: Mode, - digest: Box, } impl DecryptingKey { pub fn new(secret_key: SecretKey) -> Self { - Self::new_with_mode(secret_key.to_nonzero_scalar(), Mode::C1C2C3) + Self::new_with_mode(secret_key.to_nonzero_scalar(), Mode::C1C3C2) } pub fn new_with_mode(secret_scalar: NonZeroScalar, mode: Mode) -> Self { - Self::new_with_mode_and_hash::(secret_scalar, mode) - } - - pub fn new_with_mode_and_hash( - secret_scalar: NonZeroScalar, - mode: Mode, - ) -> Self { Self { secret_scalar, - encrytingKey: EncryptingKey::new_with_mode_and_hash::( + encryting_key: EncryptingKey::new_with_mode( PublicKey::from_secret_scalar(&secret_scalar), mode, ), mode, - digest: Box::new(D::new()), } } @@ -62,7 +55,7 @@ impl DecryptingKey { /// Create a signing key from a non-zero scalar. pub fn from_nonzero_scalar(secret_scalar: NonZeroScalar) -> Result { - Ok(Self::new_with_mode(secret_scalar, Mode::C1C2C3)) + Ok(Self::new_with_mode(secret_scalar, Mode::C1C3C2)) } /// Serialize as bytes. @@ -81,47 +74,76 @@ impl DecryptingKey { &self.secret_scalar } - /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`]. + /// Get the [`EncryptingKey`] which corresponds to this [`DecryptingKey`]. pub fn encrypting_key(&self) -> &EncryptingKey { - &self.encrytingKey + &self.encryting_key + } + + /// Decrypt inplace + pub fn decrypt(&self, ciphertext: &mut [u8]) -> Result> { + self.decrypt_digest::(ciphertext) + } + /// Decrypt inplace + pub fn decrypt_digest(&self, ciphertext: &mut [u8]) -> Result> + where + D: 'static + Digest + DynDigest + Send + Sync, + { + let mut digest = D::new(); + decrypt(&self.secret_scalar, self.mode, &mut digest, ciphertext) + } + + pub fn decrypt_asna1(&self, ciphertext: &[u8]) -> Result> { + self.decrypt_asna1_digest::(ciphertext) } /// Decrypt inplace - pub fn decrypt(&mut self, ciphertext: &mut [u8]) -> Result> { - decrypt( - &self.secret_scalar, - self.mode, - &mut *self.digest, - ciphertext, - ) + pub fn decrypt_asna1_digest(&self, ciphertext: &[u8]) -> Result> + where + D: 'static + Digest + DynDigest + Send + Sync, + { + let cipher = + Cipher::from_der(&ciphertext).map_err(|e| elliptic_curve::pkcs8::Error::from(e))?; + + let mut cipher = match self.mode { + Mode::C1C2C3 => [&[0x04], cipher.x, cipher.y, cipher.cipher, cipher.digest].concat(), + Mode::C1C3C2 => [&[0x04], cipher.x, cipher.y, cipher.digest, cipher.cipher].concat(), + }; + + Ok(self.decrypt_digest::(&mut cipher)?) } } -fn decrypt_asna1( - secret_scalar: &Scalar, - mode: Mode, - hasher: &mut dyn DynDigest, - cipher: &mut [u8], -) -> Result> { - - let digest_size = hasher.output_size(); - let (x, cipher) = cipher.split_at(32); - let (y, cipher) = cipher.split_at(32); - let (sm3, cipher) = match mode { - Mode::C1C2C3 => cipher.split_at(digest_size), - Mode::C1C3C2 => cipher.split_at(cipher.len() - digest_size), - }; - Cipher { - x: x.into(), - y, - sm3, - secret: cipher, +// +// Other trait impls +// + +impl AsRef for DecryptingKey { + fn as_ref(&self) -> &EncryptingKey { + &self.encryting_key } - .to_der() - .map_err(|e| Error) +} +impl ConstantTimeEq for DecryptingKey { + fn ct_eq(&self, other: &Self) -> Choice { + self.secret_scalar.ct_eq(&other.secret_scalar) + } +} + +impl Debug for DecryptingKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DecryptingKey") + .field("private_key", &self.secret_scalar.as_ref()) + .field("encrypting_key", &self.encrypting_key()) + .finish_non_exhaustive() + } +} - let plain = decrypt(secret_scalar, mode, hasher, cipher)?; +/// Constant-time comparison +impl Eq for DecryptingKey {} +impl PartialEq for DecryptingKey { + fn eq(&self, other: &DecryptingKey) -> bool { + self.ct_eq(other).into() + } } fn decrypt( diff --git a/sm2/src/pke/encrypting.rs b/sm2/src/pke/encrypting.rs index 33b92ef2..f3fb07ba 100644 --- a/sm2/src/pke/encrypting.rs +++ b/sm2/src/pke/encrypting.rs @@ -1,3 +1,5 @@ +use core::fmt::Debug; + use crate::pke::{kdf, vec}; use crate::{AffinePoint, Scalar}; use crate::{ProjectivePoint, PublicKey, Sm2}; @@ -5,8 +7,7 @@ use crate::{ProjectivePoint, PublicKey, Sm2}; #[cfg(feature = "alloc")] use alloc::{borrow::ToOwned, boxed::Box, vec::Vec}; -use elliptic_curve::group::GroupEncoding; -use elliptic_curve::pkcs8::der::{Encode, EncodeValue}; +use elliptic_curve::pkcs8::der::Encode; use elliptic_curve::sec1::ToEncodedPoint; use elliptic_curve::{ bigint::{RandomBits, Uint, Zero, U256}, @@ -20,10 +21,10 @@ use sm3::Sm3; use super::{Cipher, Mode}; +#[derive(Clone, Debug)] pub struct EncryptingKey { public_key: PublicKey, mode: Mode, - digest: Box, } impl EncryptingKey { @@ -34,19 +35,7 @@ impl EncryptingKey { /// Initialize [`EncryptingKey`] from PublicKey and set Encryption mode pub fn new_with_mode(public_key: PublicKey, mode: Mode) -> Self { - Self::new_with_mode_and_hash::(public_key, mode) - } - - /// Initialize [`EncryptingKey`] from PublicKey and set Encryption mode then set hasher - pub fn new_with_mode_and_hash( - public_key: PublicKey, - mode: Mode, - ) -> Self { - Self { - public_key, - mode, - digest: Box::new(D::new()), - } + Self { public_key, mode } } /// Initialize [`VerifyingKey`] from a SEC1-encoded public key. @@ -80,8 +69,47 @@ impl EncryptingKey { self.public_key.to_sec1_bytes() } - pub fn encrypt(&mut self, msg: &[u8]) -> Result> { - encrypt(self.public_key, self.mode, &mut *self.digest, msg) + pub fn encrypt(&self, msg: &[u8]) -> Result> { + self.encrypt_digest::(msg) + } + + pub fn encrypt_asna1(&self, msg: &[u8]) -> Result> { + self.encrypt_asna1_digest::(msg) + } + + pub fn encrypt_digest(&self, msg: &[u8]) -> Result> + where + D: 'static + Digest + DynDigest + Send + Sync, + { + let mut digest = D::new(); + encrypt(&self.public_key, self.mode, &mut digest, msg) + } + + pub fn encrypt_asna1_digest(&self, msg: &[u8]) -> Result> + where + D: 'static + Digest + DynDigest + Send + Sync, + { + let mut digest = D::new(); + let cipher = encrypt(&self.public_key, self.mode, &mut digest, msg)?; + let digest_size = digest.output_size(); + let (_, cipher) = cipher.split_at(1); + let (x, cipher) = cipher.split_at(32); + let (y, cipher) = cipher.split_at(32); + let (digest, cipher) = match self.mode { + Mode::C1C2C3 => { + let (cipher, digest) = cipher.split_at(cipher.len() - digest_size); + (digest, cipher) + } + Mode::C1C3C2 => cipher.split_at(digest_size), + }; + Ok(Cipher { + x, + y, + digest, + cipher, + } + .to_der() + .map_err(|e| elliptic_curve::pkcs8::Error::from(e))?) } } @@ -91,35 +119,10 @@ impl From for EncryptingKey { } } -fn encrypt_asna1( - public_key: PublicKey, - mode: Mode, - hasher: &mut dyn DynDigest, - msg: &[u8], -) -> Result> { - let digest_size = hasher.output_size(); - - let mut cipher = encrypt(public_key, mode, hasher, msg)?; - let (x, cipher) = cipher.split_at(32); - let (y, cipher) = cipher.split_at(32); - let (sm3, cipher) = match mode { - Mode::C1C2C3 => cipher.split_at(digest_size), - Mode::C1C3C2 => cipher.split_at(cipher.len() - digest_size), - }; - Cipher { - x: x.into(), - y, - sm3, - secret: cipher, - } - .to_der() - .map_err(|e| Error) -} - fn encrypt( - public_key: PublicKey, + public_key: &PublicKey, mode: Mode, - hasher: &mut dyn DynDigest, + digest: &mut dyn DynDigest, msg: &[u8], ) -> Result> { const N_BYTES: u32 = (Sm2::ORDER.bits() + 7) / 8; @@ -133,7 +136,8 @@ fn encrypt( c1.copy_from_slice(uncompress_kg.as_bytes()); let public_pp = public_key.to_projective(); kpb = (public_pp * &k).to_affine(); - kdf(hasher, kpb, &mut c2)?; + + kdf(digest, kpb, &mut c2)?; // if all of t are 0, xor(c2) == c2 @@ -143,11 +147,11 @@ fn encrypt( } let encode_point = kpb.to_encoded_point(false); - let mut c3 = vec![0; hasher.output_size()]; - hasher.update(encode_point.x().unwrap()); - hasher.update(msg); - hasher.update(encode_point.y().unwrap()); - hasher.finalize_into_reset(&mut c3).map_err(|_e| Error)?; + let mut c3 = vec![0; digest.output_size()]; + digest.update(encode_point.x().unwrap()); + digest.update(msg); + digest.update(encode_point.y().unwrap()); + digest.finalize_into_reset(&mut c3).map_err(|_e| Error)?; Ok(match mode { Mode::C1C2C3 => [c1.as_slice(), &c2, &c3].concat().to_vec(), @@ -156,10 +160,9 @@ fn encrypt( } fn next_k(bit_length: u32) -> Uint<4> { - let mut k: Uint<4>; loop { - k = U256::random_bits(&mut rand_core::OsRng, bit_length); - if !(k == U256::zero() || k > Sm2::ORDER) { + let k = U256::random_bits(&mut rand_core::OsRng, bit_length); + if k.is_zero().unwrap_u8() == 0 && k <= Sm2::ORDER { return k; } } diff --git a/sm2/tests/sm2pke.rs b/sm2/tests/sm2pke.rs index 4ef1468a..ef2bfee1 100644 --- a/sm2/tests/sm2pke.rs +++ b/sm2/tests/sm2pke.rs @@ -1,24 +1,28 @@ #![cfg(feature = "pke")] +use elliptic_curve::ops::Reduce; use elliptic_curve::{CurveArithmetic, NonZeroScalar}; use hex_literal::hex; +use proptest::prelude::*; +use sm2::pke::Mode; use sm2::{ pke::{DecryptingKey, EncryptingKey}, PublicKey, Sm2, }; +use sm2::{AffinePoint, Scalar, SecretKey, U256}; // sec1 public key b const PUBLIC_KEY: [u8; 65] = hex!("044b0b5d9b5b13c62238c86ecb7161ce0831fb1a0f29441ddd212cecbad2eea0b99e792d0e2dcfc70555ac24b536d7476e99ba8562f75de5ce8f0595909138ec0c"); const PRIVATE_KEY: [u8; 32] = - hex!("F7526C518958878A11D2F933F85DECFB3C51D8D170192C2CD1A4A2B69A1C92E0"); + hex!("46AE010C87F8D73FE3124C52FF6695DD839D73748FD40D6B2A3D7CC3E0233F1E"); const MSG: &[u8] = b"plaintext"; // uncompress, starts with 04 const CIPHER: [u8; 106] = hex!("0437c84a1ee61a707cf5819b64a56f82186e69771775119c82139b6a56f5ff64fec5f3b1c5783648fc306be5a0a4e14c219bbecd62670bc8d8d0fe56d67e6baeff8ba118c7e79dc3abacbf1f4e13533081862158f720fd32705fc318f7f1f617a8734bbcc7569d0665e1"); -// compressed starts with 03 // openssl pkeyutl -encrypt -in - -inkey sm2.key -out cipher.txt -const ASN1_CIPHER: [u8; 116] = hex!("3072022012872d1645ee63d49d801867e3ff8a4812358d062fe680e2bcf9d15a72b9b7c90220057f515f4c399dfff8eb979964c07057f1c9ca516ee11df4576bc08da98877ec04208cbbb21e95ad104f6e8fc04514e4be536dc5be53c9bb7da9177fafca40642ab5040a69e62a53a2e930fda314"); - +const ASN1_CIPHER: [u8; 116] = hex!("3072022000d9df58aa6af5f94534bc500bf31233c8824379b2494edd2ff29526ce04d424022100b3049c48f5d69c65456382513e4eba827972ae86a38c7a886cf6ed50fb5fe06004206886509211229740694b3ac17bec1ea59ce42c21d1566c15fbd65c5e5d90f6390409a02ecfa10309326f43"); +// 3071022007868d682d1d613148a5b993959f9ba344fbb2ef4150238f4f0d453d1564f9d2022000cbbb8903f07f0104db3b619296247f2f43477649b9b363d73af3ddba7597ac042006af6a9af1eb46c8fceba124e8743985581d5e2b88682a9bc44c0ff6cb3981d2040947bc9c69914d3a2ec5 +// 3072022000d9df58aa6af5f94534bc500bf31233c8824379b2494edd2ff29526ce04d424022100b3049c48f5d69c65456382513e4eba827972ae86a38c7a886cf6ed50fb5fe06004206886509211229740694b3ac17bec1ea59ce42c21d1566c15fbd65c5e5d90f6390409a02ecfa10309326f43 #[test] fn decrypt_verify() { let scalar = NonZeroScalar::::try_from(PRIVATE_KEY.as_ref() as &[u8]).unwrap(); @@ -34,27 +38,87 @@ fn decrypt_verify() { } #[test] -fn encrypt_and_decrpyt() { - let mut ek = EncryptingKey::from_sec1_bytes(&PUBLIC_KEY).unwrap(); - let mut dk = DecryptingKey::from_bytes(&PRIVATE_KEY.into()).unwrap(); - let mut cipher_bytes = ek.encrypt(MSG).unwrap(); - assert_eq!(dk.decrypt(&mut cipher_bytes).unwrap(), MSG); +fn decrypt_asna1_verify() { + let scalar = NonZeroScalar::::try_from(PRIVATE_KEY.as_ref() as &[u8]).unwrap(); + + let mut cipher = Vec::from(&ASN1_CIPHER); + let dk = DecryptingKey::new_with_mode(scalar, sm2::pke::Mode::C1C2C3); + println!("{:?}", dk); + assert_eq!(dk.decrypt_asna1(&mut cipher).unwrap(), MSG); } #[test] -fn encrypt_and_decrpyt_mode() { - let mut cipher_bytes = EncryptingKey::new_with_mode( - PublicKey::from_sec1_bytes(&PUBLIC_KEY).unwrap(), - sm2::pke::Mode::C1C3C2, - ) - .encrypt(MSG) - .unwrap(); - let scalar: NonZeroScalar = - NonZeroScalar::::try_from(PRIVATE_KEY.as_ref() as &[u8]).unwrap(); - assert_eq!( - DecryptingKey::new_with_mode(scalar, sm2::pke::Mode::C1C3C2) - .decrypt(&mut cipher_bytes) - .unwrap(), - MSG - ); +fn encrypt_and_decrpyt_asna1_test() { + let scalar = >::reduce_bytes(&PRIVATE_KEY.as_ref()); + let dk = if let Some(scalar) = Option::from(NonZeroScalar::new(scalar)) { + DecryptingKey::from_nonzero_scalar(scalar).unwrap() + } else { + panic!("...") + }; + let ek = dk.encrypting_key(); + println!("{:?}", dk); + let cipher_bytes = ek.encrypt_asna1(MSG).unwrap(); + let ciphertext: String = cipher_bytes.iter().map(|x| format!("{:02x}", x)).collect(); + println!("{}", ciphertext); + let dk_bytes = dk.as_nonzero_scalar().to_bytes(); + let sk = SecretKey::from_bytes(&dk_bytes).unwrap(); + let pem = sk.to_sec1_pem(sm2::pkcs8::LineEnding::LF).unwrap(); + println!("{}", (pem.as_ref() as &str)); + assert_eq!(dk.decrypt_asna1(&cipher_bytes).unwrap(), MSG); +} + +prop_compose! { + fn decrypting_key()(bytes in any::<[u8; 32]>()) -> DecryptingKey { + loop { + let scalar = >::reduce_bytes(&bytes.into()); + if let Some(scalar) = Option::from(NonZeroScalar::new(scalar)) { + return DecryptingKey::from_nonzero_scalar(scalar).unwrap(); + } + } + } +} + +prop_compose! { + fn decrypting_key_c1c2c3()(bytes in any::<[u8; 32]>()) -> DecryptingKey { + loop { + let scalar = >::reduce_bytes(&bytes.into()); + if let Some(scalar) = Option::from(NonZeroScalar::new(scalar)) { + return DecryptingKey::new_with_mode(scalar, sm2::pke::Mode::C1C2C3); + } + } + } +} + +proptest! { + #[test] + fn encrypt_and_decrpyt_asna1(dk in decrypting_key()) { + let ek = dk.encrypting_key(); + let cipher_bytes = ek.encrypt_asna1(MSG).unwrap(); + let ciphertext : String = cipher_bytes.iter().map(|x| format!("{:02x}", x)).collect(); + let dk_bytes = dk.as_nonzero_scalar().to_bytes(); + let sk = SecretKey::from_bytes(&dk_bytes).unwrap(); + let pem = sk.to_sec1_pem(sm2::pkcs8::LineEnding::LF).unwrap(); + println!("{}", (pem.as_ref() as &str)); + println!("{:?}", dk); + println!("{}", ciphertext); + prop_assert!(dk.decrypt_asna1(&cipher_bytes).is_ok()); + } + + #[test] + fn encrypt_and_decrpyt(dk in decrypting_key()) { + let ek = dk.encrypting_key(); + let mut cipher_bytes = ek.encrypt(MSG).unwrap(); + assert_eq!(dk.decrypt(&mut cipher_bytes).unwrap(), MSG); + } + + #[test] + fn encrypt_and_decrpyt_mode(dk in decrypting_key_c1c2c3()) { + let ek = dk.encrypting_key(); + let mut cipher_bytes = ek.encrypt(MSG).unwrap(); + assert_eq!( + dk.decrypt(&mut cipher_bytes) + .unwrap(), + MSG + ); + } }