Skip to content

Commit

Permalink
Merge pull request #122 from jayden-sudo/develop
Browse files Browse the repository at this point in the history
Added recoverWebAuthNPublicKey() function
  • Loading branch information
jayden-sudo authored Oct 27, 2023
2 parents 51670d8 + e66af6c commit 562e79e
Show file tree
Hide file tree
Showing 13 changed files with 512 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-turkeys-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@soulwallet/sdk": patch
---

Added recoverWebAuthNPublicKey() function
12 changes: 12 additions & 0 deletions packages/soulwallet-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# @soulwallet/sdk

## 0.1.10

### Patch Changes

- Fix bug

## 0.1.9

### Patch Changes

- add securityControlModuleDelay argument

## 0.1.8

### Patch Changes
Expand Down
81 changes: 70 additions & 11 deletions packages/soulwallet-sdk/__tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
randomBytes
} from 'crypto';


describe('SDK', () => {
test('P256Lib', () => {
//uint256 Qx = uint256(0xe89e8b4be943fadb4dc599fe2e8af87a79b438adde328a3b72d43324506cd5b6);
Expand Down Expand Up @@ -121,19 +120,29 @@ describe('SDK', () => {
test('passkey-test', async () => {
for (let index = 0; index < 10; index++) {
const { privateKeyBase64, publicKeyBase64, X, Y } = await WebAuthNMock.createPassKey();
for (let j = 0; j < 5; j++) {
const userOpHash = '0x' + randomBytes(32).toString('hex');
const { r, s, authenticatorData, clientDataSuffix, _message } = await WebAuthNMock.signPassKey(privateKeyBase64, publicKeyBase64, userOpHash);

const p = WebAuthN.recoverWebAuthN(userOpHash, r, s, authenticatorData, clientDataSuffix);

const userOpHash = '0x' + randomBytes(32).toString('hex');
const { r, s, authenticatorData, clientDataSuffix } = await WebAuthNMock.signPassKey(privateKeyBase64, publicKeyBase64, userOpHash);
if (BigInt(p[0].x) === X && BigInt(p[0].y) === Y) {
// succ
} else if (BigInt(p[1].x) === X && BigInt(p[1].y) === Y) {
// succ
} else {
throw new Error('recover failed');
}

const p = WebAuthN.recoverWebAuthN(userOpHash, r, s, authenticatorData, clientDataSuffix);
if (BigInt(p[0].x) === X && BigInt(p[0].y) === Y) {
// succ
} else if (BigInt(p[1].x) === X && BigInt(p[1].y) === Y) {
// succ
} else {
throw new Error('recover failed');
if (P256Lib.verify(BigInt(p[0].x), BigInt(p[0].y), BigInt(_message), BigInt(r), BigInt(s)) === false) {
throw new Error('verify failed');
}
if (P256Lib.verify(BigInt(p[1].x), BigInt(p[1].y), BigInt(_message), BigInt(r), BigInt(s)) === false) {
throw new Error('verify failed');
}
}
}
console.log('passkey-test 1000 times succ');
});
});

Expand Down Expand Up @@ -172,6 +181,17 @@ class WebAuthNMock {
private static uint8ArrayToHex(uint8Array: Uint8Array): string {
return '0x' + Array.from(uint8Array).map(b => b.toString(16).padStart(2, '0')).join('');
}

private static hexToUint8Array(hex: string): Uint8Array {
if (hex.startsWith('0x')) hex = hex.slice(2);
const len = hex.length;
const uint8Array = new Uint8Array(len / 2);
for (let i = 0; i < len; i += 2) {
uint8Array[i / 2] = parseInt(hex.substring(i, i + 2), 16);
}
return uint8Array;
}

private static arrayBufferToBase64(buffer: ArrayBuffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
Expand Down Expand Up @@ -202,13 +222,51 @@ class WebAuthNMock {
// keyPair.publicKey to x,y
const publicKeyBuffer = await webcrypto.subtle.exportKey('spki', keyPair.publicKey);
if (publicKeyBuffer.byteLength === 91) {
// ASN.1 DER format
const _buffer: Uint8Array = new Uint8Array(publicKeyBuffer);
if (_buffer[26] !== 0x04) {
throw new Error('Unexpected public key format');
}
const x = publicKeyBuffer.slice(27, 59);
const y = publicKeyBuffer.slice(59, 91);
X = BigInt(WebAuthNMock.uint8ArrayToHex(new Uint8Array(x)));
Y = BigInt(WebAuthNMock.uint8ArrayToHex(new Uint8Array(y)));
} else {
throw new Error('Unexpected public key format');
}
/*
{
const _pk_x = WebAuthNMock.hexToUint8Array(X.toString(16));
const _pk_y = WebAuthNMock.hexToUint8Array(Y.toString(16));
const _buffer1 = [48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, 3, 66, 0, 4];
const _buffer: Uint8Array = new Uint8Array(91);
for (let i = 0; i < 27; i++) {
_buffer[i] = _buffer1[i];
}
for (let i = 0; i < _pk_x.length; i++) {
_buffer[58 - i] = _pk_x[_pk_x.length - 1 - i];
}
for (let i = 0; i < _pk_y.length; i++) {
_buffer[59 - i] = _pk_y[_pk_y.length - 1 - i];
}
const p1 = new Uint8Array(publicKeyBuffer).toString();
const p2 = _buffer.toString();
if (p1 !== p2) {
throw new Error('Unexpected public key format');
}
// const publicKey: webcrypto.CryptoKey = await webcrypto.subtle.importKey(
// 'spki',
// _buffer.buffer,
// algoParams,
// true,
// ['verify']
// );
}
*/


const privateKeyBase64 = WebAuthNMock.arrayBufferToBase64(await webcrypto.subtle.exportKey('pkcs8', keyPair.privateKey));
Expand Down Expand Up @@ -275,7 +333,8 @@ class WebAuthNMock {
r,
s,
authenticatorData,
clientDataSuffix
clientDataSuffix,
_message
}
}
}
5 changes: 4 additions & 1 deletion packages/soulwallet-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
{
"name": "@soulwallet/sdk",
"version": "0.1.8",
"version": "0.1.10",
"description": "SoulWalletSDK = { Basic Functions, Bundler, KeyStore, SignatureTools }",
"author": "Jayden@SoulWallet",
"homepage": "https://github.com/SoulWallet/soulwalletlib#readme",
"license": "ISC",
"main": "./lib.cjs/main.js",
"module": "./lib.esm/main.js",
"types": "./lib.esm/main.d.ts",
"browser": {
"./lib.esm/tools/webCrypto.js": "./lib.esm/tools/webCrypto-browser.js"
},
"directories": {
"lib": "lib.cjs",
"test": "__tests__"
Expand Down
9 changes: 7 additions & 2 deletions packages/soulwallet-sdk/src/L1KeyStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,13 @@ export class L1KeyStore implements IL1KeyStore {
const _initialKeys: string[] = [];
for (const oneKey of initialKeys) {
if (typeof oneKey === 'string') {
if (TypeGuard.onlyAddress(oneKey).isErr() === true) { throw new Error(`invalid key: ${oneKey}`); }
_initialKeys.push(Hex.paddingZero(oneKey, 32));
if (TypeGuard.onlyAddress(oneKey).isOk() === true) {
_initialKeys.push(Hex.paddingZero(oneKey, 32));
} else if (TypeGuard.onlyBytes32(oneKey).isOk() === true) {
_initialKeys.push(oneKey);
} else {
throw new Error(`invalid key: ${oneKey}`);
}
} else {
if (TypeGuard.onlyBytes32(oneKey.x).isErr() === true) { throw new Error(`invalid key.x: ${oneKey.x}`); }
if (TypeGuard.onlyBytes32(oneKey.y).isErr() === true) { throw new Error(`invalid key.y: ${oneKey.y}`); }
Expand Down
2 changes: 1 addition & 1 deletion packages/soulwallet-sdk/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ export {
ECCPoint,
SignkeyType,
Base64Url,
WebAuthN,/* dev export */
WebAuthN,
P256Lib/* dev export */
}
20 changes: 12 additions & 8 deletions packages/soulwallet-sdk/src/soulWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { UserOpErrors, UserOpErrorCodes } from "./interface/IUserOpErrors.js";
import { Bundler } from "./bundler.js";
import { Ok, Err, Result } from '@soulwallet/result';
import { getUserOpHash } from "./tools/userOpHash.js";
import { ECCPoint } from "./tools/webauthn.js";
import { L1KeyStore } from "./L1KeyStore.js";

export class onChainConfig {
Expand Down Expand Up @@ -173,7 +172,12 @@ export class SoulWallet implements ISoulWallet {
return new Ok(_onChainConfig.OK.entryPoint);
}

async initializeData(initialKeys: InitialKey[], initialGuardianHash: string, initialGuardianSafePeriod: number = L1KeyStore.defalutInitialGuardianSafePeriod): Promise<Result<string, Error>> {
async initializeData(
initialKeys: InitialKey[],
initialGuardianHash: string,
initialGuardianSafePeriod: number = L1KeyStore.defalutInitialGuardianSafePeriod,
securityControlModuleDelay: number = L1KeyStore.defalutInitialGuardianSafePeriod
): Promise<Result<string, Error>> {
/*
function initialize(
bytes32[] anOwner,
Expand All @@ -183,11 +187,11 @@ export class SoulWallet implements ISoulWallet {
)
*/

const initalkeys = L1KeyStore.initialKeysToAddress(initialKeys);
const initialKeyHash = L1KeyStore.getKeyHash(initalkeys);
const _initalkeys = L1KeyStore.initialKeysToAddress(initialKeys);
const initialKeyHash = L1KeyStore.getKeyHash(_initalkeys);

// default dely time is 2 days
const securityControlModuleAndData = (this.securityControlModuleAddress + Hex.paddingZero(initialGuardianSafePeriod, 32).substring(2)).toLowerCase();
const securityControlModuleAndData = (this.securityControlModuleAddress + Hex.paddingZero(securityControlModuleDelay, 32).substring(2)).toLowerCase();
/*
(bytes32 initialKey, bytes32 initialGuardianHash, uint64 guardianSafePeriod) = abi.decode(_data, (bytes32, bytes32, uint64));
*/
Expand All @@ -200,7 +204,7 @@ export class SoulWallet implements ISoulWallet {
}
const _soulWallet = new ethers.Contract(_onChainConfig.OK.soulWalletLogic, ABI_SoulWallet, this.provider);
const initializeData = _soulWallet.interface.encodeFunctionData("initialize", [
initalkeys,
_initalkeys,
this.defalutCallbackHandlerAddress,
[
securityControlModuleAndData,
Expand Down Expand Up @@ -462,7 +466,7 @@ export class SoulWallet implements ISoulWallet {
*
* @param {{
* messageHash:string,
* publicKey: ECCPoint,
* publicKey: InitialKey,
* r: string,
* s: string,
* authenticatorData: string,
Expand All @@ -475,7 +479,7 @@ export class SoulWallet implements ISoulWallet {
*/
async packUserOpP256Signature(signatureData: {
messageHash: string,
publicKey: ECCPoint,
publicKey: InitialKey,
r: string,
s: string,
authenticatorData: string,
Expand Down
11 changes: 11 additions & 0 deletions packages/soulwallet-sdk/src/tools/hex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,15 @@ export class Hex {
public static uint8ArrayToHex(uint8Array: Uint8Array): string {
return '0x' + Array.from(uint8Array).map(b => b.toString(16).padStart(2, '0')).join('');
}

public static hexToUint8Array(hex: string): Uint8Array {
if (hex.startsWith('0x')) hex = hex.slice(2);
const len = hex.length;
const uint8Array = new Uint8Array(len / 2);
for (let i = 0; i < len; i += 2) {
uint8Array[i / 2] = parseInt(hex.substring(i, i + 2), 16);
}
return uint8Array;
}

}
Loading

0 comments on commit 562e79e

Please sign in to comment.