Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: Avoid attack of appending bytes to the P256 signature #119

Merged
merged 4 commits into from
Oct 16, 2023
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
55 changes: 0 additions & 55 deletions contracts/dev/TestWebAuthn.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,46 +53,6 @@ contract TestWebAuthn {
require(succ, "WebAuthn verifySignature failed");
}

function packSignature(
bytes32 r,
bytes32 s,
uint8 v,
bytes memory authenticatorData,
bytes memory clientDataPrefix,
bytes memory clientDataSuffix
) public pure returns (bytes memory signature) {
/*
signature layout:
1. r (32 bytes)
2. s (32 bytes)
3. v (1 byte) ---+
4. authenticatorData length (4 byte) |
5. clientDataPrefix length (4 byte) +--> 32 bytes
6. clientDataSuffix length (4 byte) |
7. gap (19 byte = 32-13) ---+
7. authenticatorData
8. clientDataPrefix
9. clientDataSuffix

*/
uint32 authenticatorDataLength = uint32(authenticatorData.length);
uint32 clientDataPrefixLength = uint32(bytes(clientDataPrefix).length);
uint32 clientDataSuffixLength = uint32(bytes(clientDataSuffix).length);
// {v}{authenticatorDataLength}{clientDataPrefix}{clientDataSuffix}00000000000000000000000000000000000000
bytes32 lenData = bytes32(
uint256(v) << 248 | uint256(authenticatorDataLength) << 216 | uint256(clientDataPrefixLength) << 184
| uint256(clientDataSuffixLength) << 152
);
signature = abi.encodePacked(r, s, lenData, authenticatorData, clientDataPrefix, clientDataSuffix);
}

// function DataTest() public pure returns (bytes memory, bytes memory) {
// string memory ClIENTDATA_PREFIX = "{\"type\":\"webauthn.get\",\"challenge\":\"";
// string memory clientDataSuffix = "\",\"origin\":\"http://localhost:5500\",\"crossOrigin\":false}";

// return (bytes(ClIENTDATA_PREFIX), bytes(clientDataSuffix));
// }

function unPackSignature(bytes calldata signature)
public
pure
Expand All @@ -109,25 +69,10 @@ contract TestWebAuthn {
}

