Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
33 changes: 12 additions & 21 deletions src/WebAuthnValidator/WebAuthnValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import { CheckSignatures } from "checknsignatures/CheckNSignatures.sol";
import { ECDSA } from "solady/utils/ECDSA.sol";
import { WebAuthn } from "@webauthn/WebAuthn.sol";
import { LibSort } from "solady/utils/LibSort.sol";
import { MODULE_TYPE_STATELESS_VALIDATOR as TYPE_STATELESS_VALIDATOR } from
"modulekit/module-bases/utils/ERC7579Constants.sol";
import {
MODULE_TYPE_STATELESS_VALIDATOR as TYPE_STATELESS_VALIDATOR
} from "modulekit/module-bases/utils/ERC7579Constants.sol";

/// @title WebAuthnValidator
/// @author Based on Rhinestone's OwnableValidator
Expand Down Expand Up @@ -188,8 +189,7 @@ contract WebAuthnValidator is ERC7579HybridValidatorBase {
}

// Generate deterministic credential ID
bytes32 credId =
generateCredentialId(_credentials[i].pubKeyX, _credentials[i].pubKeyY, account);
bytes32 credId = generateCredentialId(_credentials[i].pubKeyX, _credentials[i].pubKeyY);

// Store the credential
credentialDetails[credId][account] = WebAuthnCredential({
Expand Down Expand Up @@ -289,7 +289,7 @@ contract WebAuthnValidator is ERC7579HybridValidatorBase {
}

// Generate deterministic credential ID
bytes32 credentialId = generateCredentialId(pubKeyX, pubKeyY, account);
bytes32 credentialId = generateCredentialId(pubKeyX, pubKeyY);

// Check if max credentials is reached
if (credentials.length(account) >= MAX_CREDENTIALS) {
Expand Down Expand Up @@ -325,7 +325,7 @@ contract WebAuthnValidator is ERC7579HybridValidatorBase {
if (!isInitialized(account)) revert NotInitialized(account);

// Generate deterministic credential ID
bytes32 credentialId = generateCredentialId(pubKeyX, pubKeyY, account);
bytes32 credentialId = generateCredentialId(pubKeyX, pubKeyY);

// Check if removing would break threshold
if (credentials.length(account) <= threshold[account]) {
Expand Down Expand Up @@ -378,7 +378,7 @@ contract WebAuthnValidator is ERC7579HybridValidatorBase {
view
returns (bool exists)
{
bytes32 credentialId = generateCredentialId(pubKeyX, pubKeyY, account);
bytes32 credentialId = generateCredentialId(pubKeyX, pubKeyY);
return credentials.contains(account, credentialId);
}

Expand Down Expand Up @@ -416,16 +416,8 @@ contract WebAuthnValidator is ERC7579HybridValidatorBase {
return (cred.pubKeyX, cred.pubKeyY, cred.requireUV);
}

function generateCredentialId(
uint256 pubKeyX,
uint256 pubKeyY,
address account
)
public
pure
returns (bytes32)
{
return keccak256(abi.encode(pubKeyX, pubKeyY, account));
function generateCredentialId(uint256 pubKeyX, uint256 pubKeyY) public pure returns (bytes32) {
return keccak256(abi.encode(pubKeyX, pubKeyY));
}

/*//////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -497,9 +489,8 @@ contract WebAuthnValidator is ERC7579HybridValidatorBase {
override
returns (bool)
{
// Decode the threshold, credentials and account address from data
(WebAuthVerificationContext memory context, address account) =
abi.decode(data, (WebAuthVerificationContext, address));
// Decode the threshold and credentials from data
(WebAuthVerificationContext memory context) = abi.decode(data, (WebAuthVerificationContext));
// Make sure the credentials are unique and sorted
require(context.credentialIds.isSortedAndUniquified(), NotSorted());

Expand All @@ -516,7 +507,7 @@ contract WebAuthnValidator is ERC7579HybridValidatorBase {
// Generate credentialId from each entry and verify that it matches the provided data
for (uint256 i = 0; i < credentialsLength; ++i) {
bytes32 expectedId = generateCredentialId(
context.credentialData[i].pubKeyX, context.credentialData[i].pubKeyY, account
context.credentialData[i].pubKeyX, context.credentialData[i].pubKeyY
);
if (context.credentialIds[i] != expectedId) {
return false;
Expand Down
100 changes: 47 additions & 53 deletions test/WebAuthnValidator/integration/WebAuthnValidator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,7 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {
// Pre-compute credential IDs for testing
_credentialIds = new bytes32[](2);
for (uint256 i = 0; i < 2; i++) {
_credentialIds[i] = validator.generateCredentialId(
_pubKeysX[i], _pubKeysY[i], address(instance.account)
);
_credentialIds[i] = validator.generateCredentialId(_pubKeysX[i], _pubKeysY[i]);
}

// Use a fixed challenge for testing
Expand Down Expand Up @@ -118,14 +116,10 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {
WebAuthnValidator.WebAuthnCredential[] memory credentialData =
new WebAuthnValidator.WebAuthnCredential[](2);
credentialData[0] = WebAuthnValidator.WebAuthnCredential({
pubKeyX: _pubKeysX[0],
pubKeyY: _pubKeysY[0],
requireUV: _requireUVs[0]
pubKeyX: _pubKeysX[0], pubKeyY: _pubKeysY[0], requireUV: _requireUVs[0]
});
credentialData[1] = WebAuthnValidator.WebAuthnCredential({
pubKeyX: _pubKeysX[1],
pubKeyY: _pubKeysY[1],
requireUV: _requireUVs[1]
pubKeyX: _pubKeysX[1], pubKeyY: _pubKeysY[1], requireUV: _requireUVs[1]
});

// Install the validator module on the account
Expand Down Expand Up @@ -168,9 +162,7 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {
function test_OnUninstallRemovesCredentialsAndThreshold() public {
// It should remove the credentials and threshold
instance.uninstallModule({
moduleTypeId: MODULE_TYPE_VALIDATOR,
module: address(validator),
data: ""
moduleTypeId: MODULE_TYPE_VALIDATOR, module: address(validator), data: ""
});

uint256 threshold = validator.threshold(address(instance.account));
Expand All @@ -188,11 +180,13 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {
uint256 newThreshold = 1;

instance.getExecOps({
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(WebAuthnValidator.setThreshold.selector, newThreshold),
txValidator: address(instance.defaultValidator)
}).execUserOps();
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(
WebAuthnValidator.setThreshold.selector, newThreshold
),
txValidator: address(instance.defaultValidator)
}).execUserOps();

uint256 threshold = validator.threshold(address(instance.account));
assertEq(threshold, newThreshold, "Threshold should be updated");
Expand All @@ -204,11 +198,13 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {

instance.expect4337Revert();
instance.getExecOps({
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(WebAuthnValidator.setThreshold.selector, newThreshold),
txValidator: address(instance.defaultValidator)
}).execUserOps();
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(
WebAuthnValidator.setThreshold.selector, newThreshold
),
txValidator: address(instance.defaultValidator)
}).execUserOps();
}

function test_AddCredential() public {
Expand All @@ -219,17 +215,16 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {
bool newRequireUV = true;

instance.getExecOps({
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(
WebAuthnValidator.addCredential.selector, newPubKeyX, newPubKeyY, newRequireUV
),
txValidator: address(instance.defaultValidator)
}).execUserOps();
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(
WebAuthnValidator.addCredential.selector, newPubKeyX, newPubKeyY, newRequireUV
),
txValidator: address(instance.defaultValidator)
}).execUserOps();

// Check credential was added
bytes32 newCredentialId =
validator.generateCredentialId(newPubKeyX, newPubKeyY, address(instance.account));
bytes32 newCredentialId = validator.generateCredentialId(newPubKeyX, newPubKeyY);

assertTrue(
validator.hasCredentialById(newCredentialId, address(instance.account)),
Expand All @@ -247,13 +242,16 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {

// It should remove a credential and decrement the credential count
instance.getExecOps({
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(
WebAuthnValidator.removeCredential.selector, _pubKeysX[0], _pubKeysY[0], _requireUVs[0]
),
txValidator: address(instance.defaultValidator)
}).execUserOps();
target: address(validator),
value: 0,
callData: abi.encodeWithSelector(
WebAuthnValidator.removeCredential.selector,
_pubKeysX[0],
_pubKeysY[0],
_requireUVs[0]
),
txValidator: address(instance.defaultValidator)
}).execUserOps();

// Check credential was removed
assertFalse(
Expand Down Expand Up @@ -290,26 +288,22 @@ contract WebAuthnValidatorIntegrationTest is BaseIntegrationTest {
WebAuthnValidator.WebAuthnCredential[] memory credentialData =
new WebAuthnValidator.WebAuthnCredential[](2);
credentialData[0] = WebAuthnValidator.WebAuthnCredential({
pubKeyX: _pubKeysX[0],
pubKeyY: _pubKeysY[0],
requireUV: _requireUVs[0]
pubKeyX: _pubKeysX[0], pubKeyY: _pubKeysY[0], requireUV: _requireUVs[0]
});
credentialData[1] = WebAuthnValidator.WebAuthnCredential({
pubKeyX: _pubKeysX[1],
pubKeyY: _pubKeysY[1],
requireUV: _requireUVs[1]
pubKeyX: _pubKeysX[1], pubKeyY: _pubKeysY[1], requireUV: _requireUVs[1]
});

// Create verification context
WebAuthnValidator.WebAuthVerificationContext memory context = WebAuthnValidator
.WebAuthVerificationContext({
usePrecompile: false,
threshold: 2,
credentialIds: credentialIds,
credentialData: credentialData
});

bytes memory data = abi.encode(context, address(instance.account));
WebAuthnValidator.WebAuthVerificationContext memory context =
WebAuthnValidator.WebAuthVerificationContext({
usePrecompile: false,
threshold: 2,
credentialIds: credentialIds,
credentialData: credentialData
});

bytes memory data = abi.encode(context);
WebAuthn.WebAuthnAuth[] memory sigs = new WebAuthn.WebAuthnAuth[](2);
(credentialIds,, sigs) = abi.decode(sig, (bytes32[], bool, WebAuthn.WebAuthnAuth[]));

Expand Down
Loading
Loading