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