Skip to main content

rialo_s_secp256r1_program/
lib.rs

1//! Instructions for the [secp256r1 native program](https://docs.solana.com/developing/runtime-facilities/programs#secp256r1-program).
2//!
3//! Note on Signature Malleability:
4//! This precompile requires low-S values in signatures (s <= half_curve_order) to prevent signature malleability.
5//! Signature malleability means that for a valid signature (r,s), (r, order-s) is also valid for the
6//! same message and public key.
7//!
8//! This property can be problematic for developers who assume each signature is unique. Without enforcing
9//! low-S values, the same message and key can produce two different valid signatures, potentially breaking
10//! replay protection schemes that rely on signature uniqueness.
11use bytemuck::{Pod, Zeroable};
12pub use rialo_s_sdk_ids::secp256r1_program::{check_id, id, ID};
13
14#[derive(Default, Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)]
15#[repr(C)]
16pub struct Secp256r1SignatureOffsets {
17    /// Offset to compact secp256r1 signature of 64 bytes
18    pub signature_offset: u16,
19
20    /// Instruction index where the signature can be found
21    pub signature_instruction_index: u16,
22
23    /// Offset to compressed public key of 33 bytes
24    pub public_key_offset: u16,
25
26    /// Instruction index where the public key can be found
27    pub public_key_instruction_index: u16,
28
29    /// Offset to the start of message data
30    pub message_data_offset: u16,
31
32    /// Size of message data in bytes
33    pub message_data_size: u16,
34
35    /// Instruction index where the message data can be found
36    pub message_instruction_index: u16,
37}
38
39#[cfg(all(not(target_arch = "wasm32"), not(target_os = "solana")))]
40mod target_arch {
41    use bytemuck::bytes_of;
42    use openssl::{
43        bn::{BigNum, BigNumContext},
44        ec::{EcGroup, EcKey, EcPoint},
45        ecdsa::EcdsaSig,
46        nid::Nid,
47        pkey::{PKey, Private},
48        sign::{Signer, Verifier},
49    };
50    use rialo_s_feature_set::FeatureSet;
51    use rialo_s_instruction::Instruction;
52    use rialo_s_precompile_error::PrecompileError;
53
54    use crate::Secp256r1SignatureOffsets;
55
56    pub const COMPRESSED_PUBKEY_SERIALIZED_SIZE: usize = 33;
57    pub const SIGNATURE_SERIALIZED_SIZE: usize = 64;
58    pub const SIGNATURE_OFFSETS_SERIALIZED_SIZE: usize = 14;
59    pub const SIGNATURE_OFFSETS_START: usize = 2;
60    pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + SIGNATURE_OFFSETS_START;
61
62    // Order as defined in SEC2: 2.7.2 Recommended Parameters secp256r1
63    pub const SECP256R1_ORDER: [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, 0x51,
67    ];
68
69    // Computed SECP256R1_ORDER - 1
70    pub const SECP256R1_ORDER_MINUS_ONE: [u8; FIELD_SIZE] = [
71        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
72        0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63,
73        0x25, 0x50,
74    ];
75
76    // Computed half order
77    const SECP256R1_HALF_ORDER: [u8; FIELD_SIZE] = [
78        0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
79        0xFF, 0xDE, 0x73, 0x7D, 0x56, 0xD3, 0x8B, 0xCF, 0x42, 0x79, 0xDC, 0xE5, 0x61, 0x7E, 0x31,
80        0x92, 0xA8,
81    ];
82    // Field size in bytes
83    const FIELD_SIZE: usize = 32;
84
85    pub fn new_secp256r1_instruction(
86        message: &[u8],
87        signing_key: EcKey<Private>,
88    ) -> Result<Instruction, Box<dyn std::error::Error>> {
89        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
90        if signing_key.group().curve_name() != Some(Nid::X9_62_PRIME256V1) {
91            return Err(("Signing key must be on the secp256r1 curve".to_string()).into());
92        }
93
94        let mut ctx = BigNumContext::new()?;
95        let pubkey = signing_key.public_key().to_bytes(
96            &group,
97            openssl::ec::PointConversionForm::COMPRESSED,
98            &mut ctx,
99        )?;
100
101        let signing_key_pkey = PKey::from_ec_key(signing_key)?;
102
103        let mut signer = Signer::new(openssl::hash::MessageDigest::sha256(), &signing_key_pkey)?;
104        signer.update(message)?;
105        let signature = signer.sign_to_vec()?;
106
107        let ecdsa_sig = EcdsaSig::from_der(&signature)?;
108        let r = ecdsa_sig.r().to_vec();
109        let s = ecdsa_sig.s().to_vec();
110        let mut signature = vec![0u8; SIGNATURE_SERIALIZED_SIZE];
111
112        // Incase of an r or s value of 31 bytes we need to pad it to 32 bytes
113        let mut padded_r = vec![0u8; FIELD_SIZE];
114        let mut padded_s = vec![0u8; FIELD_SIZE];
115        padded_r[FIELD_SIZE.saturating_sub(r.len())..].copy_from_slice(&r);
116        padded_s[FIELD_SIZE.saturating_sub(s.len())..].copy_from_slice(&s);
117
118        signature[..FIELD_SIZE].copy_from_slice(&padded_r);
119        signature[FIELD_SIZE..].copy_from_slice(&padded_s);
120
121        // Check if s > half_order, if so, compute s = order - s
122        let s_bignum = BigNum::from_slice(&s)?;
123        let half_order = BigNum::from_slice(&SECP256R1_HALF_ORDER)?;
124        let order = BigNum::from_slice(&SECP256R1_ORDER)?;
125        if s_bignum > half_order {
126            let mut new_s = BigNum::new()?;
127            new_s.checked_sub(&order, &s_bignum)?;
128            let new_s_bytes = new_s.to_vec();
129
130            // Incase the new s value is 31 bytes we need to pad it to 32 bytes
131            let mut new_padded_s = vec![0u8; FIELD_SIZE];
132            new_padded_s[FIELD_SIZE.saturating_sub(new_s_bytes.len())..]
133                .copy_from_slice(&new_s_bytes);
134
135            signature[FIELD_SIZE..].copy_from_slice(&new_padded_s);
136        }
137
138        assert_eq!(pubkey.len(), COMPRESSED_PUBKEY_SERIALIZED_SIZE);
139        assert_eq!(signature.len(), SIGNATURE_SERIALIZED_SIZE);
140
141        let mut instruction_data = Vec::with_capacity(
142            DATA_START
143                .saturating_add(SIGNATURE_SERIALIZED_SIZE)
144                .saturating_add(COMPRESSED_PUBKEY_SERIALIZED_SIZE)
145                .saturating_add(message.len()),
146        );
147
148        let num_signatures: u8 = 1;
149        let public_key_offset = DATA_START;
150        let signature_offset = public_key_offset.saturating_add(COMPRESSED_PUBKEY_SERIALIZED_SIZE);
151        let message_data_offset = signature_offset.saturating_add(SIGNATURE_SERIALIZED_SIZE);
152
153        instruction_data.extend_from_slice(bytes_of(&[num_signatures, 0]));
154
155        let offsets = Secp256r1SignatureOffsets {
156            signature_offset: signature_offset as u16,
157            signature_instruction_index: u16::MAX,
158            public_key_offset: public_key_offset as u16,
159            public_key_instruction_index: u16::MAX,
160            message_data_offset: message_data_offset as u16,
161            message_data_size: message.len() as u16,
162            message_instruction_index: u16::MAX,
163        };
164
165        instruction_data.extend_from_slice(bytes_of(&offsets));
166        instruction_data.extend_from_slice(&pubkey);
167        instruction_data.extend_from_slice(&signature);
168        instruction_data.extend_from_slice(message);
169
170        Ok(Instruction {
171            program_id: crate::id(),
172            accounts: vec![],
173            data: instruction_data,
174        })
175    }
176
177    pub fn verify(
178        data: &[u8],
179        instruction_datas: &[&[u8]],
180        _feature_set: &FeatureSet,
181    ) -> Result<(), PrecompileError> {
182        if data.len() < SIGNATURE_OFFSETS_START {
183            return Err(PrecompileError::InvalidInstructionDataSize);
184        }
185        let num_signatures = data[0] as usize;
186        if num_signatures == 0 {
187            return Err(PrecompileError::InvalidInstructionDataSize);
188        }
189        if num_signatures > 8 {
190            return Err(PrecompileError::InvalidInstructionDataSize);
191        }
192
193        let expected_data_size = num_signatures
194            .saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
195            .saturating_add(SIGNATURE_OFFSETS_START);
196
197        // We do not check or use the byte at data[1]
198        if data.len() < expected_data_size {
199            return Err(PrecompileError::InvalidInstructionDataSize);
200        }
201
202        // Parse half order from constant
203        let half_order: BigNum = BigNum::from_slice(&SECP256R1_HALF_ORDER)
204            .map_err(|_| PrecompileError::InvalidSignature)?;
205
206        // Parse order - 1 from constant
207        let order_minus_one: BigNum = BigNum::from_slice(&SECP256R1_ORDER_MINUS_ONE)
208            .map_err(|_| PrecompileError::InvalidSignature)?;
209
210        // Create a BigNum for 1
211        let one = BigNum::from_u32(1).map_err(|_| PrecompileError::InvalidSignature)?;
212
213        // Define curve group
214        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
215            .map_err(|_| PrecompileError::InvalidSignature)?;
216        let mut ctx = BigNumContext::new().map_err(|_| PrecompileError::InvalidSignature)?;
217
218        for i in 0..num_signatures {
219            let start = i
220                .saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
221                .saturating_add(SIGNATURE_OFFSETS_START);
222            let end = start.saturating_add(SIGNATURE_OFFSETS_SERIALIZED_SIZE);
223
224            // bytemuck wants structures aligned
225            let offsets: &Secp256r1SignatureOffsets =
226                bytemuck::try_from_bytes(&data[start..end])
227                    .map_err(|_| PrecompileError::InvalidDataOffsets)?;
228
229            // Parse out signature
230            let signature = get_data_slice(
231                data,
232                instruction_datas,
233                offsets.signature_instruction_index,
234                offsets.signature_offset,
235                SIGNATURE_SERIALIZED_SIZE,
236            )?;
237
238            // Parse out pubkey
239            let pubkey = get_data_slice(
240                data,
241                instruction_datas,
242                offsets.public_key_instruction_index,
243                offsets.public_key_offset,
244                COMPRESSED_PUBKEY_SERIALIZED_SIZE,
245            )?;
246
247            // Parse out message
248            let message = get_data_slice(
249                data,
250                instruction_datas,
251                offsets.message_instruction_index,
252                offsets.message_data_offset,
253                offsets.message_data_size as usize,
254            )?;
255
256            let r_bignum = BigNum::from_slice(&signature[..FIELD_SIZE])
257                .map_err(|_| PrecompileError::InvalidSignature)?;
258            let s_bignum = BigNum::from_slice(&signature[FIELD_SIZE..])
259                .map_err(|_| PrecompileError::InvalidSignature)?;
260
261            // Check that the signature is generally in range
262            let within_range = r_bignum >= one
263                && r_bignum <= order_minus_one
264                && s_bignum >= one
265                && s_bignum <= half_order;
266
267            if !within_range {
268                return Err(PrecompileError::InvalidSignature);
269            }
270
271            // Create an ECDSA signature object from the ASN.1 integers
272            let ecdsa_sig = openssl::ecdsa::EcdsaSig::from_private_components(r_bignum, s_bignum)
273                .and_then(|sig| sig.to_der())
274                .map_err(|_| PrecompileError::InvalidSignature)?;
275
276            let public_key_point = EcPoint::from_bytes(&group, pubkey, &mut ctx)
277                .map_err(|_| PrecompileError::InvalidPublicKey)?;
278            let public_key = EcKey::from_public_key(&group, &public_key_point)
279                .map_err(|_| PrecompileError::InvalidPublicKey)?;
280            let public_key_as_pkey =
281                PKey::from_ec_key(public_key).map_err(|_| PrecompileError::InvalidPublicKey)?;
282
283            let mut verifier =
284                Verifier::new(openssl::hash::MessageDigest::sha256(), &public_key_as_pkey)
285                    .map_err(|_| PrecompileError::InvalidSignature)?;
286            verifier
287                .update(message)
288                .map_err(|_| PrecompileError::InvalidSignature)?;
289
290            if !verifier
291                .verify(&ecdsa_sig)
292                .map_err(|_| PrecompileError::InvalidSignature)?
293            {
294                return Err(PrecompileError::InvalidSignature);
295            }
296        }
297        Ok(())
298    }
299
300    fn get_data_slice<'a>(
301        data: &'a [u8],
302        instruction_datas: &'a [&[u8]],
303        instruction_index: u16,
304        offset_start: u16,
305        size: usize,
306    ) -> Result<&'a [u8], PrecompileError> {
307        let instruction = if instruction_index == u16::MAX {
308            data
309        } else {
310            let signature_index = instruction_index as usize;
311            if signature_index >= instruction_datas.len() {
312                return Err(PrecompileError::InvalidDataOffsets);
313            }
314            instruction_datas[signature_index]
315        };
316
317        let start = offset_start as usize;
318        let end = start.saturating_add(size);
319        if end > instruction.len() {
320            return Err(PrecompileError::InvalidDataOffsets);
321        }
322
323        Ok(&instruction[start..end])
324    }
325
326    #[cfg(test)]
327    mod test {
328        use rialo_s_feature_set::FeatureSet;
329        use rialo_s_sdk::{
330            signature::{Keypair, Signer},
331            transaction::Transaction,
332        };
333
334        use super::*;
335
336        fn test_case(
337            num_signatures: u16,
338            offsets: &Secp256r1SignatureOffsets,
339        ) -> Result<(), PrecompileError> {
340            assert_eq!(
341                bytemuck::bytes_of(offsets).len(),
342                SIGNATURE_OFFSETS_SERIALIZED_SIZE
343            );
344
345            let mut instruction_data = vec![0u8; DATA_START];
346            instruction_data[0..SIGNATURE_OFFSETS_START].copy_from_slice(bytes_of(&num_signatures));
347            instruction_data[SIGNATURE_OFFSETS_START..DATA_START]
348                .copy_from_slice(bytes_of(offsets));
349            verify(
350                &instruction_data,
351                &[&[0u8; 100]],
352                &FeatureSet::all_enabled(),
353            )
354        }
355
356        #[test]
357        fn test_invalid_offsets() {
358            rialo_s_logger::setup();
359
360            let mut instruction_data = vec![0u8; DATA_START];
361            let offsets = Secp256r1SignatureOffsets::default();
362            instruction_data[0..SIGNATURE_OFFSETS_START].copy_from_slice(bytes_of(&1u16));
363            instruction_data[SIGNATURE_OFFSETS_START..DATA_START]
364                .copy_from_slice(bytes_of(&offsets));
365            instruction_data.truncate(instruction_data.len() - 1);
366
367            assert_eq!(
368                verify(
369                    &instruction_data,
370                    &[&[0u8; 100]],
371                    &FeatureSet::all_enabled()
372                ),
373                Err(PrecompileError::InvalidInstructionDataSize)
374            );
375
376            let offsets = Secp256r1SignatureOffsets {
377                signature_instruction_index: 1,
378                ..Secp256r1SignatureOffsets::default()
379            };
380            assert_eq!(
381                test_case(1, &offsets),
382                Err(PrecompileError::InvalidDataOffsets)
383            );
384
385            let offsets = Secp256r1SignatureOffsets {
386                message_instruction_index: 1,
387                ..Secp256r1SignatureOffsets::default()
388            };
389            assert_eq!(
390                test_case(1, &offsets),
391                Err(PrecompileError::InvalidDataOffsets)
392            );
393
394            let offsets = Secp256r1SignatureOffsets {
395                public_key_instruction_index: 1,
396                ..Secp256r1SignatureOffsets::default()
397            };
398            assert_eq!(
399                test_case(1, &offsets),
400                Err(PrecompileError::InvalidDataOffsets)
401            );
402        }
403
404        #[test]
405        fn test_invalid_signature_data_size() {
406            rialo_s_logger::setup();
407
408            // Test data.len() < SIGNATURE_OFFSETS_START
409            let small_data = vec![0u8; SIGNATURE_OFFSETS_START - 1];
410            assert_eq!(
411                verify(&small_data, &[&[]], &FeatureSet::all_enabled()),
412                Err(PrecompileError::InvalidInstructionDataSize)
413            );
414
415            // Test num_signatures == 0
416            let mut zero_sigs_data = vec![0u8; DATA_START];
417            zero_sigs_data[0] = 0; // Set num_signatures to 0
418            assert_eq!(
419                verify(&zero_sigs_data, &[&[]], &FeatureSet::all_enabled()),
420                Err(PrecompileError::InvalidInstructionDataSize)
421            );
422
423            // Test num_signatures > 8
424            let mut too_many_sigs = vec![0u8; DATA_START];
425            too_many_sigs[0] = 9; // Set num_signatures to 9
426            assert_eq!(
427                verify(&too_many_sigs, &[&[]], &FeatureSet::all_enabled()),
428                Err(PrecompileError::InvalidInstructionDataSize)
429            );
430        }
431        #[test]
432        fn test_message_data_offsets() {
433            let offsets = Secp256r1SignatureOffsets {
434                message_data_offset: 99,
435                message_data_size: 1,
436                ..Secp256r1SignatureOffsets::default()
437            };
438            assert_eq!(
439                test_case(1, &offsets),
440                Err(PrecompileError::InvalidSignature)
441            );
442
443            let offsets = Secp256r1SignatureOffsets {
444                message_data_offset: 100,
445                message_data_size: 1,
446                ..Secp256r1SignatureOffsets::default()
447            };
448            assert_eq!(
449                test_case(1, &offsets),
450                Err(PrecompileError::InvalidDataOffsets)
451            );
452
453            let offsets = Secp256r1SignatureOffsets {
454                message_data_offset: 100,
455                message_data_size: 1000,
456                ..Secp256r1SignatureOffsets::default()
457            };
458            assert_eq!(
459                test_case(1, &offsets),
460                Err(PrecompileError::InvalidDataOffsets)
461            );
462
463            let offsets = Secp256r1SignatureOffsets {
464                message_data_offset: u16::MAX,
465                message_data_size: u16::MAX,
466                ..Secp256r1SignatureOffsets::default()
467            };
468            assert_eq!(
469                test_case(1, &offsets),
470                Err(PrecompileError::InvalidDataOffsets)
471            );
472        }
473
474        #[test]
475        fn test_pubkey_offset() {
476            let offsets = Secp256r1SignatureOffsets {
477                public_key_offset: u16::MAX,
478                ..Secp256r1SignatureOffsets::default()
479            };
480            assert_eq!(
481                test_case(1, &offsets),
482                Err(PrecompileError::InvalidDataOffsets)
483            );
484
485            let offsets = Secp256r1SignatureOffsets {
486                public_key_offset: 100 - (COMPRESSED_PUBKEY_SERIALIZED_SIZE as u16) + 1,
487                ..Secp256r1SignatureOffsets::default()
488            };
489            assert_eq!(
490                test_case(1, &offsets),
491                Err(PrecompileError::InvalidDataOffsets)
492            );
493        }
494
495        #[test]
496        fn test_signature_offset() {
497            let offsets = Secp256r1SignatureOffsets {
498                signature_offset: u16::MAX,
499                ..Secp256r1SignatureOffsets::default()
500            };
501            assert_eq!(
502                test_case(1, &offsets),
503                Err(PrecompileError::InvalidDataOffsets)
504            );
505
506            let offsets = Secp256r1SignatureOffsets {
507                signature_offset: 100 - (SIGNATURE_SERIALIZED_SIZE as u16) + 1,
508                ..Secp256r1SignatureOffsets::default()
509            };
510            assert_eq!(
511                test_case(1, &offsets),
512                Err(PrecompileError::InvalidDataOffsets)
513            );
514        }
515
516        #[test]
517        fn test_secp256r1() {
518            rialo_s_logger::setup();
519            let message_arr = b"hello";
520            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
521            let signing_key = EcKey::generate(&group).unwrap();
522            let mut instruction = new_secp256r1_instruction(message_arr, signing_key).unwrap();
523            let mint_keypair = Keypair::new();
524            let feature_set = FeatureSet::all_enabled();
525            let valid_from = 0;
526
527            let tx = Transaction::new_signed_with_payer(
528                &[instruction.clone()],
529                Some(&mint_keypair.pubkey()),
530                &[&mint_keypair],
531                valid_from,
532            );
533
534            assert!(tx.verify_precompiles(&feature_set).is_ok());
535
536            // The message is the last field in the instruction data so
537            // changing its last byte will also change the signature validity
538            let message_byte_index = instruction.data.len() - 1;
539            instruction.data[message_byte_index] =
540                instruction.data[message_byte_index].wrapping_add(12);
541            let tx = Transaction::new_signed_with_payer(
542                &[instruction.clone()],
543                Some(&mint_keypair.pubkey()),
544                &[&mint_keypair],
545                valid_from,
546            );
547
548            assert!(tx.verify_precompiles(&feature_set).is_err());
549        }
550
551        #[test]
552        fn test_secp256r1_high_s() {
553            rialo_s_logger::setup();
554            let message_arr = b"hello";
555            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
556            let signing_key = EcKey::generate(&group).unwrap();
557            let mut instruction = new_secp256r1_instruction(message_arr, signing_key).unwrap();
558
559            // To double check that the untampered low-S value signature passes
560            let feature_set = FeatureSet::all_enabled();
561            let tx_pass = verify(
562                instruction.data.as_slice(),
563                &[instruction.data.as_slice()],
564                &feature_set,
565            );
566            assert!(tx_pass.is_ok());
567
568            // Determine offsets at which to perform the S-value manipulation
569            let public_key_offset = DATA_START;
570            let signature_offset = public_key_offset + COMPRESSED_PUBKEY_SERIALIZED_SIZE;
571            let s_offset = signature_offset + FIELD_SIZE;
572
573            // Create a high S value by doing order - s
574            let order = BigNum::from_slice(&SECP256R1_ORDER).unwrap();
575            let current_s =
576                BigNum::from_slice(&instruction.data[s_offset..s_offset + FIELD_SIZE]).unwrap();
577            let mut high_s = BigNum::new().unwrap();
578            high_s.checked_sub(&order, &current_s).unwrap();
579
580            // Replace the S value in the signature with our high S
581            instruction.data[s_offset..s_offset + FIELD_SIZE].copy_from_slice(&high_s.to_vec());
582
583            // Since Transaction::verify_precompiles only returns a vague
584            // `InvalidAccountIndex` error on precompile failure, we use verify()
585            // here directly to check for the specific
586            // InvalidSignatureValueRange error
587            let tx_fail = verify(
588                instruction.data.as_slice(),
589                &[instruction.data.as_slice()],
590                &feature_set,
591            );
592            assert!(tx_fail.unwrap_err() == PrecompileError::InvalidSignature);
593        }
594        #[test]
595        fn test_new_secp256r1_instruction_31byte_components() {
596            rialo_s_logger::setup();
597            let message_arr = b"hello";
598            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
599            let signing_key = EcKey::generate(&group).unwrap();
600
601            // Keep generating signatures until we get one with a 31-byte component
602            loop {
603                let instruction =
604                    new_secp256r1_instruction(message_arr, signing_key.clone()).unwrap();
605
606                // Extract r and s from the signature
607                let signature_offset = DATA_START + COMPRESSED_PUBKEY_SERIALIZED_SIZE;
608                let r = &instruction.data[signature_offset..signature_offset + FIELD_SIZE];
609                let s = &instruction.data
610                    [signature_offset + FIELD_SIZE..signature_offset + 2 * FIELD_SIZE];
611
612                // Convert to BigNum and back to get byte representation
613                let r_bn = BigNum::from_slice(r).unwrap();
614                let s_bn = BigNum::from_slice(s).unwrap();
615                let r_bytes = r_bn.to_vec();
616                let s_bytes = s_bn.to_vec();
617
618                if r_bytes.len() == 31 || s_bytes.len() == 31 {
619                    // Once found, verify the signature and break out of the loop
620                    let mint_keypair = Keypair::new();
621                    let valid_from = 0;
622                    let tx = Transaction::new_signed_with_payer(
623                        &[instruction],
624                        Some(&mint_keypair.pubkey()),
625                        &[&mint_keypair],
626                        valid_from,
627                    );
628
629                    let feature_set = FeatureSet::all_enabled();
630                    assert!(tx.verify_precompiles(&feature_set).is_ok());
631                    break;
632                }
633            }
634        }
635
636        #[test]
637        fn test_new_secp256r1_instruction_signing_key() {
638            rialo_s_logger::setup();
639            let message_arr = b"hello";
640            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
641            let signing_key = EcKey::generate(&group).unwrap();
642            assert!(new_secp256r1_instruction(message_arr, signing_key).is_ok());
643
644            let incorrect_group = EcGroup::from_curve_name(Nid::X9_62_PRIME192V1).unwrap();
645            let incorrect_key = EcKey::generate(&incorrect_group).unwrap();
646            assert!(new_secp256r1_instruction(message_arr, incorrect_key).is_err());
647        }
648        #[test]
649        fn test_secp256r1_order() {
650            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
651            let mut ctx = BigNumContext::new().unwrap();
652            let mut openssl_order = BigNum::new().unwrap();
653            group.order(&mut openssl_order, &mut ctx).unwrap();
654
655            let our_order = BigNum::from_slice(&SECP256R1_ORDER).unwrap();
656            assert_eq!(our_order, openssl_order);
657        }
658
659        #[test]
660        fn test_secp256r1_order_minus_one() {
661            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
662            let mut ctx = BigNumContext::new().unwrap();
663            let mut openssl_order = BigNum::new().unwrap();
664            group.order(&mut openssl_order, &mut ctx).unwrap();
665
666            let mut expected_order_minus_one = BigNum::new().unwrap();
667            expected_order_minus_one
668                .checked_sub(&openssl_order, &BigNum::from_u32(1).unwrap())
669                .unwrap();
670
671            let our_order_minus_one = BigNum::from_slice(&SECP256R1_ORDER_MINUS_ONE).unwrap();
672            assert_eq!(our_order_minus_one, expected_order_minus_one);
673        }
674
675        #[test]
676        fn test_secp256r1_half_order() {
677            // Get the secp256r1 curve group
678            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
679
680            // Get the order from OpenSSL
681            let mut ctx = BigNumContext::new().unwrap();
682            let mut openssl_order = BigNum::new().unwrap();
683            group.order(&mut openssl_order, &mut ctx).unwrap();
684
685            // Calculate half order
686            let mut calculated_half_order = BigNum::new().unwrap();
687            let two = BigNum::from_u32(2).unwrap();
688            calculated_half_order
689                .checked_div(&openssl_order, &two, &mut ctx)
690                .unwrap();
691
692            // Get our constant half order
693            let our_half_order = BigNum::from_slice(&SECP256R1_HALF_ORDER).unwrap();
694
695            // Compare the calculated half order with our constant
696            assert_eq!(calculated_half_order, our_half_order);
697        }
698
699        #[test]
700        fn test_secp256r1_order_relationships() {
701            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
702            let mut ctx = BigNumContext::new().unwrap();
703            let mut openssl_order = BigNum::new().unwrap();
704            group.order(&mut openssl_order, &mut ctx).unwrap();
705
706            let our_order = BigNum::from_slice(&SECP256R1_ORDER).unwrap();
707            let our_order_minus_one = BigNum::from_slice(&SECP256R1_ORDER_MINUS_ONE).unwrap();
708            let our_half_order = BigNum::from_slice(&SECP256R1_HALF_ORDER).unwrap();
709
710            // Verify our order matches OpenSSL's order
711            assert_eq!(our_order, openssl_order);
712
713            // Verify order - 1
714            let mut expected_order_minus_one = BigNum::new().unwrap();
715            expected_order_minus_one
716                .checked_sub(&openssl_order, &BigNum::from_u32(1).unwrap())
717                .unwrap();
718            assert_eq!(our_order_minus_one, expected_order_minus_one);
719
720            // Verify half order
721            let mut expected_half_order = BigNum::new().unwrap();
722            expected_half_order
723                .checked_div(&openssl_order, &BigNum::from_u32(2).unwrap(), &mut ctx)
724                .unwrap();
725            assert_eq!(our_half_order, expected_half_order);
726
727            // Verify half order * 2 = order - 1
728            let mut double_half_order = BigNum::new().unwrap();
729            double_half_order
730                .checked_mul(&our_half_order, &BigNum::from_u32(2).unwrap(), &mut ctx)
731                .unwrap();
732            assert_eq!(double_half_order, expected_order_minus_one);
733        }
734    }
735}
736
737#[cfg(any(target_arch = "wasm32", target_os = "solana"))]
738mod target_arch {
739    use rialo_s_feature_set::FeatureSet;
740    use rialo_s_precompile_error::PrecompileError;
741
742    pub fn verify(
743        _data: &[u8],
744        _instruction_datas: &[&[u8]],
745        _feature_set: &FeatureSet,
746    ) -> Result<(), PrecompileError> {
747        Err(PrecompileError::InvalidSignature)
748    }
749}
750
751pub use self::target_arch::*;