1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use digest::Digest;
use serde_derive::{Deserialize, Serialize};
#[derive(Debug)]
pub enum Secp256k1Error {
InvalidSignature,
InvalidRecoveryId,
InvalidDataOffsets,
InvalidInstructionDataSize,
}
pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20;
pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 11;
pub fn construct_eth_pubkey(pubkey: &secp256k1::PublicKey) -> [u8; HASHED_PUBKEY_SERIALIZED_SIZE] {
let mut addr = [0u8; HASHED_PUBKEY_SERIALIZED_SIZE];
addr.copy_from_slice(&sha3::Keccak256::digest(&pubkey.serialize()[1..])[12..]);
assert_eq!(addr.len(), HASHED_PUBKEY_SERIALIZED_SIZE);
addr
}
#[derive(Default, Serialize, Deserialize, Debug)]
pub struct SecpSignatureOffsets {
pub signature_offset: u16,
pub signature_instruction_index: u8,
pub eth_address_offset: u16,
pub eth_address_instruction_index: u8,
pub message_data_offset: u16,
pub message_data_size: u16,
pub message_instruction_index: u8,
}
fn get_data_slice<'a>(
instruction_datas: &'a [&[u8]],
instruction_index: u8,
offset_start: u16,
size: usize,
) -> Result<&'a [u8], Secp256k1Error> {
let signature_index = instruction_index as usize;
if signature_index > instruction_datas.len() {
return Err(Secp256k1Error::InvalidDataOffsets);
}
let signature_instruction = &instruction_datas[signature_index];
let start = offset_start as usize;
let end = start + size;
if end > signature_instruction.len() {
return Err(Secp256k1Error::InvalidSignature);
}
Ok(&instruction_datas[signature_index][start..end])
}
pub fn verify_eth_addresses(
data: &[u8],
instruction_datas: &[&[u8]],
) -> Result<(), Secp256k1Error> {
if data.is_empty() {
return Err(Secp256k1Error::InvalidInstructionDataSize);
}
let count = data[0] as usize;
let expected_data_size = 1 + count * SIGNATURE_OFFSETS_SERIALIZED_SIZE;
if data.len() < expected_data_size {
return Err(Secp256k1Error::InvalidInstructionDataSize);
}
for i in 0..count {
let start = 1 + i * SIGNATURE_OFFSETS_SERIALIZED_SIZE;
let end = start + SIGNATURE_OFFSETS_SERIALIZED_SIZE;
let offsets: SecpSignatureOffsets = bincode::deserialize(&data[start..end])
.map_err(|_| Secp256k1Error::InvalidSignature)?;
let signature_index = offsets.signature_instruction_index as usize;
if signature_index > instruction_datas.len() {
return Err(Secp256k1Error::InvalidInstructionDataSize);
}
let signature_instruction = instruction_datas[signature_index];
let sig_start = offsets.signature_offset as usize;
let sig_end = sig_start + SIGNATURE_SERIALIZED_SIZE;
if sig_end >= signature_instruction.len() {
return Err(Secp256k1Error::InvalidSignature);
}
let signature =
secp256k1::Signature::parse_slice(&signature_instruction[sig_start..sig_end])
.map_err(|_| Secp256k1Error::InvalidSignature)?;
let recovery_id = secp256k1::RecoveryId::parse(signature_instruction[sig_end])
.map_err(|_| Secp256k1Error::InvalidRecoveryId)?;
let eth_address_slice = get_data_slice(
&instruction_datas,
offsets.eth_address_instruction_index,
offsets.eth_address_offset,
HASHED_PUBKEY_SERIALIZED_SIZE,
)?;
let message_slice = get_data_slice(
&instruction_datas,
offsets.message_instruction_index,
offsets.message_data_offset,
offsets.message_data_size as usize,
)?;
let mut hasher = sha3::Keccak256::new();
hasher.update(message_slice);
let message_hash = hasher.finalize();
let pubkey = secp256k1::recover(
&secp256k1::Message::parse_slice(&message_hash).unwrap(),
&signature,
&recovery_id,
)
.map_err(|_| Secp256k1Error::InvalidSignature)?;
let eth_address = construct_eth_pubkey(&pubkey);
if eth_address_slice != eth_address {
return Err(Secp256k1Error::InvalidSignature);
}
}
Ok(())
}