Repository: OpenZeppelin/cairo-contracts (language: python)

Issue:
Add support for secp256r1
**🧐 Motivation**

`OpenZeppelin/cairo-contracts` includes a number of utils to support secp256k1:

- types for publickey and signature
- traits for serialization, debug, eq
- a store to save the secp256k1 public keys in a contract in a packed format
- is_valid_eth_signature to check a secp256k1 signature is valid
- a collection of Cairo tests and a data set to validate the implementation of the above

These tools are used internally in `OpenZeppelin/cairo-contracts` to implement the Eth Account. They are also available for third party projects that uses your project as a library. We use them in our [starknet-modular-account](https://github.com/0xknwn/starknet-modular-account) project.

We have implemented the same set of tools for secp256r1 that is also supported by the starknet corelib. We would like to submit a PR that them so that we can rely on them the same way we rely on your secp256k1 implementation.

**📝 Details**

See PR #988 for details about the implementation. We've done the adaptation from secp256k1 to secp256r1 but, except for the mapping between the functions and features the implementation is the same.



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24acb42e1..018474dfb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+- `is_valid_p256_signature` utility function to `openzeppelin_account::utils::signature` (#1189)
+- `Secp256r1KeyPair` type and helpers to `openzeppelin_testing::signing` (#1189)
 - Embeddable impls for ERC2981 component (#1173)
   - `ERC2981Info` with read functions for discovering the component's state
   - `ERC2981AdminOwnable` providing admin functions for a token that implements Ownable component
@@ -18,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Changed (Breaking)
 
+- Refactor `openzeppelin_account::utils::secp256k1` module to `openzeppelin_account::utils::secp256_point` (#1189)
+  - `Secp256k1PointStorePacking` replaced by a generic `Secp256PointStorePacking`
+  - `Secp256k1PointPartialEq` replaced by a generic `Secp256PointPartialEq`
+  - `DebugSecp256k1Point` replaced by a generic `DebugSecp256Point`
 - Apply underscore pattern to the internal functions of `ERC2981Component` to prevent collisions
 with new external functions (#1173)
 
diff --git a/packages/account/src/eth_account.cairo b/packages/account/src/eth_account.cairo
index dad381abd..afc8e046d 100644
--- a/packages/account/src/eth_account.cairo
+++ b/packages/account/src/eth_account.cairo
@@ -12,7 +12,7 @@ pub mod EthAccountComponent {
     use core::starknet::secp256_trait::Secp256PointTrait;
     use crate::interface::EthPublicKey;
     use crate::interface;
-    use crate::utils::secp256k1::Secp256k1PointStorePacking;
+    use crate::utils::secp256_point::Secp256PointStorePacking;
     use crate::utils::{MIN_TRANSACTION_VERSION, QUERY_OFFSET};
     use crate::utils::{execute_calls, is_valid_eth_signature};
     use openzeppelin_introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait;
diff --git a/packages/account/src/interface.cairo b/packages/account/src/interface.cairo
index f1a284b21..9a32f50b4 100644
--- a/packages/account/src/interface.cairo
+++ b/packages/account/src/interface.cairo
@@ -4,6 +4,7 @@
 use starknet::account::Call;
 
 pub type EthPublicKey = starknet::secp256k1::Secp256k1Point;
+pub type P256PublicKey = starknet::secp256r1::Secp256r1Point;
 
 pub const ISRC6_ID: felt252 = 0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd;
 
diff --git a/packages/account/src/utils.cairo b/packages/account/src/utils.cairo
index cf1630fd7..efcde050c 100644
--- a/packages/account/src/utils.cairo
+++ b/packages/account/src/utils.cairo
@@ -1,10 +1,10 @@
 // SPDX-License-Identifier: MIT
 // OpenZeppelin Contracts for Cairo v0.18.0 (account/utils.cairo)
 
-pub mod secp256k1;
+pub mod secp256_point;
 pub mod signature;
 
-pub use signature::{is_valid_stark_signature, is_valid_eth_signature};
+pub use signature::{is_valid_stark_signature, is_valid_eth_signature, is_valid_p256_signature};
 
 use starknet::SyscallResultTrait;
 use starknet::account::Call;
diff --git a/packages/account/src/utils/secp256k1.cairo b/packages/account/src/utils/secp256_point.cairo
similarity index 51%
rename from packages/account/src/utils/secp256k1.cairo
rename to packages/account/src/utils/secp256_point.cairo
index 0722584a6..c9cc00294 100644
--- a/packages/account/src/utils/secp256k1.cairo
+++ b/packages/account/src/utils/secp256_point.cairo
@@ -1,57 +1,64 @@
 // SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts for Cairo v0.18.0 (account/utils/secp256k1.cairo)
+// OpenZeppelin Contracts for Cairo v0.18.0 (account/utils/secp256_point.cairo)
 
 use core::fmt::{Formatter, Error};
 use starknet::SyscallResultTrait;
 use starknet::secp256_trait::{Secp256Trait, Secp256PointTrait};
-use starknet::secp256k1::Secp256k1Point;
 use starknet::storage_access::StorePacking;
 
-/// Packs a Secp256k1Point into a (felt252, felt252).
+/// Packs a Secp256Point into a (felt252, felt252).
 ///
 /// The packing is done as follows:
+///
 /// - First felt contains x.low (x being the x-coordinate of the point).
 /// - Second felt contains x.high and the parity bit, at the least significant bits (2 * x.high +
 /// parity).
-pub impl Secp256k1PointStorePacking of StorePacking<Secp256k1Point, (felt252, felt252)> {
-    fn pack(value: Secp256k1Point) -> (felt252, felt252) {
+pub impl Secp256PointStorePacking<
+    Secp256Point, +Secp256Trait<Secp256Point>, +Secp256PointTrait<Secp256Point>
+> of StorePacking<Secp256Point, (felt252, felt252)> {
+    fn pack(value: Secp256Point) -> (felt252, felt252) {
         let (x, y) = value.get_coordinates().unwrap_syscall();
 
-        let parity = y % 2;
-        let xhigh_and_parity = 2 * x.high.into() + parity.try_into().unwrap();
+        let parity = y.low % 2;
+        let xhigh_and_parity = 2 * x.high.into() + parity.into();
 
         (x.low.into(), xhigh_and_parity)
     }
 
-    fn unpack(value: (felt252, felt252)) -> Secp256k1Point {
+    fn unpack(value: (felt252, felt252)) -> Secp256Point {
         let (xlow, xhigh_and_parity) = value;
         let xhigh_and_parity: u256 = xhigh_and_parity.into();
 
-        let x = u256 {
-            low: xlow.try_into().unwrap(), high: (xhigh_and_parity / 2).try_into().unwrap(),
-        };
+        let low = xlow.try_into().unwrap();
+        let high = (xhigh_and_parity / 2).try_into().unwrap();
+
+        let x = u256 { low, high };
         let parity = xhigh_and_parity % 2 == 1;
 
         // Expects parity odd to be true
         Secp256Trait::secp256_ec_get_point_from_x_syscall(x, parity)
             .unwrap_syscall()
-            .expect('Secp256k1Point: Invalid point.')
+            .expect('Secp256Point: Invalid point.')
     }
 }
 
-pub impl Secp256k1PointPartialEq of PartialEq<Secp256k1Point> {
+pub impl Secp256PointPartialEq<
+    Secp256Point, +Secp256PointTrait<Secp256Point>, +Copy<Secp256Point>
+> of PartialEq<Secp256Point> {
     #[inline(always)]
-    fn eq(lhs: @Secp256k1Point, rhs: @Secp256k1Point) -> bool {
+    fn eq(lhs: @Secp256Point, rhs: @Secp256Point) -> bool {
         (*lhs).get_coordinates().unwrap_syscall() == (*rhs).get_coordinates().unwrap_syscall()
     }
     #[inline(always)]
-    fn ne(lhs: @Secp256k1Point, rhs: @Secp256k1Point) -> bool {
+    fn ne(lhs: @Secp256Point, rhs: @Secp256Point) -> bool {
         !(lhs == rhs)
     }
 }
 
-pub impl DebugSecp256k1Point of core::fmt::Debug<Secp256k1Point> {
-    fn fmt(self: @Secp256k1Point, ref f: Formatter) -> Result<(), Error> {
+pub impl DebugSecp256Point<
+    Secp256Point, +Secp256PointTrait<Secp256Point>, +Copy<Secp256Point>
+> of core::fmt::Debug<Secp256Point> {
+    fn fmt(self: @Secp256Point, ref f: Formatter) -> Result<(), Error> {
         let (x, y) = (*self).get_coordinates().unwrap_syscall();
         write!(f, "({x:?},{y:?})")
     }
diff --git a/packages/account/src/utils/signature.cairo b/packages/account/src/utils/signature.cairo
index 33e8c4246..d4465dba2 100644
--- a/packages/account/src/utils/signature.cairo
+++ b/packages/account/src/utils/signature.cairo
@@ -2,11 +2,13 @@
 // OpenZeppelin Contracts for Cairo v0.18.0 (account/utils/signature.cairo)
 
 use core::ecdsa::check_ecdsa_signature;
-use crate::interface::EthPublicKey;
+use crate::interface::{EthPublicKey, P256PublicKey};
+use starknet::secp256_trait::{Secp256Trait, Secp256PointTrait};
 use starknet::secp256_trait;
 
+/// A signature format compatible with the family of secp256 curves.
 #[derive(Copy, Drop, Serde)]
-pub struct EthSignature {
+pub struct Secp256Signature {
     pub r: u256,
     pub s: u256,
 }
@@ -35,9 +37,29 @@ pub fn is_valid_stark_signature(
 /// See https://github.com/OpenZeppelin/cairo-contracts/issues/889.
 pub fn is_valid_eth_signature(
     msg_hash: felt252, public_key: EthPublicKey, signature: Span<felt252>
+) -> bool {
+    is_valid_secp256_signature(msg_hash, public_key, signature)
+}
+
+/// This function assumes the `s` component of the signature to be positive
+/// for efficiency reasons. It is not recommended to use it other than for
+/// validating account signatures over transaction hashes since otherwise
+/// it's not protected against signature malleability.
+/// See https://github.com/OpenZeppelin/cairo-contracts/issues/889.
+pub fn is_valid_p256_signature(
+    msg_hash: felt252, public_key: P256PublicKey, signature: Span<felt252>
+) -> bool {
+    is_valid_secp256_signature(msg_hash, public_key, signature)
+}
+
+/// Verifies a Secp256 compatible signature for a valid point in the corresponding curve.
+fn is_valid_secp256_signature<
+    Secp256Point, +Secp256Trait<Secp256Point>, +Secp256PointTrait<Secp256Point>, +Drop<Secp256Point>
+>(
+    msg_hash: felt252, public_key: Secp256Point, signature: Span<felt252>
 ) -> bool {
     let mut signature = signature;
-    let signature: EthSignature = Serde::deserialize(ref signature)
+    let signature: Secp256Signature = Serde::deserialize(ref signature)
         .expect('Signature: Invalid format.');
 
     secp256_trait::is_valid_signature(msg_hash.into(), signature.r, signature.s, public_key)


Test patch:
diff --git a/docs/modules/ROOT/pages/api/testing.adoc b/docs/modules/ROOT/pages/api/testing.adoc
index eae3cfbce..d46310582 100644
--- a/docs/modules/ROOT/pages/api/testing.adoc
+++ b/docs/modules/ROOT/pages/api/testing.adoc
@@ -2,6 +2,7 @@
 
 :stark: https://docs.starknet.io/architecture-and-concepts/cryptography/stark-curve/[Stark]
 :secp256k1: https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/secp256k1.cairo[Secp256k1]
+:secp256r1: https://www.nervos.org/knowledge-base/what_is_secp256r1[Secp256r1]
 
 This crate provides various helper functions for declaring, deploying,
 and testing smart contracts using the `snforge` toolchain from Starknet Foundry.
@@ -267,6 +268,7 @@ A module offering utility functions for easier management of key pairs and signa
 .Functions
 * xref:#testing-signing-get_stark_keys_from[`++get_stark_keys_from(private_key)++`]
 * xref:#testing-signing-get_secp256k1_keys_from[`++get_secp256k1_keys_from(private_key)++`]
+* xref:#testing-signing-get_secp256r1_keys_from[`++get_secp256r1_keys_from(private_key)++`]
 
 .Traits
 * xref:#testing-signing-SerializedSigning[`++SerializedSigning++`]
@@ -287,6 +289,12 @@ Builds a {stark} key pair from a private key represented by a `felt252` value.
 
 Builds a {secp256k1} key pair from a private key represented by a `u256` value.
 
+[.contract-item]
+[[testing-signing-get_secp256r1_keys_from]]
+==== `[.contract-item-name]#++get_secp256r1_keys_from++#++(private_key: u256) → Secp256r1KeyPair++` [.item-kind]#function#
+
+Builds a {secp256r1} key pair from a private key represented by a `u256` value.
+
 [#testing-signing-Traits]
 ==== Traits
 
diff --git a/packages/account/src/tests.cairo b/packages/account/src/tests.cairo
index 0780cf6a0..1d84fc250 100644
--- a/packages/account/src/tests.cairo
+++ b/packages/account/src/tests.cairo
@@ -2,5 +2,5 @@ mod extensions;
 
 mod test_account;
 mod test_eth_account;
-mod test_secp256k1;
+mod test_secp256_point;
 mod test_signature;
diff --git a/packages/account/src/tests/test_eth_account.cairo b/packages/account/src/tests/test_eth_account.cairo
index cfa32e99e..7c844a075 100644
--- a/packages/account/src/tests/test_eth_account.cairo
+++ b/packages/account/src/tests/test_eth_account.cairo
@@ -3,8 +3,8 @@ use crate::EthAccountComponent::{PublicKeyCamelImpl, PublicKeyImpl};
 use crate::EthAccountComponent;
 use crate::interface::{EthAccountABIDispatcherTrait, EthAccountABIDispatcher};
 use crate::interface::{ISRC6, ISRC6_ID};
-use crate::utils::secp256k1::{DebugSecp256k1Point, Secp256k1PointPartialEq};
-use crate::utils::signature::EthSignature;
+use crate::utils::secp256_point::{DebugSecp256Point, Secp256PointPartialEq};
+use crate::utils::signature::Secp256Signature;
 use openzeppelin_introspection::interface::{ISRC5, ISRC5_ID};
 use openzeppelin_test_common::eth_account::EthAccountSpyHelpers;
 use openzeppelin_test_common::eth_account::{
@@ -364,14 +364,14 @@ fn test_account_called_from_contract() {
 //
 
 #[test]
-#[should_panic(expected: ('Secp256k1Point: Invalid point.',))]
+#[should_panic(expected: 'Secp256Point: Invalid point.')]
 fn test_cannot_get_without_initialize() {
     let state = COMPONENT_STATE();
     state.get_public_key();
 }
 
 #[test]
-#[should_panic(expected: ('Secp256k1Point: Invalid point.',))]
+#[should_panic(expected: 'Secp256Point: Invalid point.')]
 fn test_cannot_set_without_initialize() {
     let key_pair = KEY_PAIR();
     let mut state = COMPONENT_STATE();
@@ -529,7 +529,7 @@ fn test_assert_valid_new_owner_invalid_signature() {
 
     start_cheat_caller_address(test_address(), test_address());
     let mut bad_signature = array![];
-    EthSignature { r: 'BAD'.into(), s: 'SIG'.into() }.serialize(ref bad_signature);
+    Secp256Signature { r: 'BAD'.into(), s: 'SIG'.into() }.serialize(ref bad_signature);
     let new_key_pair = KEY_PAIR_2();
 
     state
diff --git a/packages/account/src/tests/test_secp256k1.cairo b/packages/account/src/tests/test_secp256_point.cairo
similarity index 85%
rename from packages/account/src/tests/test_secp256k1.cairo
rename to packages/account/src/tests/test_secp256_point.cairo
index d2cc160a3..20ad7a217 100644
--- a/packages/account/src/tests/test_secp256k1.cairo
+++ b/packages/account/src/tests/test_secp256_point.cairo
@@ -1,5 +1,5 @@
-use crate::utils::secp256k1::{
-    DebugSecp256k1Point, Secp256k1PointPartialEq, Secp256k1PointStorePacking as StorePacking
+use crate::utils::secp256_point::{
+    DebugSecp256Point, Secp256PointPartialEq, Secp256PointStorePacking as StorePacking
 };
 use starknet::SyscallResultTrait;
 use starknet::secp256_trait::{Secp256Trait, Secp256PointTrait};
@@ -11,7 +11,6 @@ fn test_pack_big_secp256k1_points() {
     let curve_size = Secp256Trait::<Secp256k1Point>::get_curve_size();
 
     // Check point 1
-
     let (xlow, xhigh_and_parity) = StorePacking::pack(big_point_1);
     let xhigh_and_parity: u256 = xhigh_and_parity.into();
 
@@ -24,7 +23,6 @@ fn test_pack_big_secp256k1_points() {
     assert_eq!(parity, true, "Parity should be odd");
 
     // Check point 2
-
     let (xlow, xhigh_and_parity) = StorePacking::pack(big_point_2);
     let xhigh_and_parity: u256 = xhigh_and_parity.into();
 
@@ -42,21 +40,21 @@ fn test_unpack_big_secp256k1_points() {
     let (big_point_1, big_point_2) = get_points();
 
     // Check point 1
-
     let (expected_x, expected_y) = big_point_1.get_coordinates().unwrap_syscall();
 
     let (xlow, xhigh_and_parity) = StorePacking::pack(big_point_1);
-    let (x, y) = StorePacking::unpack((xlow, xhigh_and_parity)).get_coordinates().unwrap_syscall();
+    let point: Secp256k1Point = StorePacking::unpack((xlow, xhigh_and_parity));
+    let (x, y) = point.get_coordinates().unwrap_syscall();
 
     assert_eq!(x, expected_x);
     assert_eq!(y, expected_y);
 
     // Check point 2
-
     let (expected_x, _) = big_point_2.get_coordinates().unwrap_syscall();
 
     let (xlow, xhigh_and_parity) = StorePacking::pack(big_point_2);
-    let (x, _) = StorePacking::unpack((xlow, xhigh_and_parity)).get_coordinates().unwrap_syscall();
+    let point: Secp256k1Point = StorePacking::unpack((xlow, xhigh_and_parity));
+    let (x, _) = point.get_coordinates().unwrap_syscall();
 
     assert_eq!(x, expected_x);
     assert_eq!(y, expected_y);
diff --git a/packages/account/src/tests/test_signature.cairo b/packages/account/src/tests/test_signature.cairo
index 7b2bfffa0..f7841b2b3 100644
--- a/packages/account/src/tests/test_signature.cairo
+++ b/packages/account/src/tests/test_signature.cairo
@@ -1,9 +1,16 @@
-use crate::utils::signature::{is_valid_stark_signature, is_valid_eth_signature};
+use crate::interface::P256PublicKey;
+use crate::utils::signature::{
+    is_valid_stark_signature, is_valid_eth_signature, is_valid_p256_signature
+};
+use openzeppelin_account::utils::signature::Secp256Signature;
 use openzeppelin_test_common::account::SIGNED_TX_DATA as stark_signature_data;
 use openzeppelin_test_common::eth_account::SIGNED_TX_DATA as eth_signature_data;
-use openzeppelin_testing::constants::{stark, secp256k1};
+use openzeppelin_testing::constants::{TRANSACTION_HASH, stark, secp256k1, secp256r1};
+use openzeppelin_testing::signing::{Secp256r1KeyPair, Secp256r1SerializedSigning};
+use snforge_std::signature::secp256r1_curve::Secp256r1CurveSignerImpl;
 use starknet::secp256_trait::Secp256Trait;
 use starknet::secp256k1::Secp256k1Point;
+use starknet::secp256r1::Secp256r1Point;
 
 //
 // is_valid_stark_signature
@@ -86,7 +93,7 @@ fn test_is_valid_eth_signature_invalid_format_sig() {
 }
 
 #[test]
-fn test_signature_r_out_of_range() {
+fn test_eth_signature_r_out_of_range() {
     let key_pair = secp256k1::KEY_PAIR();
     let data = eth_signature_data(key_pair);
     let mut bad_signature = data.signature;
@@ -106,7 +113,7 @@ fn test_signature_r_out_of_range() {
 }
 
 #[test]
-fn test_signature_s_out_of_range() {
+fn test_eth_signature_s_out_of_range() {
     let key_pair = secp256k1::KEY_PAIR();
     let data = eth_signature_data(key_pair);
     let mut bad_signature = data.signature;
@@ -124,3 +131,108 @@ fn test_signature_s_out_of_range() {
     );
     assert!(is_invalid);
 }
+
+//
+// is_valid_p256_signature
+//
+
+#[derive(Drop)]
+pub struct SignedTransactionData {
+    pub private_key: u256,
+    pub public_key: P256PublicKey,
+    pub tx_hash: felt252,
+    pub signature: Secp256Signature
+}
+
+fn p256_signature_data(key_pair: Secp256r1KeyPair) -> SignedTransactionData {
+    let tx_hash = TRANSACTION_HASH;
+    let (r, s) = key_pair.sign(tx_hash.into()).unwrap();
+    SignedTransactionData {
+        private_key: key_pair.secret_key,
+        public_key: key_pair.public_key,
+        tx_hash,
+        signature: Secp256Signature { r, s }
+    }
+}
+
+#[test]
+fn test_is_valid_p256_signature_good_sig() {
+    let key_pair = secp256r1::KEY_PAIR();
+    let data = p256_signature_data(key_pair);
+
+    let mut serialized_good_signature = array![];
+    data.signature.serialize(ref serialized_good_signature);
+
+    let is_valid = is_valid_p256_signature(
+        data.tx_hash, key_pair.public_key, serialized_good_signature.span()
+    );
+    assert!(is_valid);
+}
+
+#[test]
+fn test_is_valid_p256_signature_bad_sig() {
+    let key_pair = secp256r1::KEY_PAIR();
+    let data = p256_signature_data(key_pair);
+    let mut bad_signature = data.signature;
+
+    bad_signature.r += 1;
+
+    let mut serialized_bad_signature = array![];
+
+    bad_signature.serialize(ref serialized_bad_signature);
+
+    let is_invalid = !is_valid_p256_signature(
+        data.tx_hash, key_pair.public_key, serialized_bad_signature.span()
+    );
+    assert!(is_invalid);
+}
+
+#[test]
+#[should_panic(expected: 'Signature: Invalid format.')]
+fn test_is_valid_p256_signature_invalid_format_sig() {
+    let key_pair = secp256r1::KEY_PAIR();
+    let data = p256_signature_data(key_pair);
+    let mut serialized_bad_signature = array![0x1];
+
+    is_valid_p256_signature(data.tx_hash, key_pair.public_key, serialized_bad_signature.span());
+}
+
+#[test]
+fn test_p256_signature_r_out_of_range() {
+    let key_pair = secp256r1::KEY_PAIR();
+    let data = p256_signature_data(key_pair);
+    let mut bad_signature = data.signature;
+
+    let curve_size = Secp256Trait::<Secp256r1Point>::get_curve_size();
+
+    bad_signature.r = curve_size + 1;
+
+    let mut serialized_bad_signature = array![];
+
+    bad_signature.serialize(ref serialized_bad_signature);
+
+    let is_invalid = !is_valid_p256_signature(
+        data.tx_hash, key_pair.public_key, serialized_bad_signature.span()
+    );
+    assert!(is_invalid);
+}
+
+#[test]
+fn test_p256_signature_s_out_of_range() {
+    let key_pair = secp256r1::KEY_PAIR();
+    let data = p256_signature_data(key_pair);
+    let mut bad_signature = data.signature;
+
+    let curve_size = Secp256Trait::<Secp256r1Point>::get_curve_size();
+
+    bad_signature.s = curve_size + 1;
+
+    let mut serialized_bad_signature = array![];
+
+    bad_signature.serialize(ref serialized_bad_signature);
+
+    let is_invalid = !is_valid_p256_signature(
+        data.tx_hash, key_pair.public_key, serialized_bad_signature.span()
+    );
+    assert!(is_invalid);
+}
diff --git a/packages/presets/src/tests/test_eth_account.cairo b/packages/presets/src/tests/test_eth_account.cairo
index a46d39c64..7b457edfd 100644
--- a/packages/presets/src/tests/test_eth_account.cairo
+++ b/packages/presets/src/tests/test_eth_account.cairo
@@ -7,7 +7,7 @@ use crate::interfaces::{
     EthAccountUpgradeableABIDispatcher, EthAccountUpgradeableABIDispatcherTrait
 };
 use openzeppelin_account::interface::ISRC6_ID;
-use openzeppelin_account::utils::secp256k1::{DebugSecp256k1Point, Secp256k1PointPartialEq};
+use openzeppelin_account::utils::secp256_point::{DebugSecp256Point, Secp256PointPartialEq};
 use openzeppelin_introspection::interface::ISRC5_ID;
 use openzeppelin_test_common::erc20::deploy_erc20;
 use openzeppelin_test_common::eth_account::EthAccountSpyHelpers;
diff --git a/packages/test_common/src/eth_account.cairo b/packages/test_common/src/eth_account.cairo
index 857581b47..377c06770 100644
--- a/packages/test_common/src/eth_account.cairo
+++ b/packages/test_common/src/eth_account.cairo
@@ -5,7 +5,7 @@ use core::starknet::secp256_trait::Secp256PointTrait;
 use openzeppelin_account::EthAccountComponent::{OwnerAdded, OwnerRemoved};
 use openzeppelin_account::EthAccountComponent;
 use openzeppelin_account::interface::EthPublicKey;
-use openzeppelin_account::utils::signature::EthSignature;
+use openzeppelin_account::utils::signature::Secp256Signature;
 use openzeppelin_testing::constants::TRANSACTION_HASH;
 use openzeppelin_testing::events::EventSpyExt;
 use openzeppelin_testing::signing::{Secp256k1KeyPair, Secp256k1SerializedSigning};
@@ -18,7 +18,7 @@ pub struct SignedTransactionData {
     pub private_key: u256,
     pub public_key: EthPublicKey,
     pub tx_hash: felt252,
-    pub signature: EthSignature
+    pub signature: Secp256Signature
 }
 
 pub fn SIGNED_TX_DATA(key_pair: Secp256k1KeyPair) -> SignedTransactionData {
@@ -28,7 +28,7 @@ pub fn SIGNED_TX_DATA(key_pair: Secp256k1KeyPair) -> SignedTransactionData {
         private_key: key_pair.secret_key,
         public_key: key_pair.public_key,
         tx_hash,
-        signature: EthSignature { r, s }
+        signature: Secp256Signature { r, s }
     }
 }
 
diff --git a/packages/testing/src/constants.cairo b/packages/testing/src/constants.cairo
index 33226fd6b..9a4a8891c 100644
--- a/packages/testing/src/constants.cairo
+++ b/packages/testing/src/constants.cairo
@@ -118,6 +118,18 @@ pub fn EMPTY_DATA() -> Span<felt252> {
 // Signing keys
 //
 
+pub mod stark {
+    use crate::signing::{StarkKeyPair, get_stark_keys_from};
+
+    pub fn KEY_PAIR() -> StarkKeyPair {
+        get_stark_keys_from('PRIVATE_KEY')
+    }
+
+    pub fn KEY_PAIR_2() -> StarkKeyPair {
+        get_stark_keys_from('PRIVATE_KEY_2')
+    }
+}
+
 pub mod secp256k1 {
     use crate::signing::{Secp256k1KeyPair, get_secp256k1_keys_from};
 
@@ -132,14 +144,16 @@ pub mod secp256k1 {
     }
 }
 
-pub mod stark {
-    use crate::signing::{StarkKeyPair, get_stark_keys_from};
+pub mod secp256r1 {
+    use crate::signing::{Secp256r1KeyPair, get_secp256r1_keys_from};
 
-    pub fn KEY_PAIR() -> StarkKeyPair {
-        get_stark_keys_from('PRIVATE_KEY')
+    pub fn KEY_PAIR() -> Secp256r1KeyPair {
+        let private_key = u256 { low: 'PRIVATE_LOW', high: 'PRIVATE_HIGH' };
+        get_secp256r1_keys_from(private_key)
     }
 
-    pub fn KEY_PAIR_2() -> StarkKeyPair {
-        get_stark_keys_from('PRIVATE_KEY_2')
+    pub fn KEY_PAIR_2() -> Secp256r1KeyPair {
+        let private_key = u256 { low: 'PRIVATE_LOW_2', high: 'PRIVATE_HIGH_2' };
+        get_secp256r1_keys_from(private_key)
     }
 }
diff --git a/packages/testing/src/signing.cairo b/packages/testing/src/signing.cairo
index 4e4d5da2f..18e56ab49 100644
--- a/packages/testing/src/signing.cairo
+++ b/packages/testing/src/signing.cairo
@@ -1,10 +1,13 @@
+use snforge_std::signature::KeyPair;
 use snforge_std::signature::secp256k1_curve::{Secp256k1CurveSignerImpl, Secp256k1CurveKeyPairImpl};
+use snforge_std::signature::secp256r1_curve::{Secp256r1CurveSignerImpl, Secp256r1CurveKeyPairImpl};
 use snforge_std::signature::stark_curve::{StarkCurveSignerImpl, StarkCurveKeyPairImpl};
-use snforge_std::signature::{KeyPair};
 use starknet::secp256k1::Secp256k1Point;
+use starknet::secp256r1::Secp256r1Point;
 
 pub type StarkKeyPair = KeyPair<felt252, felt252>;
 pub type Secp256k1KeyPair = KeyPair<u256, Secp256k1Point>;
+pub type Secp256r1KeyPair = KeyPair<u256, Secp256r1Point>;
 
 /// Builds a Stark Key Pair from a private key represented by a `felt252` value.
 pub fn get_stark_keys_from(private_key: felt252) -> StarkKeyPair {
@@ -16,6 +19,11 @@ pub fn get_secp256k1_keys_from(private_key: u256) -> Secp256k1KeyPair {
     Secp256k1CurveKeyPairImpl::from_secret_key(private_key)
 }
 
+/// Builds a Secp256r1 Key Pair from a private key represented by a `u256` value.
+pub fn get_secp256r1_keys_from(private_key: u256) -> Secp256r1KeyPair {
+    Secp256r1CurveKeyPairImpl::from_secret_key(private_key)
+}
+
 /// A helper trait that facilitates converting a signature into a serialized format.
 pub trait SerializedSigning<KP, M> {
     fn serialized_sign(self: KP, msg: M) -> Array<felt252>;
@@ -34,3 +42,10 @@ pub impl Secp256k1SerializedSigning of SerializedSigning<Secp256k1KeyPair, u256>
         array![r.low.into(), r.high.into(), s.low.into(), s.high.into()]
     }
 }
+
+pub impl Secp256r1SerializedSigning of SerializedSigning<Secp256r1KeyPair, u256> {
+    fn serialized_sign(self: Secp256r1KeyPair, msg: u256) -> Array<felt252> {
+        let (r, s) = self.sign(msg).unwrap();
+        array![r.low.into(), r.high.into(), s.low.into(), s.high.into()]
+    }
+}