function recoverTest(bytes32 userOpHash, bytes calldata signature) public view returns (bytes32) {
// signature:
// 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d61c0000002500000024000000370000000000000000000000000000000000000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d
// or
// 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d61c0000002500000000000000370000000000000000000000000000000000000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d
// bytes32 expected;
// {
// uint256 Qx = uint256(0xe89e8b4be943fadb4dc599fe2e8af87a79b438adde328a3b72d43324506cd5b6);
// uint256 Qy = uint256(0x4fbfe4a2f9934783c3b1af712ee87abc08f576e79346efc3b8355d931bd7b976);
// expected = keccak256(abi.encodePacked(Qx, Qy));
// }
//bytes32 userOpHash = 0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd;

bytes32 publicKey = WebAuthn.recover(userOpHash, signature);
if (publicKey == 0) {
revert("WebAuthn recover failed");
}
return publicKey;
// if (expected != publicKey) {
// revert("WebAuthn signature verification failed");
// }
}
}
35 changes: 17 additions & 18 deletions contracts/libraries/WebAuthn.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,39 +83,38 @@ library WebAuthn {
signature layout:
1. r (32 bytes)
2. s (32 bytes)
3. v (1 byte) ---+
4. authenticatorData length (4 byte) |
5. clientDataPrefix length (4 byte) +--> 32 bytes
6. clientDataSuffix length (4 byte) |
7. gap (19 byte = 32-13) ---+
7. authenticatorData
8. clientDataPrefix
9. clientDataSuffix
3. v (1 byte)
4. authenticatorData length (2 byte max 65535)
5. clientDataPrefix length (2 byte max 65535)
6. authenticatorData
7. clientDataPrefix
8. clientDataSuffix

*/
uint256 authenticatorDataLength;
uint256 clientDataPrefixLength;
uint256 clientDataSuffixLength;
assembly ("memory-safe") {
let calldataOffset := signature.offset
r := calldataload(calldataOffset)
s := calldataload(add(calldataOffset, 0x20))
let lengthData := calldataload(add(calldataOffset, 0x40))
v := shr(248, /* 8*31 */ lengthData)
authenticatorDataLength := and(shr(216, /* 27*8 */ lengthData), 0xffffffff)
clientDataPrefixLength := and(shr(184, /* 23*8 */ lengthData), 0xffffffff)
clientDataSuffixLength := and(shr(152, /* 19*8 */ lengthData), 0xffffffff)
let lengthData :=
and(
calldataload(add(calldataOffset, 0x25 /* 32+5 */ )),
0xffffffffff /* v+authenticatorDataLength+clientDataPrefixLength */
)
v := shr(0x20, /* 4*8 */ lengthData)
authenticatorDataLength := and(shr(0x10, /* 2*8 */ lengthData), 0xffff)
clientDataPrefixLength := and(lengthData, 0xffff)
}
unchecked {
uint256 _dataOffset1 = 0x60;
uint256 _dataOffset2 = 0x60 + authenticatorDataLength;
uint256 _dataOffset1 = 0x45; // 32+32+1+2+2
uint256 _dataOffset2 = 0x45 + authenticatorDataLength;
authenticatorData = signature[_dataOffset1:_dataOffset2];

_dataOffset1 = _dataOffset2 + clientDataPrefixLength;
clientDataPrefix = signature[_dataOffset2:_dataOffset1];

_dataOffset2 = _dataOffset1 + clientDataSuffixLength;
clientDataSuffix = signature[_dataOffset1:_dataOffset2];
clientDataSuffix = signature[_dataOffset1:];
}
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/validator/libraries/ValidatorSigDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ library ValidatorSigDecoder {
+----------------------------------------------------------------------------------------------------------------+
| | |
| | |
| 0x2 | passkey dynamic signature |
| 0x02 | passkey dynamic signature |
| | |
| | |
+-------------------+--------------------------------------------------------------------------------------------+
Expand All @@ -75,7 +75,7 @@ library ValidatorSigDecoder {
| | | |
+-----------------------------------------------------------------------------------------------------------------------------------+
| | | |
| 0x3 | uint256 | passkey dynamic signature |
| 0x03 | uint256 | passkey dynamic signature |
| | 32 bytes | |
+-----------------+--------------------+--------------------------------------------------------------------------------------------+

Expand Down
53 changes: 15 additions & 38 deletions test/libraries/WebAuthn.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,18 @@ contract WebAuthnTest is Test {
expected = keccak256(abi.encodePacked(Qx, Qy));
}
bytes32 userOpHash = 0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd;
// bytes memory sig =
// 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d61c0000002500000024000000370000000000000000000000000000000000000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d;
bytes memory sig;
assembly {
// store 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d61c0000002500000024000000370000000000000000000000000000000000000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d
let ptr := mload(0x40)
mstore(0x40, add(ptr, mul(8, 0x20)))
mstore(ptr, mul(7, 0x20))
/*
2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a
87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6
1c00000025000000240000003700000000000000000000000000000000000000
49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763
05000000007b2274797065223a22776562617574686e2e676574222c22636861
6c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f6361
6c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d
*/
mstore(add(ptr, 0x20), 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a)
mstore(add(ptr, 0x40), 0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6)
mstore(add(ptr, 0x60), 0x1c00000025000000240000003700000000000000000000000000000000000000)
mstore(add(ptr, 0x80), 0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763)
mstore(add(ptr, 0xa0), 0x05000000007b2274797065223a22776562617574686e2e676574222c22636861)
mstore(add(ptr, 0xc0), 0x6c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f6361)
mstore(add(ptr, 0xe0), 0x6c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d)
mstore(ptr, 0xc5) // signature length
mstore(add(ptr, 0x20), 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a) // r
mstore(add(ptr, 0x40), 0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6) // s
mstore(add(ptr, 0x60), 0x1c0025002449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3) // 0x1c00250024: v=0x1c, authenticatorDataLength=0x25, clientDataPrefixLength=0x24
mstore(add(ptr, 0x80), 0xba831d976305000000007b2274797065223a22776562617574686e2e67657422)
mstore(add(ptr, 0xa0), 0x2c226368616c6c656e6765223a22222c226f726967696e223a22687474703a2f)
mstore(add(ptr, 0xc0), 0x2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66)
mstore(add(ptr, 0xe0), 0x616c73657d000000000000000000000000000000000000000000000000000000)
sig := ptr
}
bytes32 publicKey = testWebAuthn.recoverTest(userOpHash, sig);
Expand All @@ -61,29 +49,18 @@ contract WebAuthnTest is Test {
expected = keccak256(abi.encodePacked(Qx, Qy));
}
bytes32 userOpHash = 0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd;
// bytes memory sig =
// "0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d61c0000002500000000000000370000000000000000000000000000000000000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d";

bytes memory sig;
assembly {
// store 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d61c0000002500000000000000370000000000000000000000000000000000000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d97630500000000222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d
let ptr := mload(0x40)
mstore(0x40, add(ptr, mul(7, 0x20)))
mstore(ptr, 0xbc)
/*
2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a
87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6
1c00000025000000000000003700000000000000000000000000000000000000
49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763
0500000000222c226f726967696e223a22687474703a2f2f6c6f63616c686f73
743a35353030222c2263726f73734f726967696e223a66616c73657d00000000
*/
mstore(add(ptr, 0x20), 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a)
mstore(add(ptr, 0x40), 0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6)
mstore(add(ptr, 0x60), 0x1c00000025000000000000003700000000000000000000000000000000000000)
mstore(add(ptr, 0x80), 0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763)
mstore(add(ptr, 0xa0), 0x0500000000222c226f726967696e223a22687474703a2f2f6c6f63616c686f73)
mstore(add(ptr, 0xc0), 0x743a35353030222c2263726f73734f726967696e223a66616c73657d00000000)
mstore(ptr, 0xa1) // signature length
mstore(add(ptr, 0x20), 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a) // r
mstore(add(ptr, 0x40), 0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6) // s
mstore(add(ptr, 0x60), 0x1c0025000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3) // 0x1c00250000: v=0x1c, authenticatorDataLength=0x25, clientDataPrefixLength=0x00
mstore(add(ptr, 0x80), 0xba831d97630500000000222c226f726967696e223a22687474703a2f2f6c6f63)
mstore(add(ptr, 0xa0), 0x616c686f73743a35353030222c2263726f73734f726967696e223a66616c7365)
mstore(add(ptr, 0xc0), 0x7d00000000000000000000000000000000000000000000000000000000000000)
sig := ptr
}
bytes32 publicKey = testWebAuthn.recoverTest(userOpHash, sig);
Expand Down
26 changes: 8 additions & 18 deletions test/validator/DefaultValidator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,26 +122,16 @@ contract ValidatorSigDecoderTest is Test {
}
bytes memory sig;
assembly {
// store 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d61c0000002500000024000000370000000000000000000000000000000000000049960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976305000000007b2274797065223a22776562617574686e2e676574222c226368616c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d
let ptr := mload(0x40)
mstore(0x40, add(ptr, mul(8, 0x20)))
mstore(ptr, mul(7, 0x20))
/*
2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a
87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6
1c00000025000000240000003700000000000000000000000000000000000000
49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763
05000000007b2274797065223a22776562617574686e2e676574222c22636861
6c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f6361
6c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d
*/
mstore(add(ptr, 0x20), 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a)
mstore(add(ptr, 0x40), 0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6)
mstore(add(ptr, 0x60), 0x1c00000025000000240000003700000000000000000000000000000000000000)
mstore(add(ptr, 0x80), 0x49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763)
mstore(add(ptr, 0xa0), 0x05000000007b2274797065223a22776562617574686e2e676574222c22636861)
mstore(add(ptr, 0xc0), 0x6c6c656e6765223a22222c226f726967696e223a22687474703a2f2f6c6f6361)
mstore(add(ptr, 0xe0), 0x6c686f73743a35353030222c2263726f73734f726967696e223a66616c73657d)
mstore(ptr, 0xc5) // signature length
mstore(add(ptr, 0x20), 0x2ae3ddfe4cc414dc0fad7ff3a5c960d1cee1211722d3099ade76e5ac1826731a) // r
mstore(add(ptr, 0x40), 0x87e5d654f357e4cd6cb52512b2da4d91eae0ae48e9d892ce532b9352f63a55d6) // s
mstore(add(ptr, 0x60), 0x1c0025002449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3) // 0x1c00250024: v=0x1c, authenticatorDataLength=0x25, clientDataPrefixLength=0x24
mstore(add(ptr, 0x80), 0xba831d976305000000007b2274797065223a22776562617574686e2e67657422)
mstore(add(ptr, 0xa0), 0x2c226368616c6c656e6765223a22222c226f726967696e223a22687474703a2f)
mstore(add(ptr, 0xc0), 0x2f6c6f63616c686f73743a35353030222c2263726f73734f726967696e223a66)
mstore(add(ptr, 0xe0), 0x616c73657d000000000000000000000000000000000000000000000000000000)
sig := ptr
}
bytes32 userOpHash = 0x83714056da6e6910b51595330c2c2cdfbf718f2deff5bdd84b95df7a7f36f6dd;
Expand Down