Skip to content

Commit

Permalink
working webauthn verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
nalinbhardwaj committed Oct 17, 2023
1 parent 87b8648 commit 6292817
Show file tree
Hide file tree
Showing 12 changed files with 2,165 additions and 37 deletions.
152 changes: 118 additions & 34 deletions lcov.info
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
TN:
SF:src/P256.sol
FN:10,P256.verifySignatureAllowMalleability
FNDA:3,P256.verifySignatureAllowMalleability
DA:17,3
DA:18,3
DA:19,3
FNDA:4,P256.verifySignatureAllowMalleability
DA:17,4
DA:18,4
DA:19,4
BRDA:19,0,0,-
BRDA:19,0,1,-
DA:21,3
DA:21,4
FN:28,P256.verifySignature
FNDA:2,P256.verifySignature
DA:36,2
BRDA:36,1,0,1
FNDA:3,P256.verifySignature
DA:36,3
BRDA:36,1,0,2
BRDA:36,1,1,1
DA:37,1
DA:40,1
DA:40,2
FNF:2
FNH:2
LF:7
Expand Down Expand Up @@ -235,7 +235,115 @@ BRF:46
BRH:39
end_of_record
TN:
SF:test/FCL_elliptic.sol
SF:src/WebAuthn.sol
FN:11,WebAuthn.contains
FNDA:1,WebAuthn.contains
DA:16,1
DA:17,1
DA:19,1
DA:20,1
DA:22,1
DA:23,20
BRDA:23,0,0,20
BRDA:23,0,1,-
DA:24,0
DA:27,20
BRDA:27,1,0,20
BRDA:27,1,1,-
DA:28,0
DA:31,1
FN:34,WebAuthn.checkAuthFlags
FNDA:1,WebAuthn.checkAuthFlags
DA:39,1
BRDA:39,2,0,1
BRDA:39,2,1,-
DA:40,0
DA:44,1
BRDA:44,3,0,1
BRDA:44,3,1,-
DA:45,0
DA:48,1
FN:51,WebAuthn.verifySignature
FNDA:1,WebAuthn.verifySignature
DA:63,1
BRDA:63,4,0,1
BRDA:63,4,1,-
DA:64,0
DA:68,1
DA:69,1
DA:75,1
BRDA:75,5,0,1
BRDA:75,5,1,-
DA:76,0
DA:79,1
DA:80,1
DA:84,1
FNF:3
FNH:3
LF:24
LH:18
BRF:12
BRH:6
end_of_record
TN:
SF:src/utils/Base64URL.sol
FN:7,Base64URL.encode
FNDA:1795,Base64URL.encode
DA:8,1795
DA:9,1795
DA:12,1795
DA:13,1795
DA:14,3330
BRDA:14,0,0,1539
BRDA:14,0,1,1791
DA:15,1539
DA:17,1791
DA:21,1795
DA:22,1795
DA:24,1795
DA:25,524821
BRDA:25,1,0,1248
BRDA:25,1,1,523573
DA:26,1248
DA:27,523573
BRDA:27,2,0,176191
BRDA:27,2,1,347382
DA:28,176191
DA:30,347382
DA:34,1795
FNF:1
FNH:1
LF:16
LH:16
BRF:6
BRH:6
end_of_record
TN:
SF:test/GasBenchmark.t.sol
FN:28,FCLWrapperEIP7212.
FNDA:1778,FCLWrapperEIP7212.
DA:29,1778
BRDA:29,0,0,1778
BRDA:29,0,1,-
DA:30,0
DA:33,1778
DA:34,1778
DA:35,1778
DA:36,1778
DA:37,1778
DA:39,1778
DA:40,1778
DA:42,1778
DA:44,1778
FNF:1
FNH:1
LF:11
LH:10
BRF:2
BRH:1
end_of_record
TN:
SF:test/external/FCL_elliptic.sol
FN:58,FCL_Elliptic_ZZ.FCL_nModInv
FNDA:0,FCL_Elliptic_ZZ.FCL_nModInv
DA:59,0
Expand Down Expand Up @@ -432,27 +540,3 @@ LH:0
BRF:26
BRH:0
end_of_record
TN:
SF:test/GasBenchmark.t.sol
FN:28,FCLWrapperEIP7212.
FNDA:1778,FCLWrapperEIP7212.
DA:29,1778
BRDA:29,0,0,1778
BRDA:29,0,1,-
DA:30,0
DA:33,1778
DA:34,1778
DA:35,1778
DA:36,1778
DA:37,1778
DA:39,1778
DA:40,1778
DA:42,1778
DA:44,1778
FNF:1
FNH:1
LF:11
LH:10
BRF:2
BRH:1
end_of_record
3 changes: 2 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
forge-std/=lib/forge-std/src/
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/
86 changes: 86 additions & 0 deletions src/Webauthn.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import {Base64URL} from "./utils/Base64URL.sol";
import {P256} from "./P256.sol";

