Merge pull request from GHSA-wr2p-64gm-8x2c

This allowed users to create invalid Ed25519 Extended Secret Keys
with potentially cryptographically weak ECDSA Signatures.

However we still allow to have an _unsafe_ version to construct
a `SecretKeyExtended` from bytes without performing checks.
This is in order to allow a compatibility path without breaking
codes too much.

allow the direct conversion from XPrv from ed25519_bip32 crates to pallas-crypto's SecretKeyExtended without performing the bit tweaks check

While it is unsafe to call the SecretKeyExtended::from_bytes_unchecked
(unsafe in the cryptographic sense, not in the rust memory management
sense) we know this is going to be okay because the XPrv was already
safely created.

We previously removed the direct conversion of byte arrays into SecretKeyExtended

This has been replaced with a `TryFrom` and a `from_bytes() -> Result<Self>` function.
This allows us to perform the recovery of the wrapped private keys
without losing the security of performing the checks of the validity
of the Ed25519 Extended structure.

This should be safe to use and shouldn't make incompatibilities
because the Xprv was already checked for bit tweaks previously
in the flow.

add unsafe functions to leak the content of the SecretKey or SecretKeyExtended

Remove the From implementation to convert Secret Keys into Bytes

Instead prefer the explicit unsafe functions to leak the content of the keys

temporarily remove the public access of the as_bytes function

this is to prevent leaking the bytes of the private keys.
This commit is contained in:
Nicolas Di Prima 2024-06-01 11:32:32 +01:00 committed by GitHub
parent 6b3ac2f733
commit 46197734a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 161 additions and 22 deletions

View file

@ -3,7 +3,7 @@ use bip39::rand_core::{CryptoRng, RngCore};
use bip39::{Language, Mnemonic};
use cryptoxide::{hmac::Hmac, pbkdf2::pbkdf2, sha2::Sha512};
use ed25519_bip32::{self, XPrv, XPub, XPRV_SIZE};
use pallas_crypto::key::ed25519;
use pallas_crypto::key::ed25519::{self, SecretKeyExtended};
use crate::{Error, PrivateKey};
@ -72,7 +72,11 @@ impl Bip32PrivateKey {
}
pub fn to_ed25519_private_key(&self) -> PrivateKey {
PrivateKey::Extended(self.0.extended_secret_key().into())
PrivateKey::Extended(unsafe {
// The use of unsafe is allowed here. The key is an Extended Secret Key
// already because it passed through the ed25519_bip32 crates checks
SecretKeyExtended::from_bytes_unchecked(self.0.extended_secret_key())
})
}
pub fn to_public(&self) -> Bip32PublicKey {

View file

@ -1,4 +1,6 @@
use pallas_crypto::key::ed25519::{PublicKey, SecretKey, SecretKeyExtended, Signature};
use pallas_crypto::key::ed25519::{
PublicKey, SecretKey, SecretKeyExtended, Signature, TryFromSecretKeyExtendedError,
};
use thiserror::Error;
pub mod hd;
@ -30,6 +32,10 @@ pub enum Error {
/// Error when attempting to derive ed25519-bip32 key
#[error("Error when attempting to derive ed25519-bip32 key: {0}")]
DerivationError(ed25519_bip32::DerivationError),
/// Error that may occurs when trying to decrypt a private key
/// which is not valid.
#[error("Invalid Ed25519 Extended Secret Key: {0}")]
InvalidSecretKeyExtended(#[from] TryFromSecretKeyExtendedError),
}
/// A standard or extended Ed25519 secret key
@ -64,14 +70,15 @@ impl PrivateKey {
}
}
pub fn as_bytes(&self) -> Vec<u8> {
pub(crate) fn as_bytes(&self) -> Vec<u8> {
match self {
Self::Normal(x) => {
let bytes: [u8; SecretKey::SIZE] = x.clone().into();
let bytes: [u8; SecretKey::SIZE] = unsafe { SecretKey::leak_into_bytes(x.clone()) };
bytes.to_vec()
}
Self::Extended(x) => {
let bytes: [u8; SecretKeyExtended::SIZE] = x.clone().into();
let bytes: [u8; SecretKeyExtended::SIZE] =
unsafe { SecretKeyExtended::leak_into_bytes(x.clone()) };
bytes.to_vec()
}
}

View file

@ -113,7 +113,7 @@ pub fn decrypt_private_key(password: &String, data: Vec<u8>) -> Result<PrivateKe
let mut plaintext = [0u8; SecretKeyExtended::SIZE];
if chacha20.decrypt(ciphertext, &mut plaintext, tag) {
let secret_key: SecretKeyExtended = plaintext.into();
let secret_key = SecretKeyExtended::from_bytes(plaintext)?;
Ok(secret_key.into())
} else {