Skip to content

Commit

Permalink
Session keys can sign now (EIP712 only) & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
eloi010 committed Jul 7, 2023
1 parent 35f93f2 commit 9073f95
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 3 deletions.
16 changes: 13 additions & 3 deletions contracts/core/BaseOpenfortAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ abstract contract BaseOpenfortAccount is
* @param validUntil this sessionKey is valid only after this timestamp.
* @param limit limit of uses remaining
* @param masterSessionKey if set to true, the session key does not have any limitation other than the validity time
* @param canSign if set to true, the session key can sign as the account (future)
* @param whitelising if set to true, the session key has to follow whitelisting rules
* @param whitelist - this session key can only interact with the addresses in the whitelist.
*/
Expand Down Expand Up @@ -178,7 +177,7 @@ abstract contract BaseOpenfortAccount is

/*
* @notice See EIP-1271
* Right now, only the owner can sign. In other words, session keys cannot be used for signing.
* Any signature by the owner is valid. Session keys need to sign using EIP712.
*/
function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4) {
address signer = _hash.recover(_signature);
Expand All @@ -195,7 +194,18 @@ abstract contract BaseOpenfortAccount is
if (owner() == signer) {
return MAGICVALUE;
}
return 0xffffffff;

SessionKeyStruct storage sessionKey = sessionKeys[signer];
// If the signer is a session key that is still valid
if (
sessionKey.validUntil == 0 || sessionKey.validAfter > block.timestamp
|| sessionKey.validUntil < block.timestamp || sessionKey.limit < 1
) {
return 0xffffffff;
} // Not owner or session key revoked
else {
return MAGICVALUE;
}
}

/**
Expand Down
169 changes: 169 additions & 0 deletions test/foundry/core/static/StaticOpenfortAccountTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1159,4 +1159,173 @@ contract StaticOpenfortAccountTest is Test {
assertEq(openfortAccount.owner(), signer); // Should [PASS]
assertEq(accountAdmin, signer); // Should [PASS]
}

/*
* Test Session key Signatures using isValidSignature's (EIP-1271)
*/
function testSessionKeySignature() public {
StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account));

// Now using Session keys. isValidSignature() should return the MAGIC VALUE too
bytes32 hash = keccak256("Signed by Openfort");

address sessionKey;
uint256 sessionKeyPrivKey;
(sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey");

vm.warp(100);
vm.prank(accountAdmin);
openfortAccount.registerSessionKey(sessionKey, 0, 150);
bytes32 domainSeparator;
{
(, string memory name, string memory version, uint256 chainId, address verifyingContract,,) =
openfortAccount.eip712Domain();
console.log(name);
bytes32 _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
domainSeparator = keccak256(
abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract)
);
}
bytes32 hash712 = domainSeparator.toTypedDataHash(hash);
console.logBytes32(hash712);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey, hash712);
bytes memory signature721 = abi.encodePacked(r, s, v);
console.logBytes(signature721);
bytes4 validSig = openfortAccount.isValidSignature(hash, signature721);
console.logBytes4(validSig);
assert(validSig == 0x1626ba7e); // Should [PASS]. EIP-712 typed signatures should work too
address signer = ecrecover(hash712, v, r, s);
assertEq(sessionKey, signer); // Should [PASS]
assertNotEq(accountAdmin, signer); // Should [PASS]
}

/*
* Test FAIL Session key Signatures using isValidSignature's (EIP-1271)
* Session key has expired
*/
function testFailSessionKeySignatureExpired() public {
StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account));

// Now using Session keys. isValidSignature() should return the MAGIC VALUE too
bytes32 hash = keccak256("Signed by Openfort");

