solana_secp256r1_program/
lib.rs

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