Skip to content

Commit

Permalink
secp256r1 precompile implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
deanmlittle committed Oct 25, 2023
1 parent 56294df commit 2041437
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 72 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ parking_lot = "0.12"
pbkdf2 = { version = "0.11.0", default-features = false }
pem = "1.1.1"
percentage = "0.1.0"
p256 = { version = "0.10.1", features = ["arithmetic"] }
p256 = { version = "0.10.1" }
pickledb = { version = "0.5.1", default-features = false }
pkcs8 = "0.8.0"
predicates = "2.1"
Expand Down
2 changes: 1 addition & 1 deletion sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ num-derive = { workspace = true }
num-traits = { workspace = true }
num_enum = { workspace = true }
pbkdf2 = { workspace = true }
p256 = { workspace = true, features = ["arithmetic"], optional = true }
p256 = { workspace = true, optional = true }
qstring = { workspace = true }
qualifier_attr = { workspace = true }
rand = { workspace = true, optional = true }
Expand Down
98 changes: 28 additions & 70 deletions sdk/src/secp256r1_instruction.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! Instructions for the [secp256r1 native program][np].
//!
//TODO
//! [np]: https://docs.solana.com/developing/runtime-facilities/programs#secp256r1-program
#![cfg(feature = "full")]

use {
crate::{feature_set::FeatureSet, instruction::Instruction, precompiles::PrecompileError},
bytemuck::{bytes_of, Pod, Zeroable},
Expand All @@ -14,7 +14,6 @@ use {
};

pub const COMPRESSED_PUBKEY_SERIALIZED_SIZE: usize = 33;
pub const PUBKEY_SERIALIZED_SIZE: usize = 65;
pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14;
// bytemuck requires structures to be aligned
Expand All @@ -24,17 +23,17 @@ pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFS
#[derive(Default, Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)]
#[repr(C)]
pub struct Secp256r1SignatureOffsets {
signature_offset: u16, // offset to secp256r1 signature of 64 bytes
signature_offset: u16, // offset to compact secp256r1 signature of 64 bytes
signature_instruction_index: u16, // instruction index to find signature
public_key_offset: u16, // offset to public key of 33 or 65 bytes
public_key_offset: u16, // offset to compressed public key of 33 bytes
public_key_instruction_index: u16, // instruction index to find public key
message_data_offset: u16, // offset to start of message data
message_data_size: u16, // size of message data
message_instruction_index: u16, // index of instruction data to get message data
}

pub fn new_secp256r1_instruction(signer: &SigningKey, message: &[u8]) -> Instruction {
let signature = signer.sign(message);
let signature = signer.sign(&message);
let signature = signature.normalize_s().unwrap_or(signature).to_vec();
let pubkey = VerifyingKey::from(signer).to_encoded_point(true).to_bytes();

Expand Down Expand Up @@ -125,34 +124,15 @@ pub fn verify(
SIGNATURE_SERIALIZED_SIZE,
)?;

let signature =
Signature::try_from(signature).map_err(|_| PrecompileError::InvalidSignature)?;

// TODO: Do we enforce low S?
// if signature.s().is_high().into() {
// return Err(PrecompileError::InvalidSignature);
// }

// TODO: Do we disallow uncompressed pubkeys?
// let pubkey = get_data_slice(
// data,
// instruction_datas,
// offsets.public_key_instruction_index,
// offsets.public_key_offset,
// COMPRESSED_PUBKEY_SERIALIZED_SIZE
// )?;

// Parse out SEC1 encoded pubkey
let pubkey = get_sec1_pubkey_slice(
// Parse out pubkey
let pubkey = get_data_slice(
data,
instruction_datas,
offsets.public_key_instruction_index,
offsets.public_key_offset
offsets.public_key_offset,
COMPRESSED_PUBKEY_SERIALIZED_SIZE
)?;

let publickey = p256::ecdsa::VerifyingKey::from_sec1_bytes(pubkey)
.map_err(|_| PrecompileError::InvalidPublicKey)?;

// Parse out message
let message = get_data_slice(
data,
Expand All @@ -162,60 +142,38 @@ pub fn verify(
offsets.message_data_size as usize,
)?;

publickey
.verify(message, &signature)
let signature =
Signature::try_from(signature).map_err(|_| PrecompileError::InvalidSignature)?;

// Enforce Low-S
if signature.s().is_high().into() {
return Err(PrecompileError::InvalidSignature);
}

let publickey = p256::ecdsa::VerifyingKey::from_sec1_bytes(pubkey)
.map_err(|_| PrecompileError::InvalidPublicKey)?;

publickey.verify(&message, &signature)
.map_err(|_| PrecompileError::InvalidSignature)?;
}
Ok(())
}

fn get_instruction_data<'a>(
data: &'a [u8],
instruction_datas: &'a [&[u8]],
instruction_index: u16
) -> Result<&'a [u8], PrecompileError> {
match instruction_index {
u16::MAX => {
Ok(data)
},
_ => {
let signature_index = instruction_index as usize;
if signature_index >= instruction_datas.len() {
return Err(PrecompileError::InvalidDataOffsets);
}
Ok(instruction_datas[signature_index])
}
}

fn get_data_slice<'a>(
data: &'a [u8],
instruction_datas: &'a [&[u8]],
instruction_index: u16,
offset_start: u16,
size: usize,
) -> Result<&'a [u8], PrecompileError> {
let instruction = get_instruction_data(data, instruction_datas, instruction_index)?;
let start = offset_start as usize;
let end = start.saturating_add(size);
if end > instruction.len() {
return Err(PrecompileError::InvalidDataOffsets);
}

Ok(&instruction[start..end])
}

fn get_sec1_pubkey_slice<'a>(
data: &'a [u8],
instruction_datas: &'a [&[u8]],
instruction_index: u16,
offset_start: u16,
) -> Result<&'a [u8], PrecompileError> {
let instruction = get_instruction_data(data, instruction_datas, instruction_index)?;

let size = match instruction.get(offset_start as usize).ok_or(PrecompileError::InvalidPublicKey)? {
0x02 | 0x03 => COMPRESSED_PUBKEY_SERIALIZED_SIZE,
0x04 => PUBKEY_SERIALIZED_SIZE,
_ => return Err(PrecompileError::InvalidPublicKey)
let instruction = if instruction_index == u16::MAX {
data
} else {
let signature_index = instruction_index as usize;
if signature_index >= instruction_datas.len() {
return Err(PrecompileError::InvalidDataOffsets);
}
instruction_datas[signature_index]
};

let start = offset_start as usize;
Expand Down

0 comments on commit 2041437

Please sign in to comment.