Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/keys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ alloy-primitives.workspace = true
ripemd.workspace = true

# misc
anyhow.workspace = true
sha2.workspace = true
thiserror.workspace = true

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rand = { version = "0.8.5", features = ["std"] }
Expand Down
25 changes: 16 additions & 9 deletions crates/keys/src/algorithm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::bail;
use crate::{CryptoError, Result, errors::SignatureError};
use pkcs8::{AlgorithmIdentifierRef, ObjectIdentifier};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
Expand Down Expand Up @@ -33,16 +33,16 @@ impl CryptoAlgorithm {
}

impl std::str::FromStr for CryptoAlgorithm {
type Err = ();
type Err = CryptoError;

fn from_str(input: &str) -> Result<CryptoAlgorithm, Self::Err> {
fn from_str(input: &str) -> Result<CryptoAlgorithm> {
match input.to_lowercase().as_str() {
"ed25519" => Ok(CryptoAlgorithm::Ed25519),
"secp256k1" => Ok(CryptoAlgorithm::Secp256k1),
"secp256r1" => Ok(CryptoAlgorithm::Secp256r1),
"eip191" => Ok(CryptoAlgorithm::Eip191),
"cosmos_adr36" => Ok(CryptoAlgorithm::CosmosAdr36),
_ => Err(()),
_ => Err(SignatureError::AlgorithmError(input.to_string()).into()),
}
}
}
Expand All @@ -60,24 +60,31 @@ pub const SECP256K1_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.13
pub const SECP256R1_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");

impl<'a> TryFrom<AlgorithmIdentifierRef<'a>> for CryptoAlgorithm {
type Error = anyhow::Error;
type Error = CryptoError;

fn try_from(algorithm_identifier: AlgorithmIdentifierRef<'a>) -> Result<Self, Self::Error> {
fn try_from(algorithm_identifier: AlgorithmIdentifierRef<'a>) -> Result<Self> {
let oid = algorithm_identifier.oid;

if oid == ED25519_OID {
Ok(CryptoAlgorithm::Ed25519)
} else if oid == ELLIPTIC_CURVE_OID || oid == ECDSA_SHA256_OID {
let parameter_oid = algorithm_identifier.parameters_oid()?;
let parameter_oid = algorithm_identifier
.parameters_oid()
.map_err(|e| SignatureError::AlgorithmError(e.to_string()))?;
if parameter_oid == SECP256K1_OID {
Ok(CryptoAlgorithm::Secp256k1)
} else if parameter_oid == SECP256R1_OID {
Ok(CryptoAlgorithm::Secp256r1)
} else {
bail!("Unsupported elliptic curve OID")
return Err(SignatureError::AlgorithmError(
"Unsupported elliptic curve OID".to_string(),
)
.into());
}
} else {
bail!("Unsupported algorithm OID")
return Err(
SignatureError::AlgorithmError("Unsupported algorithm OID".to_string()).into(),
);
}
}
}
12 changes: 8 additions & 4 deletions crates/keys/src/cosmos.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::Result;
use crate::{Result, errors::SignatureError};
use k256::ecdsa::VerifyingKey as Secp256k1VerifyingKey;
use prism_serde::{bech32::ToBech32, raw_or_b64};
use ripemd::Ripemd160;
Expand Down Expand Up @@ -75,7 +75,8 @@ pub fn cosmos_adr36_hash_message(
const ADDRESS_PREFIX: &str = "cosmos";

let signer = signer_from_key(ADDRESS_PREFIX, verifying_key)?;
let serialized_sign_doc = create_serialized_adr36_sign_doc(message.as_ref().to_vec(), signer)?;
let serialized_sign_doc = create_serialized_adr36_sign_doc(message.as_ref().to_vec(), signer)
.map_err(|e| SignatureError::CosmosError(e.to_string()))?;
let hashed_sign_doc = Sha256::digest(&serialized_sign_doc).to_vec();
Ok(hashed_sign_doc)
}
Expand All @@ -95,7 +96,8 @@ pub fn cosmos_adr36_hash_message(
fn create_serialized_adr36_sign_doc(data: Vec<u8>, signer: String) -> Result<Vec<u8>> {
let adr36_sign_doc = CosmosSignDoc::new(signer, data);

let sign_doc_str = serde_json::to_string(&adr36_sign_doc)?
let sign_doc_str = serde_json::to_string(&adr36_sign_doc)
.map_err(|e| SignatureError::CosmosError(e.to_string()))?
.replace("<", "\\u003c")
.replace(">", "\\u003e")
.replace("&", "\\u0026");
Expand All @@ -121,6 +123,8 @@ fn signer_from_key(address_prefix: &str, verifying_key: &Secp256k1VerifyingKey)
let hashed_key_bytes = Sha256::digest(verifying_key_bytes);
let cosmos_address = Ripemd160::digest(hashed_key_bytes);

let signer = cosmos_address.to_bech32(address_prefix)?;
let signer = cosmos_address
.to_bech32(address_prefix)
.map_err(|e| SignatureError::CosmosError(e.to_string()))?;
Ok(signer)
}
61 changes: 61 additions & 0 deletions crates/keys/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use thiserror::Error;

pub type Result<G> = std::result::Result<G, CryptoError>;

#[derive(Error, Debug, Clone)]
pub enum CryptoError {
#[error("signature error: {0}")]
SignatureError(#[from] SignatureError),

#[error("keys error: {0}")]
KeysError(#[from] KeysError),

#[error("verification error: {0}")]
VerificationError(#[from] VerificationError),
}

#[derive(Error, Clone, Debug)]
pub enum SignatureError {
#[error("No {0} specific signatures implemented")]
UnsupportedFormatError(String),
#[error("Malformed signature")]
MalformedSignError,
#[error("Algorithm Error: {0}")]
AlgorithmError(String),
#[error("Something went wrong: {0}")]
CosmosError(String),
}

#[derive(Error, Clone, Debug)]
pub enum KeysError {
#[error("Creating PKCS8 DER failed")]
DerCreationError,
#[error("Creating PKCS8 PEM failed")]
PemCreationError,
#[error("Parsing key algorithm from PKCS#8 DER failed")]
ParseError,
#[error("Invalid PEM label")]
PemLabelError,
#[error("Invalid key bytes for algorithm: {0}")]
InvalidKeyBytes(String),
#[error("Signing operation failed: {0}")]
SigningError(String),
}

#[derive(Error, Clone, Debug)]
pub enum VerificationError {
#[error("Invalid signature type")]
InvalidSignError,
#[error("Failed to verify {0} signature: {1}")]
VerifyError(String, String),
#[error("Verifying key for {0} can only verify secp256k1 signatures")]
SignatureError(String),
#[error("Creating {0} failed")]
CreationError(String),
#[error("{0} vk from DER format failed: {1}")]
IntoRefError(String, String),
#[error("{0} vk {1} DER format is not implemented")]
NotImplementedError(String, String),
#[error("Something went wrong: {0}")]
GeneralError(String),
}
2 changes: 2 additions & 0 deletions crates/keys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mod algorithm;
mod cosmos;
mod der;
pub mod errors;
pub use errors::{CryptoError, KeysError, Result, SignatureError, VerificationError};
mod payload;
mod signatures;
mod signing_keys;
Expand Down
41 changes: 26 additions & 15 deletions crates/keys/src/signatures.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{Result, bail};
use crate::{CryptoError, Result, errors::SignatureError};
use ed25519_consensus::Signature as Ed25519Signature;
use k256::ecdsa::Signature as Secp256k1Signature;
use p256::ecdsa::Signature as Secp256r1Signature;
Expand Down Expand Up @@ -41,18 +41,20 @@ impl Signature {

pub fn from_algorithm_and_bytes(algorithm: CryptoAlgorithm, bytes: &[u8]) -> Result<Self> {
match algorithm {
CryptoAlgorithm::Ed25519 => {
Ed25519Signature::try_from(bytes).map(Signature::Ed25519).map_err(|e| e.into())
}
CryptoAlgorithm::Ed25519 => Ed25519Signature::try_from(bytes)
.map(Signature::Ed25519)
.map_err(|e| SignatureError::AlgorithmError(e.to_string()).into()),
CryptoAlgorithm::Secp256k1 => Secp256k1Signature::from_slice(bytes)
.map(Signature::Secp256k1)
.map_err(|e| e.into()),
.map_err(|e| SignatureError::AlgorithmError(e.to_string()).into()),
CryptoAlgorithm::Secp256r1 => Secp256r1Signature::from_slice(bytes)
.map(Signature::Secp256r1)
.map_err(|e| e.into()),
CryptoAlgorithm::Eip191 => bail!("No EIP-191 specific signatures implemented"),
.map_err(|e| SignatureError::AlgorithmError(e.to_string()).into()),
CryptoAlgorithm::Eip191 => {
Err(SignatureError::UnsupportedFormatError("EIP-191".to_string()).into())
}
CryptoAlgorithm::CosmosAdr36 => {
bail!("No cosmos ADR-36 specific signatures implemented")
Err(SignatureError::UnsupportedFormatError("ADR-36".to_string()).into())
}
}
}
Expand Down Expand Up @@ -87,22 +89,31 @@ impl Signature {
let mut der_bytes = Vec::with_capacity(2 + signature_bytes.len());

der_bytes.push(0x04); // octet stream
der_bytes.push(signature_bytes.len().try_into()?); // length of signature bytes
der_bytes.push(
signature_bytes
.len()
.try_into()
.map_err(|_| SignatureError::AlgorithmError("Map conversion failed".to_string()))?,
); // length of signature bytes
der_bytes.extend_from_slice(&signature_bytes);

let signature_info = SignatureInfoRef {
algorithm: self.algorithm_identifier(),
signature: OctetStringRef::new(&der_bytes)?,
signature: OctetStringRef::new(&der_bytes)
.map_err(|e| SignatureError::AlgorithmError(e.to_string()))?,
};

let doc = SecretDocument::encode_msg(&signature_info)?;
let doc = SecretDocument::encode_msg(&signature_info)
.map_err(|e| SignatureError::AlgorithmError(e.to_string()))?;
der_bytes.zeroize();
Ok(doc.as_bytes().to_vec())
}

pub fn from_prism_der(bytes: &[u8]) -> Result<Self> {
let signature_info = SignatureInfoRef::from_der(bytes)?;
let algorithm = CryptoAlgorithm::try_from(signature_info.algorithm)?;
let signature_info = SignatureInfoRef::from_der(bytes)
.map_err(|e| SignatureError::AlgorithmError(e.to_string()))?;
let algorithm = CryptoAlgorithm::try_from(signature_info.algorithm)
.map_err(|e| SignatureError::AlgorithmError(e.to_string()))?;

// Signature byte representation:
// 1st byte: 0x04 (type OCTET STRING)
Expand All @@ -112,13 +123,13 @@ impl Signature {
[0x04, _, signature_bytes @ ..] => {
Signature::from_algorithm_and_bytes(algorithm, signature_bytes)
}
_ => bail!("Malformed signature"),
_ => Err(SignatureError::MalformedSignError.into()),
}
}
}

impl TryFrom<CryptoPayload> for Signature {
type Error = anyhow::Error;
type Error = CryptoError;

fn try_from(value: CryptoPayload) -> std::result::Result<Self, Self::Error> {
Signature::from_algorithm_and_bytes(value.algorithm, &value.bytes)
Expand Down
Loading
Loading