Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add sig check #460

Merged
merged 48 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
dd309e4
refactor(add 61284 sig check): verify msg vs pub key
cong-or Apr 30, 2024
6bb69d3
refactor(add 61284 sig check): verify msg vs pub key
cong-or Apr 30, 2024
c6aa019
refactor(add 61284 sig check): verify msg vs pub key
cong-or Apr 30, 2024
a09b55a
refactor(add 61284 sig check): verify msg vs pub key
cong-or Apr 30, 2024
110082d
Merge branch 'main' into add-sig-check
cong-or Apr 30, 2024
e6f1939
Merge branch 'main' into add-sig-check
cong-or May 1, 2024
78d88f2
test ci
May 1, 2024
35b6536
test ci
cong-or May 1, 2024
ab7a2a0
test ci
cong-or May 1, 2024
2385998
ci test
cong-or May 1, 2024
cafc284
snapshot test
cong-or May 2, 2024
19cd403
Verify a signature on a message with this keypair's public key.
cong-or May 3, 2024
4a705de
Verify a signature on a message with this keypair's public key.
cong-or May 3, 2024
8b165e4
new snapshot with dalek
cong-or May 3, 2024
5ee5351
verify strict rm
cong-or May 3, 2024
9dfee2c
test error handling
cong-or May 3, 2024
ef402e6
test error handling
cong-or May 3, 2024
e63d9c9
test error handling
cong-or May 3, 2024
f33a327
test error handling
cong-or May 3, 2024
26f0bbe
test error handling
cong-or May 3, 2024
ab33aa6
test error handling
cong-or May 3, 2024
7ed14fb
test error handling
cong-or May 3, 2024
74cf22c
test dalek snapshot
cong-or May 4, 2024
32196dd
refactor
cong-or May 4, 2024
5d165b5
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
0a04966
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
1461e3f
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
0b2574f
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
75cf894
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
d983600
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
150c7b3
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
f760f10
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
1efcf62
fix(legacy jor sig check): chain crypto sig check
cong-or May 4, 2024
a9e2586
fix(legacy jor sig check): chain crypto sig check
cong-or May 5, 2024
30a7086
fix(legacy jor sig check): chain crypto sig check
cong-or May 5, 2024
37524ee
fix(legacy jor sig check): chain crypto sig check
cong-or May 5, 2024
d7efd86
fix(rebuild original signed 61284 payload) sig check works
cong-or May 6, 2024
e19a33d
fix(rebuild original signed 61284 payload) sig check works
cong-or May 6, 2024
31645c6
clippy
cong-or May 7, 2024
a23405f
Merge branch 'main' into add-sig-check
cong-or May 7, 2024
1cd2886
fix snapshot
May 7, 2024
d99436e
Merge branch 'main' into add-sig-check
cong-or May 7, 2024
e6f0896
Merge branch 'main' into add-sig-check
cong-or May 7, 2024
b2606dd
fmt
cong-or May 7, 2024
d97e39b
fmt
cong-or May 7, 2024
17e73d4
dalek snapshot
cong-or May 7, 2024
6483266
dalek snapshot
cong-or May 7, 2024
4f1590f
Merge branch 'main' into add-sig-check
cong-or May 7, 2024
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
1 change: 1 addition & 0 deletions catalyst-gateway/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ anyhow = { workspace = true }
handlebars = { workspace = true }
cddl = { workspace = true }
ciborium = { workspace = true }
ed25519-dalek = "2.1.1"
122 changes: 80 additions & 42 deletions catalyst-gateway/bin/src/cardano/cip36_registration/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
//! Verify registration TXs

use anyhow::Ok;
use cardano_chain_follower::Network;
use ciborium::{value::Integer, Value};
use cryptoxide::{blake2b::Blake2b, digest::Digest};
use ed25519_dalek::{Signature, Verifier, VerifyingKey};
use pallas::ledger::{
primitives::{conway::Metadatum, Fragment},
traverse::MultiEraMeta,
};
use serde::{Deserialize, Serialize};

