Skip to content

Commit 94c091a

Browse files
authored
Merge branch 'master' into anshs
2 parents c235094 + 498ce42 commit 94c091a

11 files changed

+178
-17
lines changed

local-tests/tests/testDelegatingCapacityCreditsNFTToAnotherWalletToPkpSign.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ethers } from 'ethers';
2+
13
import { getEoaSessionSigsWithCapacityDelegations } from 'local-tests/setup/session-sigs/get-eoa-session-sigs';
24
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
35

@@ -92,5 +94,21 @@ export const testDelegatingCapacityCreditsNFTToAnotherWalletToPkpSign = async (
9294
throw new Error(`Expected "recid" to be parseable as a number`);
9395
}
9496

97+
const signature = ethers.utils.joinSignature({
98+
r: '0x' + res.r,
99+
s: '0x' + res.s,
100+
recoveryParam: res.recid,
101+
});
102+
const recoveredPubKey = ethers.utils.recoverPublicKey(
103+
alice.loveLetter,
104+
signature
105+
);
106+
if (recoveredPubKey !== `0x${res.publicKey.toLowerCase()}`) {
107+
throw new Error(`Expected recovered public key to match res.publicKey`);
108+
}
109+
if (recoveredPubKey !== `0x${bob.pkp.publicKey.toLowerCase()}`) {
110+
throw new Error(`Expected recovered public key to match bob.pkp.publicKey`);
111+
}
112+
95113
console.log('✅ res:', res);
96114
};

local-tests/tests/testUseCapacityDelegationAuthSigWithUnspecifiedCapacityTokenIdToPkpSign.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ethers } from 'ethers';
2+
13
import { getEoaSessionSigsWithCapacityDelegations } from 'local-tests/setup/session-sigs/get-eoa-session-sigs';
24
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
35

@@ -94,5 +96,23 @@ export const testUseCapacityDelegationAuthSigWithUnspecifiedCapacityTokenIdToPkp
9496
throw new Error(`Expected "recid" to be parseable as a number`);
9597
}
9698

99+
const signature = ethers.utils.joinSignature({
100+
r: '0x' + res.r,
101+
s: '0x' + res.s,
102+
recoveryParam: res.recid,
103+
});
104+
const recoveredPubKey = ethers.utils.recoverPublicKey(
105+
alice.loveLetter,
106+
signature
107+
);
108+
if (recoveredPubKey !== `0x${res.publicKey.toLowerCase()}`) {
109+
throw new Error(`Expected recovered public key to match res.publicKey`);
110+
}
111+
if (recoveredPubKey !== `0x${bob.pkp.publicKey.toLowerCase()}`) {
112+
throw new Error(
113+
`Expected recovered public key to match bob.pkp.publicKey`
114+
);
115+
}
116+
97117
console.log('✅ res:', res);
98118
};

local-tests/tests/testUseCapacityDelegationAuthSigWithUnspecifiedDelegateesToPkpSign.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ethers } from 'ethers';
2+
13
import { getEoaSessionSigsWithCapacityDelegations } from 'local-tests/setup/session-sigs/get-eoa-session-sigs';
24
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
35

@@ -86,6 +88,26 @@ export const testUseCapacityDelegationAuthSigWithUnspecifiedDelegateesToPkpSign
8688
throw new Error(`Expected "signature" to start with 0x`);
8789
}
8890