/**
* Helper library for external contracts to verify WebAuthn signatures.
**/
library WebAuthn {
function contains(
string memory substr,
string memory str,
uint256 location
) internal pure returns (bool) {
bytes memory substr_bytes = bytes(substr);
bytes memory str_bytes = bytes(str);

uint256 substr_len = substr_bytes.length;
uint256 str_len = str_bytes.length;

for (uint256 i = 0; i < substr_len; i++) {
if (location + i >= str_len) {
return false;
}

if (substr_bytes[i] != str_bytes[location + i]) {
return false;
}
}
return true;
}

function checkAuthFlags(
bytes1 flags,
bool requireUserVerification
) internal pure returns (bool) {
// Check the UP bit
if (flags & 0x01 != 0x01) {
return false;
}

// Check the UV bit
if (requireUserVerification && (flags & 0x04) != 0x04) {
return false;
}

return true;
}

function verifySignature(
bytes memory challenge,
bytes memory authenticatorData,
bool requireUserVerification,
string memory clientDataJSON,
uint256 challengeLocation,
uint256 r,
uint256 s,
uint256 x,
uint256 y
) public view returns (bool) {
// Check that authenticatorData has good flags
if (!checkAuthFlags(authenticatorData[32], requireUserVerification)) {
return false;
}

// Check that challenge is in the clientDataJSON
string memory challenge_b64url = Base64URL.encode(challenge);
string memory challenge_property = string.concat(
'"challenge":"',
challenge_b64url,
'"'
);

if (!contains(challenge_property, clientDataJSON, challengeLocation)) {
return false;
}

bytes32 clientDataJSON_hash = sha256(bytes(clientDataJSON));
bytes32 message_hash = sha256(
abi.encodePacked(authenticatorData, clientDataJSON_hash)
);

return P256.verifySignature(message_hash, r, s, x, y);
}
}
36 changes: 36 additions & 0 deletions src/utils/Base64URL.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import "openzeppelin-contracts/contracts/utils/Base64.sol";

library Base64URL {
function encode(bytes memory data) public pure returns (string memory) {
string memory strb64 = Base64.encode(data);
bytes memory b64 = bytes(strb64);

// count and ignore all "=" symbols from the end of the string
uint256 equalsCount = 0;
for (int256 i = int256(b64.length) - 1; i >= 0; i--) {
if (b64[uint256(i)] == "=") {
equalsCount++;
} else {
break;
}
}

uint256 len = b64.length - equalsCount;
bytes memory result = new bytes(len);

for (uint256 i = 0; i < len; i++) {
if (b64[i] == "+") {
result[i] = "-";
} else if (b64[i] == "/") {
result[i] = "_";
} else {
result[i] = b64[i];
}
}

return string(result);
}
}
38 changes: 38 additions & 0 deletions test-vectors/generate_scure_base64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import fetch from "cross-fetch";
import fs from "fs";
import { base64urlnopad } from "@scure/base";

const scureVectorsURL =
"https://github.com/paulmillr/scure-base/raw/main/test/vectors/base_vectors.json";

type ScureVector = {
fn_name: string;
data: string;
exp: string;
};

// Fetch scure's test vectors and filter out base64url
async function main() {
const sourceObj: { v: ScureVector[] } = await fetch(scureVectorsURL).then(
(res) => res.json()
);
const vectors = sourceObj.v.filter(
(v: ScureVector) => v.fn_name === "base64url"
);

// Write to JSON
const filepath = "./vectors_scure_base64url.jsonl";
console.log(`Writing ${vectors.length} vectors to ${filepath}`);
const lines = vectors.map((v) => {
const data = Uint8Array.from(Buffer.from(v.data, "hex"));
return JSON.stringify({
data: v.data,
exp: base64urlnopad.encode(data),
});
});
fs.writeFileSync(filepath, lines.join("\n"));
}

main()
.then(() => console.log("Done"))
.catch((err) => console.error(err));
9 changes: 9 additions & 0 deletions test-vectors/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion test-vectors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
"scripts": {
"generate_wycheproof": "ts-node generate_wycheproof.ts",
"generate_random_valid": "ts-node generate_random_valid.ts",
"generate_scure_base64": "ts-node generate_scure_base64.ts",
"test": "ts-node test.ts"
},
"author": "",
"license": "MIT",
"dependencies": {
"@lapo/asn1js": "^1.2.4",
"@noble/curves": "^1.2.0",
"@scure/base": "^1.1.3",
"@tsconfig/node20": "^20.1.2",
"cross-fetch": "^4.0.0",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
}
}
}
Loading

0 comments on commit 6292817

Please sign in to comment.