use super::util::hash;

/// Pub key
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub(crate) struct PubKey(Vec<u8>);
Expand Down Expand Up @@ -43,42 +48,6 @@ impl PubKey {
}
}

/// Ed25519 signature.
///
/// This type represents a container for the byte serialization of an Ed25519
/// signature, and does not necessarily represent well-formed field or curve
/// elements.
///
/// Signature verification libraries are expected to reject invalid field
/// elements at the time a signature is verified.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(C)]
pub struct Signature {
/// Component of an Ed25519 signature when serialized as bytes
r: [u8; Self::COMPONENT_SIZE],
/// Component of an Ed25519 signature when serialized as bytes
s: [u8; Self::COMPONENT_SIZE],
}

impl Signature {
/// Size of an encoded Ed25519 signature in bytes.
const BYTE_SIZE: usize = Self::COMPONENT_SIZE * 2;
/// Size of a single component of an Ed25519 signature.
const COMPONENT_SIZE: usize = 32;

/// Parse an Ed25519 signature from a byte slice.
fn from_bytes(bytes: &[u8; Self::BYTE_SIZE]) -> Self {
let mut r = <[u8; Self::COMPONENT_SIZE]>::default();
let mut s = <[u8; Self::COMPONENT_SIZE]>::default();

let components = bytes.split_at(Self::COMPONENT_SIZE);
r.copy_from_slice(components.0);
s.copy_from_slice(components.1);

Self { r, s }
}
}

