Skip to content

Commit

Permalink
hook r1 ecrecover
Browse files Browse the repository at this point in the history
  • Loading branch information
umadayal committed Dec 4, 2024
1 parent 75b90be commit 8b3be09
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

44 changes: 36 additions & 8 deletions crates/core/executor/src/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use std::sync::{Arc, RwLock, RwLockWriteGuard};

use hashbrown::HashMap;
use sp1_curves::{
ecdsa::RecoveryId as ecdsaRecoveryId,
k256::{Invert, RecoveryId, Signature, VerifyingKey},
p256::Signature as p256Signature,
p256::{Signature as p256Signature, VerifyingKey as p256VerifyingKey},
};

use crate::Executor;
Expand Down Expand Up @@ -187,20 +188,47 @@ pub fn hook_ed_decompress(_: HookEnv, buf: &[u8]) -> Vec<Vec<u8>> {
/// # Arguments
///
/// * `env` - The environment in which the hook is invoked.
/// * `buf` - The buffer containing the signature.
/// - The signature is 64 bytes.
/// * `buf` - The buffer containing the signature and message hash.
/// - The signature is 65 bytes, the first 64 bytes are the signature and the last byte is the
/// recovery ID.
/// - The message hash is 32 bytes.
///
/// The result is a single 32 byte vector containing s inverse.
/// The result is returned as a status and a pair of bytes, where the first 32 bytes are the X coordinate
/// and the second 32 bytes are the Y coordinate of the decompressed point.
///
/// A status of 0 indicates that the public key could not be recovered.
///
/// WARNING: This function is used to recover the public key outside of the zkVM context. These
/// values must be constrained by the zkVM for correctness.
#[must_use]
pub fn hook_r1_ecrecover(_: HookEnv, buf: &[u8]) -> Vec<Vec<u8>> {
assert_eq!(buf.len(), 64, "ecrecover input should have length 64");
let sig: &[u8; 64] = buf.try_into().unwrap();
let sig = p256Signature::from_slice(sig).unwrap();
assert_eq!(buf.len(), 65 + 32, "ecrecover input should have length 65 + 32, this is a bug.");
let (sig, msg_hash) = buf.split_at(65);
let sig: &[u8; 65] = sig.try_into().unwrap();
let msg_hash: &[u8; 32] = msg_hash.try_into().unwrap();

let mut recovery_id = sig[64];
let mut sig = p256Signature::from_slice(&sig[..64]).unwrap();

if let Some(sig_normalized) = sig.normalize_s() {
sig = sig_normalized;
recovery_id ^= 1;
};
let recid = ecdsaRecoveryId::from_byte(recovery_id)
.expect("Computed recovery ID is invalid, this is a bug.");

// Attempting to recover the public key has failed, write a 0 to indicate to the caller.
let Ok(recovered_key) = p256VerifyingKey::recover_from_prehash(&msg_hash[..], &sig, recid)
else {
return vec![vec![0]];
};

let bytes = recovered_key.to_sec1_bytes();

let (_, s) = sig.split_scalars();
let s_inverse = s.invert();

vec![s_inverse.to_bytes().to_vec()]
vec![vec![1], bytes.to_vec(), s_inverse.to_bytes().to_vec()]
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions crates/curves/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ generic-array = { version = "1.1.0", features = ["alloc", "serde"] }
amcl = { package = "snowbridge-amcl", version = "1.0.2", default-features = false, features = [
"bls381",
] }
ecdsa = "0.16.9"
elliptic-curve = "0.13.8"
dashu = "0.4.2"

Expand Down
4 changes: 4 additions & 0 deletions crates/curves/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ pub mod p256 {
};
}

pub mod ecdsa {
pub use ecdsa::RecoveryId;
}

use params::{FieldParameters, NumWords};
use sp1_primitives::consts::WORD_SIZE;
use std::{
Expand Down
1 change: 1 addition & 0 deletions crates/test-artifacts/programs/Cargo.lock

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

0 comments on commit 8b3be09

Please sign in to comment.