@@ -184,6 +184,20 @@ impl<C: Signing> Secp256k1<C> {
184
184
}
185
185
}
186
186
187
+ /// Takes a 64 byte array and reverses the endian-ness of each 32 byte segment.
188
+ /// Useful for flipping the byte endian-ness of the signature and public key components.
189
+ fn flip_secp256k1_endianness ( input : & [ u8 ; 64 ] ) -> [ u8 ; 64 ] {
190
+ let mut output = [ 0u8 ; 64 ] ;
191
+ for i in 0 ..2 {
192
+ let start = i * 32 ;
193
+ let end = start + 32 ;
194
+ let mut segment: [ u8 ; 32 ] = input[ start..end] . try_into ( ) . unwrap ( ) ;
195
+ segment. reverse ( ) ;
196
+ output[ start..end] . copy_from_slice ( & segment) ;
197
+ }
198
+ output
199
+ }
200
+
187
201
impl < C : Verification > Secp256k1 < C > {
188
202
/// Determines the public key for which `sig` is a valid signature for
189
203
/// `msg`. Requires a verify-capable context.
@@ -192,6 +206,41 @@ impl<C: Verification> Secp256k1<C> {
192
206
msg : & Message ,
193
207
sig : & RecoverableSignature ,
194
208
) -> Result < key:: PublicKey , Error > {
209
+ cfg_if:: cfg_if! {
210
+ if #[ cfg( all( target_os = "zkvm" , target_vendor = "succinct" ) ) ] {
211
+ // `msg.0` contains the message data as a byte array.
212
+ let prehash: & [ u8 ] = & msg. 0 ;
213
+
214
+ // `sig` is a 65-byte array containing r (32 bytes), s (32 bytes), and the recovery ID (1 byte). We need to handle the
215
+ // signature bytes according to their endianness. The signature is in little-endian format, but needs to be processed
216
+ // in big-endian format for masking by the ecdsa-core library.
217
+
218
+ // Reverse the first 32 bytes (r) and the second 32 bytes (s) of the signature
219
+ // and concatenate them to get the signature in big-endian format.
220
+ let mut sig_be_bytes = flip_secp256k1_endianness( & sig. 0 [ ..64 ] . try_into( ) . unwrap( ) ) ;
221
+
222
+ let signature = sp1_ecdsa:: Signature :: <k256:: Secp256k1 >:: from_slice( & sig_be_bytes) . unwrap( ) ;
223
+
224
+ // The recovery ID is the last byte of the signature.
225
+ let recovery_id = sp1_ecdsa:: RecoveryId :: from_byte( sig. 0 [ 64 ] ) . unwrap( ) ;
226
+
227
+ let verifying_key = sp1_ecdsa:: VerifyingKey :: recover_from_prehash_secp256k1( prehash, & signature, recovery_id) . unwrap( ) ;
228
+ let verifying_key_bytes = {
229
+ // Convert the verifying key to a byte array. The encoded point returned by `to_encoded_point` is in uncompressed format,
230
+ // with the prefix byte (0x04) and two 32-byte coordinates in big-endian format. This needs to be flipped to little-endian
231
+ // for the from_array_unchecked constructor.
232
+ let bytes = verifying_key. to_encoded_point( false ) . to_bytes( ) ;
233
+ flip_secp256k1_endianness( & bytes[ 1 ..65 ] . try_into( ) . unwrap( ) )
234
+ } ;
235
+
236
+ // In recover_from_prehash_secp256k1, the public key is verified to be valid, so we can use the unchecked constructor.
237
+ unsafe {
238
+ let k = key:: PublicKey ( crate :: ffi:: PublicKey :: from_array_unchecked( verifying_key_bytes) ) ;
239
+ return Ok ( k) ;
240
+ }
241
+ }
242
+ }
243
+
195
244
unsafe {
196
245
let mut pk = super_ffi:: PublicKey :: new ( ) ;
197
246
if ffi:: secp256k1_ecdsa_recover (
0 commit comments