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

Feature/lit 3379 investigate bls signed session bug #500

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { AuthSig } from '@lit-protocol/types';
import { uint8arrayToString } from '@lit-protocol/uint8arrays';
import { ethers } from 'ethers';

const LIT_SESSION_SIGNED_MESSAGE_PREFIX = 'lit_session:';
joshLong145 marked this conversation as resolved.
Show resolved Hide resolved
joshLong145 marked this conversation as resolved.
Show resolved Hide resolved

/**
* Verifies a BLS session signature.
*
* @param {Function} verifier - A wasm function that takes a public key, message, and signature to verify. NOTE: `public_key` is snake cased because it's a wasm parameter
* @param {string} networkPubKey - The public key of the network.
* @param {AuthSig} authSig
* @typedef {Object} AuthSig
* @property {string} sig - The signature in string format.
* @property {string} signedMessage - The message that was signed.
*/
export const blsSessionSigVerify = (
joshLong145 marked this conversation as resolved.
Show resolved Hide resolved
// TODO: refactor type with merger of PR 'https://github.com/LIT-Protocol/js-sdk/pull/503`
verifier: (public_key: any, message: any, signature: any) => void,
networkPubKey: string,
authSig: AuthSig
): void => {
let sigJson = JSON.parse(authSig.sig);
// we do not nessesarly need to use ethers here but was a quick way
// to get verification working.
const eip191Hash = ethers.utils.hashMessage(authSig.signedMessage);
const prefixedStr =
LIT_SESSION_SIGNED_MESSAGE_PREFIX + eip191Hash.replace('0x', '');
const prefixedEncoded = ethers.utils.toUtf8Bytes(prefixedStr);
const shaHashed = ethers.utils.base64.encode(
ethers.utils.sha256(prefixedEncoded)
);
const signatureBytes = Buffer.from(sigJson.ProofOfPossession, `hex`);

verifier(
networkPubKey,
shaHashed,
uint8arrayToString(signatureBytes, `base64`)
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { blsSessionSigVerify } from './validate-bls-session-sig';

describe('BlsSessionSigVerify', () => {
const authSig = {
sig: '{"ProofOfPossession":"ae925162cecb2f572fa76b93372dbbaee0133e89987c33d3210e0d62ca2dd5bf080dbdabb0155e61e770be1a2a629861073acc58fbc16cb6b700088d2aff114c42337c6123c8d15eeee63b522ea7d9c8f44390d3cb7b26e8d4935a283fe72a5d"}',
algo: 'LIT_BLS',
derivedVia: 'lit.bls',
signedMessage:
'litprotocol.com wants you to sign in with your Ethereum account:\n' +
'0xf087a967D9eA9445D9182692C2944DcC0Af57341\n' +
'\n' +
"Lit Protocol PKP session signature I further authorize the stated URI to perform the following actions on my behalf: I further authorize the stated URI to perform the following actions on my behalf: (1) 'Threshold': 'Execution' for 'lit-litaction://*'. (2) 'Threshold': 'Signing' for 'lit-pkp://*'. I further authorize the stated URI to perform the following actions on my behalf: (1) 'Threshold': 'Execution' for 'lit-litaction://*'. (2) 'Threshold': 'Signing' for 'lit-pkp://*'. (3) 'Auth': 'Auth' for 'lit-resolvedauthcontext://*'.\n" +
'\n' +
'URI: lit:session:efebafcc9063827a49dffdb11c36b2d64a33330631ac7f5825e2960946bcc8ff\n' +
'Version: 1\n' +
'Chain ID: 1\n' +
'Nonce: 0x1f623ab8dfe6bbd3b3dc22c7a041deb697c14817bce471b1bd1d86a25d5a319c\n' +
'Issued At: 2024-06-11T15:55:23Z\n' +
'Expiration Time: 2024-06-12T15:55:47.655Z\n' +
'Resources:\n' +
'- urn:recap:eyJhdHQiOnsibGl0LWxpdGFjdGlvbjovLyoiOnsiVGhyZXNob2xkL0V4ZWN1dGlvbiI6W3t9XX0sImxpdC1wa3A6Ly8qIjp7IlRocmVzaG9sZC9TaWduaW5nIjpbe31dfSwibGl0LXJlc29sdmVkYXV0aGNvbnRleHQ6Ly8qIjp7IkF1dGgvQXV0aCI6W3siYXV0aF9jb250ZXh0Ijp7ImFjdGlvbklwZnNJZHMiOlsiUW1ZM3F1bjlxWDNmVUJIVmZyQTlmM3Y5UnB5eVBvOFJIRXVFTjFYWVBxMVByQSJdLCJhdXRoTWV0aG9kQ29udGV4dHMiOlt7ImFwcElkIjoibGl0IiwiYXV0aE1ldGhvZFR5cGUiOjEsImV4cGlyYXRpb24iOjE3MTgyMDc3MzgsInVzZWRGb3JTaWduU2Vzc2lvbktleVJlcXVlc3QiOnRydWUsInVzZXJJZCI6IjB4NjEwM2U1MGUyQzA0OWM5MjgxNEE1Mjc1YURDZDlBNzE2NjY3OTUxZSJ9XSwiYXV0aFNpZ0FkZHJlc3MiOm51bGwsImN1c3RvbUF1dGhSZXNvdXJjZSI6InRydWUiLCJyZXNvdXJjZXMiOltdfX1dfX0sInByZiI6W119',
address: '0xf087a967D9eA9445D9182692C2944DcC0Af57341',
};

let networkPubKey =
'a43499a4b786da2dd28af9f209eb152ff6f646b34b68a02954967271e17fb4c511fd67b81e067f690c6f38acab70585d';

it(`should verify valid bls signatrue`, () => {
expect(
blsSessionSigVerify(
(public_key: any, message: any, signature: any) => {
expect(typeof public_key).toBe('string');
expect(typeof message).toBe('string');
expect(typeof signature).toBe('string');
},
networkPubKey,
authSig
)
).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ import { normalizeArray } from './helpers/normalize-array';
import { parsePkpSignResponse } from './helpers/parse-pkp-sign-response';
import { getBlsSignatures } from './helpers/get-bls-signatures';
import { processLitActionResponseStrategy } from './helpers/process-lit-action-response-strategy';
import { blsSessionSigVerify } from './helpers/validate-bls-session-sig';

export class LitNodeClientNodeJs
extends LitCore
Expand Down Expand Up @@ -512,17 +513,38 @@ export class LitNodeClientNodeJs
resourceAbilityRequests: LitResourceAbilityRequest[];
}): Promise<boolean> => {
const authSigSiweMessage = new SiweMessage(authSig.signedMessage);

try {
await authSigSiweMessage.validate(authSig.sig);
} catch (e) {
console.debug('Need retry because verify failed', e);
return true;
// We will either have `ed25519` or `LIT_BLS` as we have deviated from the specification of SIWE and use BLS signatures in some cases
// Here we need to check the `algo` of the SIWE to confirm we can validate the signature as if we attempt to validate the BLS signature here
// it will fail. If the algo is not defined we can assume that it was an EOA wallet signing the message so we can use SIWE.
if (authSig.algo === `ed25519` || authSig.algo === undefined) {
try {
await authSigSiweMessage.validate(authSig.sig);
} catch (e) {
log(`Error while verifying ECDSA signature: `, e);
return true;
joshLong145 marked this conversation as resolved.
Show resolved Hide resolved
}
} else if (authSig.algo === `LIT_BLS`) {
try {
blsSessionSigVerify(
blsSdk.verify_signature,
joshLong145 marked this conversation as resolved.
Show resolved Hide resolved
this.networkPubKey!,
authSig
);
} catch (e) {
log(`Error while verifying bls signature: `, e);
return true;
}
} else {
throwError({
message: `Unsupported signature algo for session signature. Expected ed25519 or LIT_BLS recieved ${authSig.algo}`,
errorKind: LIT_ERROR.SIGNATURE_VALIDATION_ERROR.kind,
errorCode: LIT_ERROR.SIGNATURE_VALIDATION_ERROR.code,
});
}
joshLong145 marked this conversation as resolved.
Show resolved Hide resolved

// make sure the sig is for the correct session key
if (authSigSiweMessage.uri !== sessionKeyUri) {
console.debug('Need retry because uri does not match');
log('Need retry because uri does not match');
return true;
}

Expand All @@ -531,7 +553,7 @@ export class LitNodeClientNodeJs
!authSigSiweMessage.resources ||
authSigSiweMessage.resources.length === 0
) {
console.debug('Need retry because empty resources');
log('Need retry because empty resources');
return true;
}

Expand All @@ -550,7 +572,7 @@ export class LitNodeClientNodeJs
resourceAbilityRequest.ability
)
) {
console.debug('Need retry because capabilities do not match', {
log('Need retry because capabilities do not match', {
authSigSessionCapabilityObject,
resourceAbilityRequest,
});
Expand Down Expand Up @@ -1881,6 +1903,7 @@ export class LitNodeClientNodeJs

log(`[signSessionKey] signatureShares:`, signatureShares);

// TODO: refactor type with merger of PR 'https://github.com/LIT-Protocol/js-sdk/pull/503`
const blsCombinedSignature = blsSdk.combine_signature_shares(
signatureShares.map((s) => JSON.stringify(s))
);
Expand Down
Loading