address sessionKey;
uint256 sessionKeyPrivKey;
(sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey");

// We are now at timestamp 100
vm.warp(100);
vm.prank(accountAdmin);
openfortAccount.registerSessionKey(sessionKey, 0, 99); // register an expired session key
bytes32 domainSeparator;
{
(, string memory name, string memory version, uint256 chainId, address verifyingContract,,) =
openfortAccount.eip712Domain();
console.log(name);
bytes32 _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
domainSeparator = keccak256(
abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract)
);
}
bytes32 hash712 = domainSeparator.toTypedDataHash(hash);
console.logBytes32(hash712);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey, hash712);
bytes memory signature721 = abi.encodePacked(r, s, v);
console.logBytes(signature721);
bytes4 validSig = openfortAccount.isValidSignature(hash, signature721);
console.logBytes4(validSig);
assert(validSig == 0x1626ba7e); // Should fail because the session key is expired
address signer = ecrecover(hash712, v, r, s);
assertEq(sessionKey, signer); // Should [PASS]
assertNotEq(accountAdmin, signer); // Should [PASS]
}

/*
* Test FAIL Session key Signatures using isValidSignature's (EIP-1271)
* Session key is not valid yet
*/
function testFailSessionKeySignatureNotYet() public {
StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account));

// Now using Session keys. isValidSignature() should return the MAGIC VALUE too
bytes32 hash = keccak256("Signed by Openfort");

address sessionKey;
uint256 sessionKeyPrivKey;
(sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey");

// We are now at timestamp 100
vm.warp(100);
vm.prank(accountAdmin);
openfortAccount.registerSessionKey(sessionKey, 150, 250); // register a session key that is not valid yet
bytes32 domainSeparator;
{
(, string memory name, string memory version, uint256 chainId, address verifyingContract,,) =
openfortAccount.eip712Domain();
console.log(name);
bytes32 _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
domainSeparator = keccak256(
abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract)
);
}
bytes32 hash712 = domainSeparator.toTypedDataHash(hash);
console.logBytes32(hash712);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey, hash712);
bytes memory signature721 = abi.encodePacked(r, s, v);
console.logBytes(signature721);
bytes4 validSig = openfortAccount.isValidSignature(hash, signature721);
console.logBytes4(validSig);
assert(validSig == 0x1626ba7e); // Should fail because the session key is expired
address signer = ecrecover(hash712, v, r, s);
assertEq(sessionKey, signer); // Should [PASS]
assertNotEq(accountAdmin, signer); // Should [PASS]
}

/*
* Test FAIL Session key Signatures using isValidSignature's (EIP-1271)
* Session key is not a valid one. Using a wrong session key
*/
function testFailSessionKeySignatureInvalid() public {
StaticOpenfortAccount openfortAccount = StaticOpenfortAccount(payable(account));

// Now using Session keys. isValidSignature() should return the MAGIC VALUE too
bytes32 hash = keccak256("Signed by Openfort");

address sessionKey;
uint256 sessionKeyPrivKey;
(sessionKey, sessionKeyPrivKey) = makeAddrAndKey("sessionKey");
address sessionKey2;
uint256 sessionKeyPrivKey2;
(sessionKey2, sessionKeyPrivKey2) = makeAddrAndKey("sessionKey2");

// We are now at timestamp 100
vm.warp(100);
vm.prank(accountAdmin);
openfortAccount.registerSessionKey(sessionKey, 150, 250); // register a session key that is not valid yet
bytes32 domainSeparator;
{
(, string memory name, string memory version, uint256 chainId, address verifyingContract,,) =
openfortAccount.eip712Domain();
console.log(name);
bytes32 _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
domainSeparator = keccak256(
abi.encode(_TYPE_HASH, keccak256(bytes(name)), keccak256(bytes(version)), chainId, verifyingContract)
);
}
bytes32 hash712 = domainSeparator.toTypedDataHash(hash);
console.logBytes32(hash712);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sessionKeyPrivKey2, hash712);
bytes memory signature721 = abi.encodePacked(r, s, v);
console.logBytes(signature721);
bytes4 validSig = openfortAccount.isValidSignature(hash, signature721);
console.logBytes4(validSig);
assert(validSig == 0x1626ba7e); // Should fail because the session key is expired
address signer = ecrecover(hash712, v, r, s);
assertEq(sessionKey, signer); // Should [PASS]
assertNotEq(accountAdmin, signer); // Should [PASS]
}
}

0 comments on commit 9073f95

Please sign in to comment.