91+
const signature = ethers.utils.joinSignature({
92+
r: '0x' + runWithSessionSigs.r,
93+
s: '0x' + runWithSessionSigs.s,
94+
recoveryParam: runWithSessionSigs.recid,
95+
});
96+
const recoveredPubKey = ethers.utils.recoverPublicKey(
97+
alice.loveLetter,
98+
signature
99+
);
100+
if (recoveredPubKey !== `0x${runWithSessionSigs.publicKey.toLowerCase()}`) {
101+
throw new Error(
102+
`Expected recovered public key to match runWithSessionSigs.publicKey`
103+
);
104+
}
105+
if (recoveredPubKey !== `0x${bob.pkp.publicKey.toLowerCase()}`) {
106+
throw new Error(
107+
`Expected recovered public key to match bob.pkp.publicKey`
108+
);
109+
}
110+
89111
// recid must be parseable as a number
90112
if (isNaN(runWithSessionSigs.recid)) {
91113
throw new Error(`Expected "recid" to be parseable as a number`);

local-tests/tests/testUseEoaSessionSigsToPkpSign.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ethers } from 'ethers';
2+
13
import { log } from '@lit-protocol/misc';
24
import { getEoaSessionSigs } from 'local-tests/setup/session-sigs/get-eoa-session-sigs';
35
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
@@ -58,5 +60,25 @@ export const testUseEoaSessionSigsToPkpSign = async (
5860
throw new Error(`Expected "recid" to be parseable as a number`);
5961
}
6062

63+
const signature = ethers.utils.joinSignature({
64+
r: '0x' + runWithSessionSigs.r,
65+
s: '0x' + runWithSessionSigs.s,
66+
recoveryParam: runWithSessionSigs.recid,
67+
});
68+
const recoveredPubKey = ethers.utils.recoverPublicKey(
69+
alice.loveLetter,
70+
signature
71+
);
72+
if (recoveredPubKey !== `0x${runWithSessionSigs.publicKey.toLowerCase()}`) {
73+
throw new Error(
74+
`Expected recovered public key to match runWithSessionSigs.publicKey`
75+
);
76+
}
77+
if (recoveredPubKey !== `0x${alice.pkp.publicKey.toLowerCase()}`) {
78+
throw new Error(
79+
`Expected recovered public key to match alice.pkp.publicKey`
80+
);
81+
}
82+
6183
log('✅ testUseEoaSessionSigsToPkpSign');
6284
};

local-tests/tests/testUsePkpSessionSigsToPkpSign.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ethers } from 'ethers';
2+
13
import { log } from '@lit-protocol/misc';
24
import { getPkpSessionSigs } from 'local-tests/setup/session-sigs/get-pkp-session-sigs';
35
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
@@ -58,5 +60,25 @@ export const testUsePkpSessionSigsToPkpSign = async (
5860
throw new Error(`Expected "recid" to be parseable as a number`);
5961
}
6062

63+
const signature = ethers.utils.joinSignature({
64+
r: '0x' + res.r,
65+
s: '0x' + res.s,
66+
recoveryParam: res.recid,
67+
});
68+
const recoveredPubKey = ethers.utils.recoverPublicKey(
69+
alice.loveLetter,
70+
signature
71+
);
72+
if (recoveredPubKey !== `0x${res.publicKey.toLowerCase()}`) {
73+
throw new Error(`Expected recovered public key to match res.publicKey`);
74+
}
75+
if (
76+
recoveredPubKey !== `0x${alice.authMethodOwnedPkp.publicKey.toLowerCase()}`
77+
) {
78+
throw new Error(
79+
`Expected recovered public key to match alice.authMethodOwnedPkp.publicKey`
80+
);
81+
}
82+
6183
log('✅ res:', res);
6284
};

local-tests/tests/testUseValidLitActionCodeGeneratedSessionSigsToPkpSign.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ethers } from 'ethers';
2+
13
import { log } from '@lit-protocol/misc';
24
import { LIT_NETWORK } from '@lit-protocol/constants';
35
import { getLitActionSessionSigs } from 'local-tests/setup/session-sigs/get-lit-action-session-sigs';
@@ -58,5 +60,25 @@ export const testUseValidLitActionCodeGeneratedSessionSigsToPkpSign = async (
5860
throw new Error(`Expected "recid" to be parseable as a number`);
5961
}
6062

63+
const signature = ethers.utils.joinSignature({
64+
r: '0x' + res.r,
65+
s: '0x' + res.s,
66+
recoveryParam: res.recid,
67+
});
68+
const recoveredPubKey = ethers.utils.recoverPublicKey(
69+
alice.loveLetter,
70+
signature
71+
);
72+
if (recoveredPubKey !== `0x${res.publicKey.toLowerCase()}`) {
73+
throw new Error(`Expected recovered public key to match res.publicKey`);
74+
}
75+
if (
76+
recoveredPubKey !== `0x${alice.authMethodOwnedPkp.publicKey.toLowerCase()}`
77+
) {
78+
throw new Error(
79+
`Expected recovered public key to match alice.authMethodOwnedPkp.publicKey`
80+
);
81+
}
82+
6183
log('✅ res:', res);
6284
};

