feat: Better error reporting when features are disabled (#2972)

In case support for e.g. RSA keys is disabled at compile-time, we will now print a better error message. For example:

> Failed to dial Some(PeerId("QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt")): Failed to negotiate transport protocol(s): [(/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt): : Handshake failed: Handshake failed: Invalid public key: Key decoding error: RSA keys are unsupported)]

Fixes #2971.
This commit is contained in:
Thomas Eizinger
2022-11-23 11:51:47 +11:00
committed by GitHub
parent 9b182778e1
commit 2c96d644f9
20 changed files with 245 additions and 247 deletions

View File

@ -155,23 +155,11 @@ impl Keypair {
data: data.encode().into(),
},
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
Self::Rsa(_) => {
return Err(DecodingError::new(
"Encoding RSA key into Protobuf is unsupported",
))
}
Self::Rsa(_) => return Err(DecodingError::encoding_unsupported("RSA")),
#[cfg(feature = "secp256k1")]
Self::Secp256k1(_) => {
return Err(DecodingError::new(
"Encoding Secp256k1 key into Protobuf is unsupported",
))
}
Self::Secp256k1(_) => return Err(DecodingError::encoding_unsupported("secp256k1")),
#[cfg(feature = "ecdsa")]
Self::Ecdsa(_) => {
return Err(DecodingError::new(
"Encoding ECDSA key into Protobuf is unsupported",
))
}
Self::Ecdsa(_) => return Err(DecodingError::encoding_unsupported("ECDSA")),
};
Ok(pk.encode_to_vec())
@ -182,26 +170,19 @@ impl Keypair {
use prost::Message;
let mut private_key = keys_proto::PrivateKey::decode(bytes)
.map_err(|e| DecodingError::new("Protobuf").source(e))
.map_err(|e| DecodingError::bad_protobuf("private key bytes", e))
.map(zeroize::Zeroizing::new)?;
let key_type = keys_proto::KeyType::from_i32(private_key.r#type).ok_or_else(|| {
DecodingError::new(format!("unknown key type: {}", private_key.r#type))
})?;
let key_type = keys_proto::KeyType::from_i32(private_key.r#type)
.ok_or_else(|| DecodingError::unknown_key_type(private_key.r#type))?;
match key_type {
keys_proto::KeyType::Ed25519 => {
ed25519::Keypair::decode(&mut private_key.data).map(Keypair::Ed25519)
}
keys_proto::KeyType::Rsa => Err(DecodingError::new(
"Decoding RSA key from Protobuf is unsupported.",
)),
keys_proto::KeyType::Secp256k1 => Err(DecodingError::new(
"Decoding Secp256k1 key from Protobuf is unsupported.",
)),
keys_proto::KeyType::Ecdsa => Err(DecodingError::new(
"Decoding ECDSA key from Protobuf is unsupported.",
)),
keys_proto::KeyType::Rsa => Err(DecodingError::decoding_unsupported("RSA")),
keys_proto::KeyType::Secp256k1 => Err(DecodingError::decoding_unsupported("secp256k1")),
keys_proto::KeyType::Ecdsa => Err(DecodingError::decoding_unsupported("ECDSA")),
}
}
}
@ -268,7 +249,7 @@ impl PublicKey {
use prost::Message;
let pubkey = keys_proto::PublicKey::decode(bytes)
.map_err(|e| DecodingError::new("Protobuf").source(e))?;
.map_err(|e| DecodingError::bad_protobuf("public key bytes", e))?;
pubkey.try_into()
}
@ -310,7 +291,7 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
fn try_from(pubkey: keys_proto::PublicKey) -> Result<Self, Self::Error> {
let key_type = keys_proto::KeyType::from_i32(pubkey.r#type)
.ok_or_else(|| DecodingError::new(format!("unknown key type: {}", pubkey.r#type)))?;
.ok_or_else(|| DecodingError::unknown_key_type(pubkey.r#type))?;
match key_type {
keys_proto::KeyType::Ed25519 => {
@ -323,7 +304,7 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
#[cfg(any(not(feature = "rsa"), target_arch = "wasm32"))]
keys_proto::KeyType::Rsa => {
log::debug!("support for RSA was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
Err(DecodingError::missing_feature("rsa"))
}
#[cfg(feature = "secp256k1")]
keys_proto::KeyType::Secp256k1 => {
@ -332,7 +313,7 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
#[cfg(not(feature = "secp256k1"))]
keys_proto::KeyType::Secp256k1 => {
log::debug!("support for secp256k1 was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
Err(DecodingError::missing_feature("secp256k1"))
}
#[cfg(feature = "ecdsa")]
keys_proto::KeyType::Ecdsa => {
@ -341,7 +322,7 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
#[cfg(not(feature = "ecdsa"))]
keys_proto::KeyType::Ecdsa => {
log::debug!("support for ECDSA was disabled at compile-time");
Err(DecodingError::new("Unsupported"))
Err(DecodingError::missing_feature("ecdsa"))
}
}
}

View File

@ -31,6 +31,7 @@ use p256::{
},
EncodedPoint,
};
use void::Void;
/// An ECDSA keypair.
#[derive(Clone)]
@ -107,7 +108,7 @@ impl SecretKey {
/// Decode a secret key from a byte buffer.
pub fn from_bytes(buf: &[u8]) -> Result<Self, DecodingError> {
SigningKey::from_bytes(buf)
.map_err(|err| DecodingError::new("failed to parse ecdsa p256 secret key").source(err))
.map_err(|err| DecodingError::failed_to_parse("ecdsa p256 secret key", err))
.map(SecretKey)
}
}
@ -134,12 +135,11 @@ impl PublicKey {
/// Decode a public key from a byte buffer without compression.
pub fn from_bytes(k: &[u8]) -> Result<PublicKey, DecodingError> {
let enc_pt = EncodedPoint::from_bytes(k).map_err(|_| {
DecodingError::new("failed to parse ecdsa p256 public key, bad point encoding")
})?;
let enc_pt = EncodedPoint::from_bytes(k)
.map_err(|e| DecodingError::failed_to_parse("ecdsa p256 encoded point", e))?;
VerifyingKey::from_encoded_point(&enc_pt)
.map_err(|err| DecodingError::new("failed to parse ecdsa p256 public key").source(err))
.map_err(|err| DecodingError::failed_to_parse("ecdsa p256 public key", err))
.map(PublicKey)
}
@ -157,7 +157,7 @@ impl PublicKey {
/// Decode a public key into a DER encoded byte buffer as defined by SEC1 standard.
pub fn decode_der(k: &[u8]) -> Result<PublicKey, DecodingError> {
let buf = Self::del_asn1_header(k).ok_or_else(|| {
DecodingError::new("failed to parse asn.1 encoded ecdsa p256 public key")
DecodingError::failed_to_parse::<Void, _>("ASN.1-encoded ecdsa p256 public key", None)
})?;
Self::from_bytes(buf)
}

View File

@ -55,7 +55,7 @@ impl Keypair {
kp.zeroize();
Keypair(k)
})
.map_err(|e| DecodingError::new("Ed25519 keypair").source(e))
.map_err(|e| DecodingError::failed_to_parse("Ed25519 keypair", e))
}
/// Sign a message using the private key of this keypair.
@ -169,7 +169,7 @@ impl PublicKey {
/// Decode a public key from a byte array as produced by `encode`.
pub fn decode(k: &[u8]) -> Result<PublicKey, DecodingError> {
ed25519::PublicKey::from_bytes(k)
.map_err(|e| DecodingError::new("Ed25519 public key").source(e))
.map_err(|e| DecodingError::failed_to_parse("Ed25519 public key", e))
.map(PublicKey)
}
}
@ -215,7 +215,7 @@ impl SecretKey {
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
let sk_bytes = sk_bytes.as_mut();
let secret = ed25519::SecretKey::from_bytes(&*sk_bytes)
.map_err(|e| DecodingError::new("Ed25519 secret key").source(e))?;
.map_err(|e| DecodingError::failed_to_parse("Ed25519 secret key", e))?;
sk_bytes.zeroize();
Ok(SecretKey(secret))
}

View File

@ -31,17 +31,61 @@ pub struct DecodingError {
}
impl DecodingError {
pub(crate) fn new<S: ToString>(msg: S) -> Self {
#[cfg(not(all(
feature = "ecdsa",
feature = "rsa",
feature = "secp256k1",
not(target_arch = "wasm32")
)))]
pub(crate) fn missing_feature(feature_name: &'static str) -> Self {
Self {
msg: msg.to_string(),
msg: format!("cargo feature `{feature_name}` is not enabled"),
source: None,
}
}
pub(crate) fn source(self, source: impl Error + Send + Sync + 'static) -> Self {
pub(crate) fn failed_to_parse<E, S>(what: &'static str, source: S) -> Self
where
E: Error + Send + Sync + 'static,
S: Into<Option<E>>,
{
Self {
msg: format!("failed to parse {what}"),
source: match source.into() {
None => None,
Some(e) => Some(Box::new(e)),
},
}
}
pub(crate) fn bad_protobuf(
what: &'static str,
source: impl Error + Send + Sync + 'static,
) -> Self {
Self {
msg: format!("failed to decode {what} from protobuf"),
source: Some(Box::new(source)),
..self
}
}
pub(crate) fn unknown_key_type(key_type: i32) -> Self {
Self {
msg: format!("unknown key-type {key_type}"),
source: None,
}
}
pub(crate) fn decoding_unsupported(key_type: &'static str) -> Self {
Self {
msg: format!("decoding {key_type} key from Protobuf is unsupported"),
source: None,
}
}
pub(crate) fn encoding_unsupported(key_type: &'static str) -> Self {
Self {
msg: format!("encoding {key_type} key to Protobuf is unsupported"),
source: None,
}
}
}

View File

@ -48,7 +48,7 @@ impl Keypair {
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
pub fn from_pkcs8(der: &mut [u8]) -> Result<Keypair, DecodingError> {
let kp = RsaKeyPair::from_pkcs8(der)
.map_err(|e| DecodingError::new("RSA PKCS#8 PrivateKeyInfo").source(e))?;
.map_err(|e| DecodingError::failed_to_parse("RSA PKCS#8 PrivateKeyInfo", e))?;
der.zeroize();
Ok(Keypair(Arc::new(kp)))
}
@ -111,7 +111,7 @@ impl PublicKey {
/// structure. See also `encode_x509`.
pub fn decode_x509(pk: &[u8]) -> Result<PublicKey, DecodingError> {
Asn1SubjectPublicKeyInfo::decode(pk)
.map_err(|e| DecodingError::new("RSA X.509").source(e))
.map_err(|e| DecodingError::failed_to_parse("RSA X.509", e))
.map(|spki| spki.subjectPublicKey.0)
}
}

View File

@ -100,7 +100,7 @@ impl SecretKey {
pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
let sk_bytes = sk.as_mut();
let secret = libsecp256k1::SecretKey::parse_slice(&*sk_bytes)
.map_err(|_| DecodingError::new("failed to parse secp256k1 secret key"))?;
.map_err(|e| DecodingError::failed_to_parse("parse secp256k1 secret key", e))?;
sk_bytes.zeroize();
Ok(SecretKey(secret))
}
@ -112,13 +112,12 @@ impl SecretKey {
pub fn from_der(mut der: impl AsMut<[u8]>) -> Result<SecretKey, DecodingError> {
// TODO: Stricter parsing.
let der_obj = der.as_mut();
let obj: Sequence = DerDecodable::decode(der_obj)
.map_err(|e| DecodingError::new("Secp256k1 DER ECPrivateKey").source(e))?;
let sk_obj = obj
.get(1)
.map_err(|e| DecodingError::new("Not enough elements in DER").source(e))?;
let mut sk_bytes: Vec<u8> =
asn1_der::typed::DerDecodable::load(sk_obj).map_err(DecodingError::new)?;
let mut sk_bytes = Sequence::decode(der_obj)
.and_then(|seq| seq.get(1))
.and_then(Vec::load)
.map_err(|e| DecodingError::failed_to_parse("secp256k1 SecretKey bytes", e))?;
let sk = SecretKey::from_bytes(&mut sk_bytes)?;
sk_bytes.zeroize();
der_obj.zeroize();
@ -217,7 +216,7 @@ impl PublicKey {
/// by `encode`.
pub fn decode(k: &[u8]) -> Result<PublicKey, DecodingError> {
libsecp256k1::PublicKey::parse_slice(k, Some(libsecp256k1::PublicKeyFormat::Compressed))
.map_err(|_| DecodingError::new("failed to parse secp256k1 public key"))
.map_err(|e| DecodingError::failed_to_parse("secp256k1 public key", e))
.map(PublicKey)
}
}

View File

@ -55,8 +55,8 @@ where
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UpgradeError::Select(e) => write!(f, "select error: {}", e),
UpgradeError::Apply(e) => write!(f, "upgrade apply error: {}", e),
UpgradeError::Select(_) => write!(f, "Multistream select failed"),
UpgradeError::Apply(_) => write!(f, "Handshake failed"),
}
}
}