/// The source of voting power for a given registration
///
/// The voting power can either come from:
Expand Down Expand Up @@ -142,6 +111,7 @@ impl Cip36Metadata {
let mut registration = None;
let mut raw_metadata = None;
let mut signature = None;
let mut raw_61284 = None;
let mut errors_report = Vec::new();

if let pallas::ledger::traverse::MultiEraMeta::AlonzoCompatible(tx_metadata) = tx_metadata {
Expand All @@ -157,6 +127,7 @@ impl Cip36Metadata {
},
Some,
);

for (key, metadata) in tx_metadata.iter() {
match *key {
CIP36_REGISTRATION_CBOR_KEY => {
Expand All @@ -168,6 +139,14 @@ impl Cip36Metadata {
},
Some,
);

raw_61284 = original_61284_payload(metadata).map_or_else(
|err| {
errors_report.push(format!("{err}"));
None
},
Some,
);
},
CIP36_WITNESS_CBOR_KEY => {
signature = inspect_witness_from_metadata(metadata).map_or_else(
Expand All @@ -183,15 +162,48 @@ impl Cip36Metadata {
}
};

if let Some(raw_61284) = raw_61284 {
let _ =
validate_signature(&raw_61284, &registration.clone(), &signature).map_err(|err| {
errors_report.push(format!("{err}"));
});
}

Some(Self {
registration,
registration: registration.clone(),
raw_metadata,
signature,
errors_report,
})
}
}

/// The signature is generated by:
/// - CBOR encoding the registration
/// - blake2b-256 hashing those bytes
/// - signing the hash with the private key used to generate the stake key
pub fn validate_signature(
raw_61284: &[u8], registration: &Option<Registration>, signature: &Option<Signature>,
) -> anyhow::Result<()> {
let hash_bytes = hash(raw_61284);

let verifying_key = VerifyingKey::from_bytes(
registration
.clone()
.ok_or("no registration data".to_string())
.map_err(|err| anyhow::anyhow!("{err}"))?
.stake_key
.bytes()
.try_into()?,
)?;

let sig = signature
.ok_or("cannot verify payload without signature".to_string())
.map_err(|err| anyhow::anyhow!("{err}"))?;

Ok(verifying_key.verify(&hash_bytes, &sig)?)
}

/// Validate binary data against CIP-36 registration CDDL spec
fn validate_cip36_registration(data: &[u8]) -> anyhow::Result<()> {
/// Cip36 registration CDDL definition
Expand Down Expand Up @@ -270,16 +282,42 @@ fn inspect_signature(cbor_value: ciborium::value::Value) -> anyhow::Result<Signa
.map_err(|_| anyhow::anyhow!("Invalid cip36 witness cbor, signature should be bytes"))?
.try_into()
.map_err(|vec: Vec<_>| {
anyhow::anyhow!(
"Invalid signature length, expected: {}, got {}",
Signature::BYTE_SIZE,
vec.len()
)
anyhow::anyhow!("Invalid signature length, expected: got {}", vec.len())
})?;

Ok(Signature::from_bytes(&signature))
}

/// Rebuild 61284 from pallas metadata to match original signed 61284 payload, pallas does
/// not preserve exact 61284 bytes which is required for signature verification. It must
/// be rebuilt and re-serialized to perform signature verification.
fn original_61284_payload(metadata: &Metadatum) -> anyhow::Result<Vec<u8>> {
/// 61284 CIP-36
const CIP_36_61284: usize = 61284;

let metadata_bytes = metadata
.encode_fragment()
.map_err(|err| anyhow::anyhow!("cannot encode metadata into bytes {err}"))?;

let cbor_value = ciborium::de::from_reader::<ciborium::Value, _>(metadata_bytes.as_slice())
.map_err(|err| anyhow::anyhow!("Cannot decode cbor object from bytes, err: {err}"))?;

let cbor_map = cbor_value.as_map().ok_or(anyhow::anyhow!(
"Not a valid cbor cip36 registration, should be a map"
))?;

let registration_payload = Value::Map(vec![(
Value::Integer(Integer::from(CIP_36_61284)),
ciborium::Value::Map(cbor_map.clone()),
)]);

let mut raw_61284 = Vec::new();
ciborium::ser::into_writer(&registration_payload, &mut raw_61284)
.map_err(|err| anyhow::anyhow!("Cannot decode cbor object from bytes, err: {err}"))?;

Ok(raw_61284)
}

/// Extract registration from tx metadata
fn inspect_registration_from_metadata(
metadata: &Metadatum, network: Network,
Expand Down
14 changes: 14 additions & 0 deletions catalyst-gateway/bin/src/cardano/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ pub type StakeCredentialHash = String;
/// Correct stake credential key in hex
pub type StakeCredentialKey = String;

/// Hash size
pub(crate) const BLAKE_2B_256_HASH_SIZE: usize = 256 / 8;

/// Helper function to generate the `blake2b_256` hash of a byte slice
#[allow(dead_code)]
pub(crate) fn hash(bytes: &[u8]) -> [u8; BLAKE_2B_256_HASH_SIZE] {
let mut digest = [0u8; BLAKE_2B_256_HASH_SIZE];
let mut context = Blake2b::new(BLAKE_2B_256_HASH_SIZE);
context.input(bytes);
context.result(&mut digest);

digest
}

#[derive(Default, Debug, Serialize)]
/// Assets
pub struct Asset {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl EventDB {
let Some(cip36) = Cip36Metadata::generate_from_tx_metadata(&tx.metadata(), network) else {
return Ok(());
};

let tx_hash = tx.hash().to_vec();
let (stake_credential, voting_info, rewards_address, nonce) =
if let Some(reg) = cip36.registration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@
"voting_power": 403470717,
"voting_purpose": 0,
"tx_id": 1205915,
"nonce": 36168765
"nonce": 36151489
},
{
"delegations": [
Expand All @@ -224,7 +224,7 @@
{
"delegations": [
[
"0x030606c166a947a33bf60043e70f240eac1df7936aa056429f2521553f9d4220",
"0x926cddb15c7ec46a71db0323d1d8ab18a83f8f9d73e159000b2dcfc3f7050de0",
1
]
],
Expand All @@ -233,7 +233,7 @@
"voting_power": 894284145,
"voting_purpose": 0,
"tx_id": 1151432,
"nonce": 181865062
"nonce": 36190561
},
{
"delegations": "0x1493fb2c46ef5e99928f08776b83166b2b18833389d06f8cb66e816e3829a627",
Expand Down
Loading