forked from matter-labs/era-contracts
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
160 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity 0.8.20; | ||
|
||
contract PasskeyBinder { | ||
//curve prime field modulus | ||
uint256 private constant p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF; | ||
//short weierstrass second coefficient | ||
uint256 private constant b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B; | ||
//short weierstrass first coefficient | ||
uint256 private constant a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC; | ||
|
||
struct P256PublicKey { | ||
uint256 x; | ||
uint256 y; | ||
} | ||
|
||
mapping(bytes32 keyIdHash => P256PublicKey) private authorizedKeys; | ||
mapping(bytes32 keyIdHash => address account) private keyIdHashToAccount; | ||
mapping(address account => string[] keyIds) private accountToKeyIdList; | ||
|
||
/// @dev Event emitted when a P256 key is added | ||
event AddedP256Key(bytes32 indexed keyIdHash, string keyId, uint256 x, uint256 y); | ||
|
||
/// @dev Event emitted when a P256 key is removed | ||
event RemovedP256Key(bytes32 indexed keyIdHash, uint256 x, uint256 y); | ||
|
||
/// @dev Error emitted when a P256 key is not on the curve | ||
error KeyNotOnCurve(uint256 x, uint256 y); | ||
/// @dev Error emitted when an empty key is attempted to be added | ||
error InvalidEmptyKey(); | ||
/// @dev Error emitted when a P256 key is already stored and attempted to be added | ||
error KeyAlreadyExists(string keyId); | ||
/// @dev Error emitted when a P256 key is not stored and attempted to be removed | ||
error KeyDoesNotExist(string keyId); | ||
/// @dev Error emitted when a P256 key is not owned by the caller | ||
error DoesNotOwner(string keyId); | ||
|
||
function addKey(string calldata _keyId, uint256 _x, uint256 _y) external { | ||
// slither-disable-next-line tx-origin | ||
require(msg.sender == tx.origin, "Not authorized"); | ||
_addKey(_keyId, _x, _y); | ||
} | ||
|
||
function _addKey(string calldata _keyId, uint256 _x, uint256 _y) internal { | ||
if (!isValidPublicKey(_x, _y)) revert KeyNotOnCurve(_x, _y); | ||
bytes32 keyIdHash_ = keccak256(abi.encodePacked(_keyId)); | ||
|
||
if (bytes(_keyId).length == 0) revert InvalidEmptyKey(); | ||
|
||
P256PublicKey storage publicKey_ = authorizedKeys[keyIdHash_]; | ||
|
||
// update key | ||
if (publicKey_.x != 0 || publicKey_.y != 0) { | ||
revert KeyAlreadyExists(_keyId); | ||
} | ||
|
||
authorizedKeys[keyIdHash_] = P256PublicKey(_x, _y); | ||
keyIdHashToAccount[keyIdHash_] = msg.sender; | ||
accountToKeyIdList[msg.sender].push(_keyId); | ||
|
||
emit AddedP256Key(keyIdHash_, _keyId, _x, _y); | ||
} | ||
|
||
function removeKey(string calldata _keyId) external { | ||
bytes32 keyIdHash_ = keccak256(abi.encodePacked(_keyId)); | ||
if (keyIdHashToAccount[keyIdHash_] != msg.sender) revert DoesNotOwner(_keyId); | ||
P256PublicKey memory publicKey_ = authorizedKeys[keyIdHash_]; | ||
uint256 x_ = publicKey_.x; | ||
uint256 y_ = publicKey_.y; | ||
|
||
if (x_ == 0 && y_ == 0) revert KeyDoesNotExist(_keyId); | ||
|
||
delete authorizedKeys[keyIdHash_]; | ||
delete keyIdHashToAccount[keyIdHash_]; | ||
uint256 length = accountToKeyIdList[msg.sender].length; | ||
for (uint256 i = 0; i < length; i++) { | ||
if (keccak256(abi.encodePacked(accountToKeyIdList[msg.sender][i])) == keyIdHash_) { | ||
accountToKeyIdList[msg.sender][i] = accountToKeyIdList[msg.sender][length - 1]; | ||
accountToKeyIdList[msg.sender].pop(); | ||
break; | ||
} | ||
} | ||
|
||
emit RemovedP256Key(keyIdHash_, x_, y_); | ||
} | ||
/** | ||
* @notice Returns the P256 public key coordinates of a given key ID if it is a signer | ||
* @param keyIdHash The ID Hash of the key to get | ||
* @return x_ The X value of the public key | ||
* @return y_ The Y value of the public key | ||
*/ | ||
function getKey(bytes32 keyIdHash) external view returns (uint256 x_, uint256 y_) { | ||
P256PublicKey memory publicKey_ = authorizedKeys[keyIdHash]; | ||
x_ = publicKey_.x; | ||
y_ = publicKey_.y; | ||
} | ||
|
||
function getKeyIdLength(address _account) external view returns (uint256) { | ||
return accountToKeyIdList[_account].length; | ||
} | ||
|
||
function getKeyIdByIndex(address _account, uint256 _index) external view returns (string memory) { | ||
return accountToKeyIdList[_account][_index]; | ||
} | ||
|
||
function getAccountByKeyIdHash(bytes32 keyIdHash) external view returns (address) { | ||
return keyIdHashToAccount[keyIdHash]; | ||
} | ||
|
||
function getP256PublicKey(bytes32 keyIdHash) external view returns (P256PublicKey memory) { | ||
return authorizedKeys[keyIdHash]; | ||
} | ||
|
||
function isValidPublicKey(uint256 x, uint256 y) internal pure returns (bool) { | ||
if (x >= p || y >= p || ((x == 0) && (y == 0))) { | ||
return false; | ||
} | ||
unchecked { | ||
uint256 LHS = mulmod(y, y, p); // y^2 | ||
uint256 RHS = addmod(mulmod(mulmod(x, x, p), x, p), mulmod(x, a, p), p); // x^3+ax | ||
RHS = addmod(RHS, b, p); // x^3 + a*x + b | ||
|
||
return LHS == RHS; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity 0.8.20; | ||
|
||
interface IPasskeyBinder { | ||
/// @dev Returns the public key associated with the given keyIdHash. | ||
/// @param keyIdHash The hash of the keyId. | ||
/// @return x_ The x-coordinate of the public key. | ||
/// @return y_ The y-coordinate of the public key. | ||
function getKey(bytes32 keyIdHash) external view returns (uint256 x_, uint256 y_); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters