solana_secp256r1_program/
lib.rs1use bytemuck::{Pod, Zeroable};
13pub use solana_sdk_ids::secp256r1_program::{check_id, id, ID};
14
15#[derive(Default, Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)]
16#[repr(C)]
17pub struct Secp256r1SignatureOffsets {
18 pub signature_offset: u16,
20
21 pub signature_instruction_index: u16,
23
24 pub public_key_offset: u16,
26
27 pub public_key_instruction_index: u16,
29
30 pub message_data_offset: u16,
32
33 pub message_data_size: u16,
35
36 pub message_instruction_index: u16,
38}
39
40#[cfg(all(not(target_arch = "wasm32"), not(target_os = "solana")))]
41mod target_arch {
42 use {
43 crate::Secp256r1SignatureOffsets,
44 bytemuck::bytes_of,
45 openssl::{bn::BigNum, ec::EcKey, ecdsa::EcdsaSig, nid::Nid, pkey::PKey, sign::Signer},
46 solana_instruction::Instruction,
47 };
48
49 pub const COMPRESSED_PUBKEY_SERIALIZED_SIZE: usize = 33;
50 pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
51 pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14;
52 pub const SIGNATURE_OFFSETS_START: usize = 2;
53 pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START;
54
55 pub const SECP256R1_ORDER: [u8; FIELD_SIZE] = [
57 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
58 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63,
59 0x25, 0x51,
60 ];
61
62 pub const SECP256R1_ORDER_MINUS_ONE: [u8; FIELD_SIZE] = [
64 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
65 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63,
66 0x25, 0x50,
67 ];
68
69 pub const SECP256R1_HALF_ORDER: [u8; FIELD_SIZE] = [
71 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
72 0xFF, 0xDE, 0x73, 0x7D, 0x56, 0xD3, 0x8B, 0xCF, 0x42, 0x79, 0xDC, 0xE5, 0x61, 0x7E, 0x31,
73 0x92, 0xA8,
74 ];
75 pub const FIELD_SIZE: usize = 32;
77
78 pub fn sign_message(
79 message: &[u8],
80 priv_key_bytes_der: &[u8],
81 ) -> Result<[u8; SIGNATURE_SERIALIZED_SIZE], Box<dyn core::error::Error>> {
82 let signing_key = EcKey::private_key_from_der(priv_key_bytes_der)?;
83 if signing_key.group().curve_name() != Some(Nid::X9_62_PRIME256V1) {
84 return Err(("Signing key must be on the secp256r1 curve".to_string()).into());
85 }
86
87 let signing_key_pkey = PKey::from_ec_key(signing_key)?;
88
89 let mut signer = Signer::new(openssl::hash::MessageDigest::sha256(), &signing_key_pkey)?;
90 signer.update(message)?;
91 let signature = signer.sign_to_vec()?;
92
93 let ecdsa_sig = EcdsaSig::from_der(&signature)?;
94 let r = ecdsa_sig.r().to_vec();
95 let s = ecdsa_sig.s().to_vec();
96 let mut signature = [0u8; SIGNATURE_SERIALIZED_SIZE];
97
98 let mut padded_r = [0u8; FIELD_SIZE];
100 let mut padded_s = [0u8; FIELD_SIZE];
101 padded_r[FIELD_SIZE.saturating_sub(r.len())..].copy_from_slice(&r);
102 padded_s[FIELD_SIZE.saturating_sub(s.len())..].copy_from_slice(&s);
103
104 signature[..FIELD_SIZE].copy_from_slice(&padded_r);
105 signature[FIELD_SIZE..].copy_from_slice(&padded_s);
106
107 let s_bignum = BigNum::from_slice(&s)?;
109 let half_order = BigNum::from_slice(&SECP256R1_HALF_ORDER)?;
110 let order = BigNum::from_slice(&SECP256R1_ORDER)?;
111 if s_bignum > half_order {
112 let mut new_s = BigNum::new()?;
113 new_s.checked_sub(&order, &s_bignum)?;
114 let new_s_bytes = new_s.to_vec();
115
116 let mut new_padded_s = [0u8; FIELD_SIZE];
118 new_padded_s[FIELD_SIZE.saturating_sub(new_s_bytes.len())..]
119 .copy_from_slice(&new_s_bytes);
120
121 signature[FIELD_SIZE..].copy_from_slice(&new_padded_s);
122 }
123 Ok(signature)
124 }
125
126 pub fn new_secp256r1_instruction_with_signature(
127 message: &[u8],
128 signature: &[u8; SIGNATURE_SERIALIZED_SIZE],
129 pubkey: &[u8; COMPRESSED_PUBKEY_SERIALIZED_SIZE],
130 ) -> Instruction {
131 let mut instruction_data = Vec::with_capacity(
132 DATA_START
133 .saturating_add(SIGNATURE_SERIALIZED_SIZE)
134 .saturating_add(COMPRESSED_PUBKEY_SERIALIZED_SIZE)
135 .saturating_add(message.len()),
136 );
137
138 let num_signatures: u8 = 1;
139 let public_key_offset = DATA_START;
140 let signature_offset = public_key_offset.saturating_add(COMPRESSED_PUBKEY_SERIALIZED_SIZE);
141 let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE);
142
143 instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0]));
144
145 let offsets = Secp256r1SignatureOffsets {
146 signature_offset: signature_offset as u16,
147 signature_instruction_index: u16::MAX,
148 public_key_offset: public_key_offset as u16,
149 public_key_instruction_index: u16::MAX,
150 message_data_offset: message_data_offset as u16,
151 message_data_size: message.len() as u16,
152 message_instruction_index: u16::MAX,
153 };
154
155 instruction_data.extend_from_slice(bytes_of(&offsets));
156 instruction_data.extend_from_slice(pubkey);
157 instruction_data.extend_from_slice(signature);
158 instruction_data.extend_from_slice(message);
159
160 Instruction {
161 program_id: crate::id(),
162 accounts: vec![],
163 data: instruction_data,
164 }
165 }
166}
167
168pub use self::target_arch::*;