-
Notifications
You must be signed in to change notification settings - Fork 0
/
StealthAddressValidator.sol
181 lines (160 loc) · 8.2 KB
/
StealthAddressValidator.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {UserOperation} from "I4337/interfaces/UserOperation.sol";
import {ECDSA} from "solady/utils/ECDSA.sol";
import {EIP712} from "solady/utils/EIP712.sol";
import {StealthAggreagteSignature} from "../StealthAggreagteSignature.sol";
import "kernel/interfaces/IKernelValidator.sol";
import {ValidationData} from "kernel/common/Types.sol";
import {SIG_VALIDATION_FAILED} from "kernel/common/Constants.sol";
/**
* @dev Storage structure for Stealth Address Registry Module.
* StealthPubkey, dhkey are used in aggregated signature.
* EphemeralPubkey is used to recover private key of stealth address.
*/
struct StealthAddressValidatorStorage {
uint256 stealthPubkey;
uint256 dhkey;
uint256 ephemeralPubkey;
address stealthAddress;
uint8 stealthPubkeyPrefix;
uint8 dhkeyPrefix;
uint8 ephemeralPrefix;
}
/**
* @author Justin Zen - <[email protected]>
* @title Stealth Address Validator for ZeroDev Kernel.
* @notice This validator uses the Stealth address to validate signatures.
*/
contract StealthAddressValidator is IKernelValidator, EIP712 {
/// @notice The type hash used for kernel user op validation
bytes32 constant USER_OP_TYPEHASH = keccak256("AllowUserOp(address owner,address kernelWallet,bytes32 userOpHash)");
/// @notice The type hash used for kernel signature validation
bytes32 constant SIGNATURE_TYPEHASH = keccak256("KernelSignature(address owner,address kernelWallet,bytes32 hash)");
/// @notice Emitted when the stealth address of a kernel is changed.
event StealthAddressChanged(
address indexed kernel, address indexed oldStealthAddress, address indexed newStealthAddress
);
/* -------------------------------------------------------------------------- */
/* Storage */
/* -------------------------------------------------------------------------- */
mapping(address => StealthAddressValidatorStorage) public stealthAddressValidatorStorage;
/* -------------------------------------------------------------------------- */
/* EIP-712 Methods */
/* -------------------------------------------------------------------------- */
/// @dev Get the current name & version of the validator, used for the EIP-712 domain separator from Solady
function _domainNameAndVersion() internal pure override returns (string memory, string memory) {
return ("Kernel:StealthAddressValidator", "1.0.0");
}
/// @dev Tell to solady that the current name & version of the validator won't change, so no need to recompute the eip-712 domain separator
function _domainNameAndVersionMayChange() internal pure override returns (bool) {
return false;
}
/// @dev Export the current domain seperator
function getDomainSeperator() public view returns (bytes32) {
return _domainSeparator();
}
/* -------------------------------------------------------------------------- */
/* Kernel validator Methods */
/* -------------------------------------------------------------------------- */
/// @dev Enable this validator for a given `kernel` (msg.sender)
function enable(bytes calldata _data) external payable override {
address stealthAddress = address(bytes20(_data[0:20]));
uint256 stealthAddressPubkey = uint256(bytes32(_data[20:52]));
uint256 stealthAddressDhkey = uint256(bytes32(_data[52:84]));
uint8 stealthAddressPubkeyPrefix = uint8(_data[84]);
uint8 stealthAddressDhkeyPrefix = uint8(_data[85]);
uint256 ephemeralPubkey = uint256(bytes32(_data[86:118]));
uint8 ephemeralPrefix = uint8(_data[118]);
address oldStealthAddress = stealthAddressValidatorStorage[msg.sender].stealthAddress;
stealthAddressValidatorStorage[msg.sender] = StealthAddressValidatorStorage({
stealthPubkey: stealthAddressPubkey,
dhkey: stealthAddressDhkey,
ephemeralPubkey: ephemeralPubkey,
stealthAddress: stealthAddress,
stealthPubkeyPrefix: stealthAddressPubkeyPrefix,
dhkeyPrefix: stealthAddressDhkeyPrefix,
ephemeralPrefix: ephemeralPrefix
});
emit StealthAddressChanged(msg.sender, oldStealthAddress, stealthAddress);
}
/// @dev Disable this validator for a given `kernel` (msg.sender)
function disable(bytes calldata) external payable override {
address stealthAddress;
delete stealthAddressValidatorStorage[msg.sender];
emit StealthAddressChanged(msg.sender, stealthAddress, address(0));
}
/// @dev Validate a `_userOp` using a EIP-712 signature, signed by the owner of the kernel account who is the `_userOp` sender
function validateUserOp(UserOperation calldata _userOp, bytes32 _userOpHash, uint256)
external
payable
override
returns (ValidationData validationData)
{
bytes1 mode = _userOp.signature[0];
StealthAddressValidatorStorage storage stealthData = stealthAddressValidatorStorage[_userOp.sender];
address stealthAddress = stealthData.stealthAddress;
bytes32 typedDataHash =
_hashTypedData(keccak256(abi.encode(USER_OP_TYPEHASH, stealthAddress, _userOp.sender, _userOpHash)));
// 0x00: signature from spending key
// 0x01: aggregated signature from owner and shared secret
if (mode == 0x00) {
return stealthAddress == ECDSA.recover(typedDataHash, _userOp.signature[1:])
? ValidationData.wrap(0)
: SIG_VALIDATION_FAILED;
} else if (mode == 0x01) {
return StealthAggreagteSignature.validateAggregatedSignature(
stealthData.stealthPubkey,
stealthData.dhkey,
stealthData.stealthPubkeyPrefix,
stealthData.dhkeyPrefix,
typedDataHash,
_userOp.signature[1:]
) ? ValidationData.wrap(0) : SIG_VALIDATION_FAILED;
} else {
return SIG_VALIDATION_FAILED;
}
}
/// @dev Validate a `_signature` of the `_hash` ofor the given `kernel` (msg.sender)
function validateSignature(bytes32 _hash, bytes calldata _signature)
external
view
override
returns (ValidationData validationData)
{
bytes1 mode = _signature[0];
StealthAddressValidatorStorage storage stealthData = stealthAddressValidatorStorage[msg.sender];
address stealthAddress = stealthData.stealthAddress;
bytes32 typedDataHash =
_hashTypedData(keccak256(abi.encode(SIGNATURE_TYPEHASH, stealthAddress, msg.sender, _hash)));
// 0x00: signature from spending key
// 0x01: aggregated signature from owner and shared secret
if (mode == 0x00) {
return stealthAddress == ECDSA.recover(typedDataHash, _signature[1:])
? ValidationData.wrap(0)
: SIG_VALIDATION_FAILED;
} else if (mode == 0x01) {
return StealthAggreagteSignature.validateAggregatedSignature(
stealthData.stealthPubkey,
stealthData.dhkey,
stealthData.stealthPubkeyPrefix,
stealthData.dhkeyPrefix,
typedDataHash,
_signature[1:]
) ? ValidationData.wrap(0) : SIG_VALIDATION_FAILED;
} else {
return SIG_VALIDATION_FAILED;
}
}
/// @dev Check if the caller is a valid signer for this kernel account
function validCaller(address _caller, bytes calldata) external view override returns (bool) {
return stealthAddressValidatorStorage[msg.sender].stealthAddress == _caller;
}
/* -------------------------------------------------------------------------- */
/* Public view methods */
/* -------------------------------------------------------------------------- */
/// @dev Get the owner of a given `kernel`
function getOwner(address _kernel) public view returns (address) {
return stealthAddressValidatorStorage[_kernel].stealthAddress;
}
}