local-tests/tests/testUseValidLitActionIpfsCodeGeneratedSessionSigsToPkpSign.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { ethers } from 'ethers';
2+
13
import { log } from '@lit-protocol/misc';
24
import { getLitActionSessionSigsUsingIpfsId } from 'local-tests/setup/session-sigs/get-lit-action-session-sigs';
35
import { TinnyEnvironment } from 'local-tests/setup/tinny-environment';
@@ -66,5 +68,26 @@ export const testUseValidLitActionIpfsCodeGeneratedSessionSigsToPkpSign =
6668
throw new Error(`Expected "recid" to be parseable as a number`);
6769
}
6870

71+
const signature = ethers.utils.joinSignature({
72+
r: '0x' + res.r,
73+
s: '0x' + res.s,
74+
recoveryParam: res.recid,
75+
});
76+
const recoveredPubKey = ethers.utils.recoverPublicKey(
77+
alice.loveLetter,
78+
signature
79+
);
80+
if (recoveredPubKey !== `0x${res.publicKey.toLowerCase()}`) {
81+
throw new Error(`Expected recovered public key to match res.publicKey`);
82+
}
83+
if (
84+
recoveredPubKey !==
85+
`0x${alice.authMethodOwnedPkp.publicKey.toLowerCase()}`
86+
) {
87+
throw new Error(
88+
`Expected recovered public key to match alice.authMethodOwnedPkp.publicKey`
89+
);
90+
}
91+
6992
log('✅ res:', res);
7093
};

packages/crypto/src/lib/crypto.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,20 @@ export const combineEcdsaShares = async (
202202
Buffer.from(share.signatureShare, 'hex')
203203
);
204204

205-
const [r, s, v] = await ecdsaCombine(variant!, presignature, signatureShares);
205+
const [r, s, recId] = await ecdsaCombine(
206+
variant!,
207+
presignature,
208+
signatureShares
209+
);
206210

207211
const publicKey = Buffer.from(anyValidShare.publicKey, 'hex');
208212
const messageHash = Buffer.from(anyValidShare.dataSigned!, 'hex');
209213

210-
await ecdsaVerify(variant!, messageHash, publicKey, [r, s, v]);
214+
await ecdsaVerify(variant!, messageHash, publicKey, [r, s, recId]);
211215

212-
const signature = splitSignature(Buffer.concat([r, s, Buffer.from([v])]));
216+
const signature = splitSignature(
217+
Buffer.concat([r, s, Buffer.from([recId + 27])])
218+
);
213219

