Skip to content

Commit

Permalink
feat: session keys
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Nov 13, 2024
1 parent af00b25 commit 930fc83
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 163 deletions.
76 changes: 16 additions & 60 deletions contracts/src/account/ExperimentalDelegation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,7 @@ contract ExperimentalDelegation is Receiver, MultiSendCallOnly {
/// @notice Initializes the EOA with a public key to authorize.
/// @param label_ - The label to associate with the EOA.
/// @param key - The key to authorize.
function initialize(
string calldata label_,
Key calldata key
) public onlyOwner returns (uint32 keyIndex) {
function initialize(string calldata label_, Key calldata key) public onlyOwner returns (uint32 keyIndex) {
if (keys.length > 0) revert AlreadyInitialized();

label = label_;
Expand All @@ -97,19 +94,13 @@ contract ExperimentalDelegation is Receiver, MultiSendCallOnly {
/// @param label_ - The label to associate with the EOA.
/// @param key - The key to authorize.
/// @param signature - The signature over the key: `sign(keccak256(abi.encode(nonce, key)))`.
function initialize(
string calldata label_,
Key calldata key,
ECDSA.Signature calldata signature
) public returns (uint32 keyIndex) {
function initialize(string calldata label_, Key calldata key, ECDSA.Signature calldata signature)
public
returns (uint32 keyIndex)
{
bytes32 digest = keccak256(abi.encode(nonce++, label_, key));

address signer = ecrecover(
digest,
signature.yParity == 0 ? 27 : 28,
bytes32(signature.r),
bytes32(signature.s)
);
address signer = ecrecover(digest, signature.yParity == 0 ? 27 : 28, bytes32(signature.r), bytes32(signature.s));
if (signer != address(this)) revert InvalidSignature();

if (keys.length > 0) revert AlreadyInitialized();
Expand All @@ -121,19 +112,14 @@ contract ExperimentalDelegation is Receiver, MultiSendCallOnly {

/// @notice Authorizes a new public key.
/// @param key - The key to authorize.
function authorize(
Key calldata key
) public onlyOwner returns (uint32 keyIndex) {
function authorize(Key calldata key) public onlyOwner returns (uint32 keyIndex) {
return _authorize(key);
}

/// @notice Authorizes a new public key on behalf of the EOA, provided the EOA's signature.
/// @param key - The key to authorize.
/// @param signature - The signature over the key: `sign(keccak256(abi.encode(nonce, key)))`.
function authorize(
Key calldata key,
bytes calldata signature
) public returns (uint32 keyIndex) {
function authorize(Key calldata key, bytes calldata signature) public returns (uint32 keyIndex) {
WrappedSignature memory wrappedSignature = _parseSignature(signature);
Key memory authorizingKey = keys[wrappedSignature.keyIndex];

Expand Down Expand Up @@ -188,10 +174,7 @@ contract ExperimentalDelegation is Receiver, MultiSendCallOnly {
/// @param digest - The digest to verify.
/// @param signature - The wrapped signature to verify.
/// @return magicValue - The magic value indicating the validity of the signature.
function isValidSignature(
bytes32 digest,
bytes calldata signature
) public view returns (bytes4 magicValue) {
function isValidSignature(bytes32 digest, bytes calldata signature) public view returns (bytes4 magicValue) {
WrappedSignature memory wrappedSignature = _parseSignature(signature);

// If prehash flag is set (usually for WebCrypto P256), SHA-256 hash the digest.
Expand Down Expand Up @@ -219,25 +202,12 @@ contract ExperimentalDelegation is Receiver, MultiSendCallOnly {
if (key.expiry > 0 && key.expiry < block.timestamp) return failure;

// Verify based on key type.
if (
key.keyType == KeyType.P256 &&
P256.verify(digest, wrappedSignature.signature, key.publicKey)
) {
if (key.keyType == KeyType.P256 && P256.verify(digest, wrappedSignature.signature, key.publicKey)) {
return success;
}
if (key.keyType == KeyType.WebAuthnP256) {
WebAuthnP256.Metadata memory metadata = abi.decode(
wrappedSignature.metadata,
(WebAuthnP256.Metadata)
);
if (
WebAuthnP256.verify(
digest,
metadata,
wrappedSignature.signature,
key.publicKey
)
) return success;
WebAuthnP256.Metadata memory metadata = abi.decode(wrappedSignature.metadata, (WebAuthnP256.Metadata));
if (WebAuthnP256.verify(digest, metadata, wrappedSignature.signature, key.publicKey)) return success;
}
}

Expand Down Expand Up @@ -275,14 +245,8 @@ contract ExperimentalDelegation is Receiver, MultiSendCallOnly {
/// @notice Asserts that a signature is valid.
/// @param digest - The digest to verify.
/// @param signature - The wrapped signature to verify.
function _assertSignature(
bytes32 digest,
bytes calldata signature
) internal view {
WrappedSignature memory wrappedSignature = abi.decode(
signature,
(WrappedSignature)
);
function _assertSignature(bytes32 digest, bytes calldata signature) internal view {
WrappedSignature memory wrappedSignature = abi.decode(signature, (WrappedSignature));

Key memory key = keys[wrappedSignature.keyIndex];
if (key.expiry > 0 && key.expiry < block.timestamp) {
Expand All @@ -297,20 +261,12 @@ contract ExperimentalDelegation is Receiver, MultiSendCallOnly {
/// @notice Parses a signature from bytes format.
/// @param signature - The signature to parse.
/// @return wrappedSignature - The parsed signature.
function _parseSignature(
bytes calldata signature
) internal pure returns (WrappedSignature memory) {
function _parseSignature(bytes calldata signature) internal pure returns (WrappedSignature memory) {
if (signature.length == 65) {
bytes32 r = bytes32(signature[0:32]);
bytes32 s = bytes32(signature[32:64]);
uint8 yParity = uint8(signature[64]);
return
WrappedSignature(
0,
ECDSA.Signature(uint256(r), uint256(s), yParity),
false,
OWNER_METADATA
);
return WrappedSignature(0, ECDSA.Signature(uint256(r), uint256(s), yParity), false, OWNER_METADATA);
}
return abi.decode(signature, (WrappedSignature));
}
Expand Down
8 changes: 2 additions & 6 deletions contracts/test/account/ExperimentalDelegation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,8 @@ contract ExperimentalDelegationTest is Test {
ExperimentalDelegation.Key memory key =
ExperimentalDelegation.Key(0, ExperimentalDelegation.KeyType.P256, ECDSA.PublicKey(x, y));

ExperimentalDelegation.WrappedSignature memory wrappedSignature = ExperimentalDelegation.WrappedSignature(
0,
ECDSA.Signature(uint256(r), uint256(s), uint8(0)),
false,
abi.encode(WebAuthnP256.Metadata("0x", "aa", 0, 0, false))
);
ExperimentalDelegation.WrappedSignature memory wrappedSignature =
ExperimentalDelegation.WrappedSignature(0, ECDSA.Signature(uint256(r), uint256(s), uint8(0)), false, "0x");

vm.prank(address(delegation));
delegation.authorize(key);
Expand Down
Loading

0 comments on commit 930fc83

Please sign in to comment.