214220
return {
215221
r: signature.r.slice('0x'.length),

packages/lit-node-client-nodejs/src/lib/helpers/get-signatures.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ export const getSignatures = async <T>(params: {
259259
const encodedSig = joinSignature({
260260
r: '0x' + signature.r,
261261
s: '0x' + signature.s,
262-
v: signature.recid,
262+
recoveryParam: signature.recid,
263263
});
264264

265265
signatures[key] = {

packages/types/src/lib/interfaces.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export interface AuthSig {
4242
/**
4343
* The signature produced by signing the `signMessage` property with the corresponding private key for the `address` property.
4444
*/
45-
sig: any;
45+
sig: string;
4646

4747
/**
4848
* The method used to derive the signature (e.g, `web3.eth.personal.sign`).
@@ -598,7 +598,7 @@ export interface SigResponse {
598598
r: string;
599599
s: string;
600600
recid: number;
601-
signature: string; // 0x...
601+
signature: `0x${string}`;
602602
publicKey: string; // pkp public key (no 0x prefix)
603603
dataSigned: string;
604604
}

packages/wasm/rust/src/ecdsa.rs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,22 @@ where
5858
presignature: Uint8Array,
5959
signature_shares: Vec<Uint8Array>,
6060
) -> JsResult<EcdsaSignature> {
61-
let (big_r, s) = Self::combine_inner(presignature, signature_shares)?;
62-
Self::signature_into_js(big_r.to_affine(), s)
61+
let (big_r, s, was_flipped) = Self::combine_inner(presignature, signature_shares)?;
62+
Self::signature_into_js(big_r.to_affine(), s, was_flipped)
6363
}
6464

6565
pub(crate) fn combine_inner(
6666
presignature: Uint8Array,
6767
signature_shares: Vec<Uint8Array>,
68-
) -> JsResult<(C::ProjectivePoint, C::Scalar)> {
68+
) -> JsResult<(C::ProjectivePoint, C::Scalar, bool)> {
6969
let signature_shares = signature_shares
7070
.into_iter()
7171
.map(Self::scalar_from_js)
7272
.collect::<JsResult<Vec<_>>>()?;
7373

7474
let big_r: C::AffinePoint = Self::point_from_js(presignature)?;
75-
let s = Self::sum_scalars(signature_shares)?;
76-
Ok((C::ProjectivePoint::from(big_r), s))
75+
let (s, was_flipped) = Self::sum_scalars(signature_shares)?;
76+
Ok((C::ProjectivePoint::from(big_r), s, was_flipped))
7777
}
7878

7979
pub fn verify(
@@ -108,13 +108,14 @@ where
108108
Ok(())
109109
}
110110

111-
fn sum_scalars(values: Vec<C::Scalar>) -> JsResult<C::Scalar> {
111+
fn sum_scalars(values: Vec<C::Scalar>) -> JsResult<(C::Scalar, bool)> {
112112
if values.is_empty() {
113113
return Err(JsError::new("no shares provided"));
114114
}
115115
let mut acc: C::Scalar = values.into_iter().sum();
116+
let acc_flipped = acc.is_high().into();
116117
acc.conditional_assign(&(-acc), acc.is_high());
117-
Ok(acc)
118+
Ok((acc, acc_flipped))
118119
}
119120

120121
pub fn derive_key(id: Uint8Array, public_keys: Vec<Uint8Array>) -> JsResult<Uint8Array> {
@@ -168,10 +169,15 @@ where
168169
Ok((r, s, v))
169170
}
170171

171-
fn signature_into_js(big_r: C::AffinePoint, s: C::Scalar) -> JsResult<EcdsaSignature> {
172+
fn signature_into_js(big_r: C::AffinePoint, s: C::Scalar, was_flipped: bool) -> JsResult<EcdsaSignature> {
172173
let r = Self::x_coordinate(&big_r).to_repr();
173174
let s = s.to_repr();
174-
let v = u8::conditional_select(&0, &1, big_r.y_is_odd());
175+
let mut v = u8::conditional_select(&0, &1, big_r.y_is_odd());
176+
177+
// Flip v if s was normalized (flipped, low-s rule)
178+
if was_flipped {
179+
v = 1 - v;
180+
}
175181

176182
Ok(EcdsaSignature {
177183
obj: into_js(&(Bytes::new(&r), Bytes::new(&s), v))?,
@@ -222,7 +228,7 @@ where
222228
public_key: C::ProjectivePoint,
223229
) -> JsResult<EcdsaSignature> {
224230
let z = Self::scalar_from_hash(message_hash)?;
225-
let (big_r, s) = Self::combine_inner(pre_signature, signature_shares)?;
231+
let (big_r, s, was_flipped) = Self::combine_inner(pre_signature, signature_shares)?;
226232
let r = Self::x_coordinate(&big_r.to_affine());
227233

228234
if z.is_zero().into() {
@@ -241,7 +247,7 @@ where
241247
.is_identity()
242248
.into()
243249
{
244-
Self::signature_into_js(big_r.to_affine(), s)
250+
Self::signature_into_js(big_r.to_affine(), s, was_flipped)
245251
} else {
246252
Err(JsError::new("invalid signature"))
247253
}

0 commit comments

Comments
 (0)