iso-signatures 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/signers/ecdsa.d.ts +19 -0
- package/dist/src/signers/ecdsa.d.ts.map +1 -1
- package/dist/src/signers/eddsa.d.ts +1 -1
- package/dist/src/signers/eddsa.d.ts.map +1 -1
- package/dist/src/signers/eip191.d.ts +109 -4
- package/dist/src/signers/eip191.d.ts.map +1 -1
- package/dist/src/signers/es256k.d.ts +1 -1
- package/dist/src/signers/es256k.d.ts.map +1 -1
- package/dist/src/types.d.ts +20 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/verifiers/ecdsa.d.ts +2 -2
- package/dist/src/verifiers/ecdsa.d.ts.map +1 -1
- package/dist/src/verifiers/eip191.d.ts +1 -1
- package/dist/src/verifiers/eip191.d.ts.map +1 -1
- package/dist/src/verifiers/resolver.d.ts +1 -0
- package/dist/src/verifiers/resolver.d.ts.map +1 -1
- package/license +9 -0
- package/package.json +16 -14
- package/readme.md +25 -0
- package/src/signers/ecdsa.js +62 -1
- package/src/signers/eip191.js +1 -1
- package/src/types.ts +20 -0
- package/src/verifiers/ecdsa.js +9 -5
- package/src/verifiers/eip191.js +35 -15
- package/src/verifiers/resolver.js +3 -2
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a raw `r‖s` ECDSA signature has a high S value (`s > n/2`)
|
|
3
|
+
*
|
|
4
|
+
* Signatures with the wrong length for the curve are reported as high S.
|
|
5
|
+
*
|
|
6
|
+
* @param {Curves} curve
|
|
7
|
+
* @param {Uint8Array} signature
|
|
8
|
+
*/
|
|
9
|
+
export function isHighS(curve: Curves, signature: Uint8Array): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Normalize a raw `r‖s` ECDSA signature to low S (`s <= n/2`)
|
|
12
|
+
*
|
|
13
|
+
* @param {Curves} curve
|
|
14
|
+
* @param {Uint8Array} signature
|
|
15
|
+
*/
|
|
16
|
+
export function normalizeS(curve: Curves, signature: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
1
17
|
/**
|
|
2
18
|
* Create web crypto params for ECDSA.
|
|
3
19
|
*
|
|
@@ -46,6 +62,9 @@ export class ECDSASigner extends DID implements ISigner {
|
|
|
46
62
|
/**
|
|
47
63
|
* Sign a message
|
|
48
64
|
*
|
|
65
|
+
* Signatures are normalized to low S (`s <= n/2`) so each message has a
|
|
66
|
+
* single signature encoding per signing operation.
|
|
67
|
+
*
|
|
49
68
|
* @param {Uint8Array} message
|
|
50
69
|
*/
|
|
51
70
|
sign(message: Uint8Array): Promise<Uint8Array<ArrayBufferLike>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecdsa.d.ts","sourceRoot":"","sources":["../../../src/signers/ecdsa.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ecdsa.d.ts","sourceRoot":"","sources":["../../../src/signers/ecdsa.js"],"names":[],"mappings":"AA6DA;;;;;;;GAOG;AACH,+BAHW,MAAM,aACN,UAAU,WASpB;AAED;;;;;GAKG;AACH,kCAHW,MAAM,aACN,UAAU,+BAcpB;AAED;;;;;GAKG;AACH,yCAHW,MAAM,GACJ;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAA;CAAC,CA8B7G;AAED;;;;GAIG;AACH,gDAFgB,OAAO;IAuBrB;;;;OAIG;IACH,uBAFW,MAAM,wBAkBhB;IAED;;;;;OAKG;IACH,sBAHW,OAAO,eAAe,EAAE,YAAY,QACpC,OAAO,eAAe,EAAE,aAAa,wBA2B/C;IAED;;;;;OAKG;IACH,mBAHW,aAAa,QACb,OAAO,eAAe,EAAE,aAAa,wBA8B/C;IAtGD;;;OAGG;IACH,iBAHW,OAAO,eAAe,EAAE,aAAa,WACrC,aAAa,EAQvB;IAZD,kDAAkD;IAClD,eADW,OAAO,aAAa,EAAE,aAAa,CACjC;IAOX,mCAA+C;IAkGjD;;;;;;;OAOG;IACH,cAFW,UAAU,wCAUpB;IAED;;;;OAIG;IACH,UAFa,aAAa,CAIzB;;CAKF;sBAlRY,OAAO,aAAa,EAAE,OAAO,CAAC,aAAa,CAAC;qBAC5C,OAAO,CAAC,OAAO,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;6BACrE,OAAO,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;oBAPlE,SAAS"}
|
|
@@ -42,7 +42,7 @@ export class EdDSASigner extends DID implements ISigner {
|
|
|
42
42
|
*
|
|
43
43
|
* @param {Uint8Array} message
|
|
44
44
|
*/
|
|
45
|
-
sign(message: Uint8Array): Promise<
|
|
45
|
+
sign(message: Uint8Array): Promise<Uint8Array<ArrayBufferLike> & Uint8Array<ArrayBuffer>>;
|
|
46
46
|
/**
|
|
47
47
|
* Export the signer as a encoded string
|
|
48
48
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eddsa.d.ts","sourceRoot":"","sources":["../../../src/signers/eddsa.js"],"names":[],"mappings":"AAyBA;;;;GAIG;AACH,gDAFgB,OAAO;IAGrB;;;;OAIG;IACH,aAFU,MAAM,CAEI;IAmBpB;;;;OAIG;IACH,wBAFW,UAAU,wBAiBpB;IAED;;;;;OAKG;IACH,uBAHW,MAAM,QACN,OAAO,eAAe,EAAE,aAAa,wBAU/C;IAED;;;;;OAKG;IACH,sBAHW,OAAO,eAAe,EAAE,aAAa,QACrC,OAAO,eAAe,EAAE,aAAa,eAU/C;IA/DD;;;OAGG;IACH,iBAHW,OAAO,eAAe,EAAE,aAAa,cACrC,UAAU,EAOpB;IAZD,kDAAkD;IAClD,eADW,OAAO,aAAa,EAAE,aAAa,CACjC;IAmEb;;;;OAIG;IACH,cAFW,UAAU,
|
|
1
|
+
{"version":3,"file":"eddsa.d.ts","sourceRoot":"","sources":["../../../src/signers/eddsa.js"],"names":[],"mappings":"AAyBA;;;;GAIG;AACH,gDAFgB,OAAO;IAGrB;;;;OAIG;IACH,aAFU,MAAM,CAEI;IAmBpB;;;;OAIG;IACH,wBAFW,UAAU,wBAiBpB;IAED;;;;;OAKG;IACH,uBAHW,MAAM,QACN,OAAO,eAAe,EAAE,aAAa,wBAU/C;IAED;;;;;OAKG;IACH,sBAHW,OAAO,eAAe,EAAE,aAAa,QACrC,OAAO,eAAe,EAAE,aAAa,eAU/C;IA/DD;;;OAGG;IACH,iBAHW,OAAO,eAAe,EAAE,aAAa,cACrC,UAAU,EAOpB;IAZD,kDAAkD;IAClD,eADW,OAAO,aAAa,EAAE,aAAa,CACjC;IAmEb;;;;OAIG;IACH,cAFW,UAAU,kEAIpB;IAED;;OAEG;IACH,iBAEC;;CAKF;sBApHY,OAAO,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;oBAP9B,SAAS"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import('../types.js').ISigner<string>} ISigner
|
|
3
3
|
* @typedef {import('ox').Provider.Provider} Provider
|
|
4
|
+
* @typedef {import('eventemitter3').EventEmitter} EventEmitter
|
|
4
5
|
*/
|
|
5
6
|
/**
|
|
6
7
|
* EIP191 signer
|
|
@@ -44,7 +45,7 @@ export class EIP191Signer extends DID implements ISigner {
|
|
|
44
45
|
/** @type {import('../types.js').SignatureType} */
|
|
45
46
|
signatureType: import("../types.js").SignatureType;
|
|
46
47
|
provider: {
|
|
47
|
-
request: Provider.RequestFn<{
|
|
48
|
+
request: import("ox/Provider").RequestFn<{
|
|
48
49
|
Request: {
|
|
49
50
|
method: "eth_accounts";
|
|
50
51
|
params?: undefined;
|
|
@@ -158,6 +159,12 @@ export class EIP191Signer extends DID implements ISigner {
|
|
|
158
159
|
params: [address: import("ox/Address").Address, storageKeys: import("ox/Hex").Hex[], block: import("ox/Block").Number<import("ox/Hex").Hex> | import("ox/Block").Tag | import("ox/Block").Hash | import("ox/Block").Identifier];
|
|
159
160
|
};
|
|
160
161
|
ReturnType: import("ox/AccountProof").Rpc;
|
|
162
|
+
} | {
|
|
163
|
+
Request: {
|
|
164
|
+
method: "eth_getRawTransactionByHash";
|
|
165
|
+
params: [hash: import("ox/Hex").Hex];
|
|
166
|
+
};
|
|
167
|
+
ReturnType: import("ox/Hex").Hex | null;
|
|
161
168
|
} | {
|
|
162
169
|
Request: {
|
|
163
170
|
method: "eth_getStorageAt";
|
|
@@ -248,6 +255,12 @@ export class EIP191Signer extends DID implements ISigner {
|
|
|
248
255
|
params: [serializedTransaction: import("ox/Hex").Hex];
|
|
249
256
|
};
|
|
250
257
|
ReturnType: import("ox/Hex").Hex;
|
|
258
|
+
} | {
|
|
259
|
+
Request: {
|
|
260
|
+
method: "eth_sendRawTransactionSync";
|
|
261
|
+
params: [serializedTransaction: import("ox/Hex").Hex] | [serializedTransaction: import("ox/Hex").Hex, timeout: number];
|
|
262
|
+
};
|
|
263
|
+
ReturnType: import("ox/TransactionReceipt").Rpc;
|
|
251
264
|
} | {
|
|
252
265
|
Request: {
|
|
253
266
|
method: "eth_sendTransaction";
|
|
@@ -311,6 +324,12 @@ export class EIP191Signer extends DID implements ISigner {
|
|
|
311
324
|
params: [serializedTransaction: import("ox/Hex").Hex];
|
|
312
325
|
};
|
|
313
326
|
ReturnType: import("ox/Hex").Hex;
|
|
327
|
+
} | {
|
|
328
|
+
Request: {
|
|
329
|
+
method: "eth_sendRawTransactionSync";
|
|
330
|
+
params: [serializedTransaction: import("ox/Hex").Hex] | [serializedTransaction: import("ox/Hex").Hex, timeout: number];
|
|
331
|
+
};
|
|
332
|
+
ReturnType: import("ox/TransactionReceipt").Rpc;
|
|
314
333
|
} | {
|
|
315
334
|
Request: {
|
|
316
335
|
method: "eth_sendTransaction";
|
|
@@ -525,8 +544,94 @@ export class EIP191Signer extends DID implements ISigner {
|
|
|
525
544
|
};
|
|
526
545
|
ReturnType: boolean;
|
|
527
546
|
}>;
|
|
528
|
-
|
|
529
|
-
|
|
547
|
+
eventNames?: (() => ("accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message")[]) | undefined;
|
|
548
|
+
listeners?: (<T extends "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">(event: T) => ((...args: import("eventemitter3").EventEmitter.ArgumentMap<{
|
|
549
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
550
|
+
chainChanged: (chainId: string) => void;
|
|
551
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
552
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
553
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
554
|
+
}>[Extract<T, "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">]) => void)[]) | undefined;
|
|
555
|
+
listenerCount?: ((event: "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message") => number) | undefined;
|
|
556
|
+
emit?: (<T extends "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">(event: T, ...args: import("eventemitter3").EventEmitter.ArgumentMap<{
|
|
557
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
558
|
+
chainChanged: (chainId: string) => void;
|
|
559
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
560
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
561
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
562
|
+
}>[Extract<T, "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">]) => boolean) | undefined;
|
|
563
|
+
on?: (<T extends "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">(event: T, fn: (...args: import("eventemitter3").EventEmitter.ArgumentMap<{
|
|
564
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
565
|
+
chainChanged: (chainId: string) => void;
|
|
566
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
567
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
568
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
569
|
+
}>[Extract<T, "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">]) => void, context?: any) => import("eventemitter3").EventEmitter<{
|
|
570
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
571
|
+
chainChanged: (chainId: string) => void;
|
|
572
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
573
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
574
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
575
|
+
}, any>) | undefined;
|
|
576
|
+
addListener?: (<T extends "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">(event: T, fn: (...args: import("eventemitter3").EventEmitter.ArgumentMap<{
|
|
577
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
578
|
+
chainChanged: (chainId: string) => void;
|
|
579
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
580
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
581
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
582
|
+
}>[Extract<T, "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">]) => void, context?: any) => import("eventemitter3").EventEmitter<{
|
|
583
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
584
|
+
chainChanged: (chainId: string) => void;
|
|
585
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
586
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
587
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
588
|
+
}, any>) | undefined;
|
|
589
|
+
once?: (<T extends "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">(event: T, fn: (...args: import("eventemitter3").EventEmitter.ArgumentMap<{
|
|
590
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
591
|
+
chainChanged: (chainId: string) => void;
|
|
592
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
593
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
594
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
595
|
+
}>[Extract<T, "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">]) => void, context?: any) => import("eventemitter3").EventEmitter<{
|
|
596
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
597
|
+
chainChanged: (chainId: string) => void;
|
|
598
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
599
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
600
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
601
|
+
}, any>) | undefined;
|
|
602
|
+
removeListener?: (<T extends "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">(event: T, fn?: ((...args: import("eventemitter3").EventEmitter.ArgumentMap<{
|
|
603
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
604
|
+
chainChanged: (chainId: string) => void;
|
|
605
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
606
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
607
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
608
|
+
}>[Extract<T, "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">]) => void) | undefined, context?: any, once?: boolean) => import("eventemitter3").EventEmitter<{
|
|
609
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
610
|
+
chainChanged: (chainId: string) => void;
|
|
611
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
612
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
613
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
614
|
+
}, any>) | undefined;
|
|
615
|
+
off?: (<T extends "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">(event: T, fn?: ((...args: import("eventemitter3").EventEmitter.ArgumentMap<{
|
|
616
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
617
|
+
chainChanged: (chainId: string) => void;
|
|
618
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
619
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
620
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
621
|
+
}>[Extract<T, "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message">]) => void) | undefined, context?: any, once?: boolean) => import("eventemitter3").EventEmitter<{
|
|
622
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
623
|
+
chainChanged: (chainId: string) => void;
|
|
624
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
625
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
626
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
627
|
+
}, any>) | undefined;
|
|
628
|
+
removeAllListeners?: ((event?: "accountsChanged" | "chainChanged" | "connect" | "disconnect" | "message" | undefined) => import("eventemitter3").EventEmitter<{
|
|
629
|
+
accountsChanged: (accounts: readonly import("ox/Address").Address[]) => void;
|
|
630
|
+
chainChanged: (chainId: string) => void;
|
|
631
|
+
connect: (connectInfo: import("ox/Provider").ConnectInfo) => void;
|
|
632
|
+
disconnect: (error: import("ox/Provider").ProviderRpcError) => void;
|
|
633
|
+
message: (message: import("ox/Provider").Message) => void;
|
|
634
|
+
}, any>) | undefined;
|
|
530
635
|
};
|
|
531
636
|
address: `0x${string}`;
|
|
532
637
|
chainId: number | `0x${string}`;
|
|
@@ -544,6 +649,6 @@ export class EIP191Signer extends DID implements ISigner {
|
|
|
544
649
|
}
|
|
545
650
|
export type ISigner = import("../types.js").ISigner<string>;
|
|
546
651
|
export type Provider = import("ox").Provider.Provider;
|
|
652
|
+
export type EventEmitter = import("eventemitter3").EventEmitter;
|
|
547
653
|
import { DID } from 'iso-did';
|
|
548
|
-
import { Provider } from 'ox';
|
|
549
654
|
//# sourceMappingURL=eip191.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eip191.d.ts","sourceRoot":"","sources":["../../../src/signers/eip191.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"eip191.d.ts","sourceRoot":"","sources":["../../../src/signers/eip191.js"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH;;;;GAIG;AACH,iDAFgB,OAAO;IAuBrB;;;;;;;;;;;;;;;;;OAiBG;IACH,qBAdG;QAA0B,QAAQ,EAA1B,QAAQ;QACgB,OAAO;QACE,OAAO;QACvB,SAAS;KAElC,yBAwBF;IAlDD;;;;;;;OAOG;IACH,sBALW,QAAQ,WACR,KAAK,MAAM,EAAE,YACb,MAAM,GAAG,KAAK,MAAM,EAAE,cACtB,MAAM,EAShB;IAlBD,kDAAkD;IAClD,eADW,OAAO,aAAa,EAAE,aAAa,CACjC;IAaX;;;;sBAW0B,CAAC;;;;;;sBAOJ,CAAC;;;;;;sBAUpB,CAAL;;;;;;;;;;;;sBAqCwQ,CAAC;;;;;;sBAAmP,CAAC;;;;;;;;;;;;;;;;;;sBAA6jE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAA2lQ,CAAC;;;;;;sBAAkT,CAAC;;;;;;;;;;;;sBAAovB,CAAC;;;;;;sBAAuQ,CAAC;;;;;;sBAAwW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;sCAAs0C,CAAC;6BAA2D,CAAC;sCAAmF,CAAC;;0CAA6F,CAAC;kCAAqD,CAAC;8BAAiD,CAAC;;;;qBAAkM,CAAC;yBAA8B,CAAC;4BAAwB,CAAC;;;;wBAAiI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAnEpzc,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAmE4gD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAA0Q,CAAC;;;;;;;;;;sBAA+Z,CAAC;;;;;;;;;;;;;;;sBAAmQ,CAAC;;;4BAAguI,CAAC;;;;;;;;gCAA4M,CAAC;;;;;;;;;;;;;;;;4BAA2f,CAAC;;;;;iCAA8H,CAAC;mCAAoD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAA+R,CAAC;;;0BAAgD,CAAC;4BAA+C,CAAC;6BAAwC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BAA68B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAjEh5R;IACxB,uBAAsB;IACtB,gCAAsB;IACtB,kBAA0B;IAsC5B;;;;OAIG;IACH,cAFW,UAAU,wCAQpB;IAED;;OAEG;IACH,iBAEC;CAKF;sBAzFY,OAAO,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;uBACrC,OAAO,IAAI,EAAE,QAAQ,CAAC,QAAQ;2BAC9B,OAAO,eAAe,EAAE,YAAY;oBAN7B,SAAS"}
|
|
@@ -43,7 +43,7 @@ export class ES256KSigner extends DID implements ISigner {
|
|
|
43
43
|
*
|
|
44
44
|
* @param {Uint8Array} message
|
|
45
45
|
*/
|
|
46
|
-
sign(message: Uint8Array): Promise<
|
|
46
|
+
sign(message: Uint8Array): Promise<Uint8Array<ArrayBufferLike> & Uint8Array<ArrayBuffer>>;
|
|
47
47
|
/**
|
|
48
48
|
* Export the signer as a encoded string
|
|
49
49
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"es256k.d.ts","sourceRoot":"","sources":["../../../src/signers/es256k.js"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,iDAFgB,OAAO;IAGrB;;;;OAIG;IACH,aAFU,MAAM,CAEI;IAoBpB;;;;OAIG;IACH,wBAFW,UAAU,gBAUpB;IAED;;;;;OAKG;IACH,sBAHW,OAAO,eAAe,EAAE,YAAY,QACpC,OAAO,eAAe,EAAE,aAAa,gBAU/C;IAED;;;;;OAKG;IACH,uBAHW,MAAM,QACN,OAAO,eAAe,EAAE,aAAa,gBAU/C;IAzDD;;;;OAIG;IACH,iBAHW,OAAO,eAAe,EAAE,aAAa,cACrC,UAAU,EAOpB;IAhBD,kDAAkD;IAClD,eADW,OAAO,aAAa,EAAE,aAAa,CACjC;IAgEb;;;;OAIG;IACH,cAFW,UAAU,
|
|
1
|
+
{"version":3,"file":"es256k.d.ts","sourceRoot":"","sources":["../../../src/signers/es256k.js"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,iDAFgB,OAAO;IAGrB;;;;OAIG;IACH,aAFU,MAAM,CAEI;IAoBpB;;;;OAIG;IACH,wBAFW,UAAU,gBAUpB;IAED;;;;;OAKG;IACH,sBAHW,OAAO,eAAe,EAAE,YAAY,QACpC,OAAO,eAAe,EAAE,aAAa,gBAU/C;IAED;;;;;OAKG;IACH,uBAHW,MAAM,QACN,OAAO,eAAe,EAAE,aAAa,gBAU/C;IAzDD;;;;OAIG;IACH,iBAHW,OAAO,eAAe,EAAE,aAAa,cACrC,UAAU,EAOpB;IAhBD,kDAAkD;IAClD,eADW,OAAO,aAAa,EAAE,aAAa,CACjC;IAgEb;;;;OAIG;IACH,cAFW,UAAU,kEAKpB;IAED;;OAEG;IACH,iBAEC;;CAKF;sBA9GY,OAAO,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;oBAL9B,SAAS"}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -11,6 +11,16 @@ export interface VerifyInput {
|
|
|
11
11
|
signature: Uint8Array;
|
|
12
12
|
message: Uint8Array;
|
|
13
13
|
did: VerifiableDID;
|
|
14
|
+
/**
|
|
15
|
+
* Only accept the canonical signature encoding. ECDSA (P-256/384/521) and
|
|
16
|
+
* EIP-191 signatures with a high S value (`s > n/2`) are rejected, and
|
|
17
|
+
* EIP-191 signatures must use 27/28 as the recovery byte.
|
|
18
|
+
*
|
|
19
|
+
* Other signature types ignore this option.
|
|
20
|
+
*
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
strict?: boolean;
|
|
14
24
|
}
|
|
15
25
|
export type Verify = (input: VerifyInput) => Promise<boolean>;
|
|
16
26
|
export type Verifier<T extends SignatureType> = Record<T, Verify>;
|
|
@@ -27,5 +37,15 @@ export interface IResolver {
|
|
|
27
37
|
}
|
|
28
38
|
export interface ResolverOptions {
|
|
29
39
|
cache?: Cache | boolean | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Default `strict` value for verify calls that don't set it.
|
|
42
|
+
*
|
|
43
|
+
* Only enable this when you control all signers: other implementations
|
|
44
|
+
* produce high S ECDSA signatures and 0/1 EIP-191 recovery bytes.
|
|
45
|
+
*
|
|
46
|
+
* @see {@link VerifyInput.strict}
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
strict?: boolean;
|
|
30
50
|
}
|
|
31
51
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAElD,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,OAAO,GACP,OAAO,GACP,OAAO,GACP,QAAQ,GACR,OAAO,GACP,QAAQ,CAAA;AAEZ,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,OAAO,CAAC,MAAM,SAAS,aAAa,GAAG,MAAM,GAAG,MAAM,CACrE,SAAQ,aAAa,EACnB,IAAI;IACN,MAAM,EAAE,MAAM,MAAM,CAAA;IACpB,aAAa,EAAE,aAAa,CAAA;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,UAAU,CAAA;IACrB,OAAO,EAAE,UAAU,CAAA;IACnB,GAAG,EAAE,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAElD,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,OAAO,GACP,OAAO,GACP,OAAO,GACP,QAAQ,GACR,OAAO,GACP,QAAQ,CAAA;AAEZ,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,OAAO,CAAC,MAAM,SAAS,aAAa,GAAG,MAAM,GAAG,MAAM,CACrE,SAAQ,aAAa,EACnB,IAAI;IACN,MAAM,EAAE,MAAM,MAAM,CAAA;IACpB,aAAa,EAAE,aAAa,CAAA;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,UAAU,CAAA;IACrB,OAAO,EAAE,UAAU,CAAA;IACnB,GAAG,EAAE,aAAa,CAAA;IAClB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AACD,MAAM,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE7D,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,aAAa,IAAI,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;AAEjE,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,aAAa,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;AAE5E,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD;;OAEG;IACH,IAAI,EAAE,aAAa,CAAA;CACpB;AACD,MAAM,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAE7E,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CACzD;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,SAAS,CAAA;IACnC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
*
|
|
3
3
|
* @param {'ES256' | 'ES384' | 'ES512' | 'ES256K'} type
|
|
4
|
-
* @param {import('../types.js').VerifyInput}
|
|
4
|
+
* @param {import('../types.js').VerifyInput} input
|
|
5
5
|
* @returns
|
|
6
6
|
*/
|
|
7
|
-
export function verify(type: "ES256" | "ES384" | "ES512" | "ES256K",
|
|
7
|
+
export function verify(type: "ES256" | "ES384" | "ES512" | "ES256K", input: import("../types.js").VerifyInput): Promise<boolean>;
|
|
8
8
|
/** @type {import('../types.js').Verifier<'ES256' | 'ES384' | 'ES512' | 'ES256K'>} */
|
|
9
9
|
export const verifier: import("../types.js").Verifier<"ES256" | "ES384" | "ES512" | "ES256K">;
|
|
10
10
|
//# sourceMappingURL=ecdsa.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecdsa.d.ts","sourceRoot":"","sources":["../../../src/verifiers/ecdsa.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ecdsa.d.ts","sourceRoot":"","sources":["../../../src/verifiers/ecdsa.js"],"names":[],"mappings":"AA6DA;;;;;GAKG;AACH,6BAJW,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,SACtC,OAAO,aAAa,EAAE,WAAW,oBAK3C;AAhBD,qFAAqF;AACrF,uBADW,OAAO,aAAa,EAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC,CAMhF"}
|
|
@@ -7,7 +7,7 @@ export function hexToBytes(signature: `0x${string}`): Uint8Array<ArrayBufferLike
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {Uint8Array} data
|
|
9
9
|
*/
|
|
10
|
-
export function getSignPayload(data: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
10
|
+
export function getSignPayload(data: Uint8Array): Uint8Array<ArrayBufferLike> & Uint8Array<ArrayBuffer>;
|
|
11
11
|
export function verify(input: import("../types.js").VerifyInput): Promise<boolean>;
|
|
12
12
|
/** @type {import('../types').Verifier<'EIP191'>} */
|
|
13
13
|
export const verifier: import("../types").Verifier<"EIP191">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eip191.d.ts","sourceRoot":"","sources":["../../../src/verifiers/eip191.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"eip191.d.ts","sourceRoot":"","sources":["../../../src/verifiers/eip191.js"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,sCAFW,KAAK,MAAM,EAAE,+BAIvB;AAED;;GAEG;AACH,qCAFW,UAAU,yDAKpB;;AAoCD,oDAAoD;AACpD,uBADW,OAAO,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAG/C"}
|
|
@@ -12,6 +12,7 @@ export class Resolver implements IResolver {
|
|
|
12
12
|
constructor(registry?: import("../types").VerifierRegistry<import("../types").SignatureType>, options?: import("../types").ResolverOptions);
|
|
13
13
|
registry: Partial<import("../types").Verifier<import("../types").SignatureType>>;
|
|
14
14
|
cache: import("../types").Cache;
|
|
15
|
+
strict: boolean;
|
|
15
16
|
verify(input: import("../types").ResolverVerifyInput): Promise<boolean>;
|
|
16
17
|
}
|
|
17
18
|
export type IResolver = import("../types").IResolver;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/verifiers/resolver.js"],"names":[],"mappings":"AAsCA;;;;GAIG;AACH,iCAFgB,SAAS;IAGvB;;;;OAIG;IACH,uBAHW,OAAO,UAAU,EAAE,gBAAgB,CAAC,OAAO,UAAU,EAAE,aAAa,CAAC,YACrE,OAAO,UAAU,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../../src/verifiers/resolver.js"],"names":[],"mappings":"AAsCA;;;;GAIG;AACH,iCAFgB,SAAS;IAGvB;;;;OAIG;IACH,uBAHW,OAAO,UAAU,EAAE,gBAAgB,CAAC,OAAO,UAAU,EAAE,aAAa,CAAC,YACrE,OAAO,UAAU,EAAE,eAAe,EAO5C;IAJC,iFAAwB;IACxB,gCACmE;IACnE,gBAAqC;;CAYxC;wBAhEY,OAAO,UAAU,EAAE,SAAS"}
|
package/license
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Hugo Dias <hugomrdias@gmail.com> (hugodias.me)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iso-signatures",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0",
|
|
5
5
|
"description": "Signatures tooling.",
|
|
6
6
|
"author": "Hugo Dias <hugomrdias@gmail.com> (hugodias.me)",
|
|
7
7
|
"license": "MIT",
|
|
@@ -81,23 +81,24 @@
|
|
|
81
81
|
"dist/src/**/*.d.ts.map"
|
|
82
82
|
],
|
|
83
83
|
"dependencies": {
|
|
84
|
-
"@noble/ed25519": "^3.
|
|
85
|
-
"@noble/hashes": "^2.
|
|
86
|
-
"@noble/secp256k1": "^3.
|
|
87
|
-
"
|
|
88
|
-
"iso-base": "^4.
|
|
89
|
-
"iso-did": "^
|
|
84
|
+
"@noble/ed25519": "^3.2.0",
|
|
85
|
+
"@noble/hashes": "^2.4.0",
|
|
86
|
+
"@noble/secp256k1": "^3.2.0",
|
|
87
|
+
"eventemitter3": "5.0.1",
|
|
88
|
+
"iso-base": "^4.4.0",
|
|
89
|
+
"iso-did": "^3.0.0",
|
|
90
|
+
"ox": "^0.14.49"
|
|
90
91
|
},
|
|
91
92
|
"devDependencies": {
|
|
92
93
|
"@types/assert": "^1.5.11",
|
|
93
94
|
"@types/mocha": "^10.0.10",
|
|
94
|
-
"@types/node": "^
|
|
95
|
+
"@types/node": "^26.6.2",
|
|
95
96
|
"assert": "^2.1.0",
|
|
96
|
-
"mocha": "^
|
|
97
|
-
"playwright-test": "^
|
|
98
|
-
"type-fest": "^5.0
|
|
99
|
-
"typescript": "
|
|
100
|
-
"viem": "
|
|
97
|
+
"mocha": "^12.0.2",
|
|
98
|
+
"playwright-test": "^15.0.1",
|
|
99
|
+
"type-fest": "^5.10.0",
|
|
100
|
+
"typescript": "^6.0.3",
|
|
101
|
+
"viem": "~2.56.9"
|
|
101
102
|
},
|
|
102
103
|
"publishConfig": {
|
|
103
104
|
"provenance": true
|
|
@@ -110,7 +111,8 @@
|
|
|
110
111
|
"@types/*",
|
|
111
112
|
"assert",
|
|
112
113
|
"type-fest",
|
|
113
|
-
"mocha"
|
|
114
|
+
"mocha",
|
|
115
|
+
"eventemitter3"
|
|
114
116
|
]
|
|
115
117
|
},
|
|
116
118
|
"scripts": {
|
package/readme.md
CHANGED
|
@@ -52,6 +52,31 @@ const verified = await resolver.verify({
|
|
|
52
52
|
})
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
### Strict verification
|
|
56
|
+
|
|
57
|
+
`ECDSASigner` always emits low S signatures (`s <= n/2`). Verifiers accept both
|
|
58
|
+
low and high S by default, since other implementations produce high S
|
|
59
|
+
signatures. If you control all your signers, enable `strict` to only accept the
|
|
60
|
+
canonical encoding: ECDSA (P-256/384/521) and EIP-191 signatures with high S are
|
|
61
|
+
rejected, and EIP-191 signatures must use 27/28 as the recovery byte.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import * as ECDSA from 'iso-signatures/verifiers/ecdsa'
|
|
65
|
+
import * as EIP191 from 'iso-signatures/verifiers/eip191'
|
|
66
|
+
import { Resolver } from 'iso-signatures/verifiers/resolver'
|
|
67
|
+
|
|
68
|
+
const resolver = new Resolver(
|
|
69
|
+
{
|
|
70
|
+
...ECDSA.verifier,
|
|
71
|
+
...EIP191.verifier,
|
|
72
|
+
},
|
|
73
|
+
{ strict: true }
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
// or per call
|
|
77
|
+
await ECDSA.verify('ES256', { signature, message, did, strict: true })
|
|
78
|
+
```
|
|
79
|
+
|
|
55
80
|
## Docs
|
|
56
81
|
|
|
57
82
|
Check <https://hugomrdias.github.io/iso-repo/modules/iso_signatures.html>
|
package/src/signers/ecdsa.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { webcrypto } from 'iso-base/crypto'
|
|
2
|
+
import { hex } from 'iso-base/rfc4648'
|
|
2
3
|
import { u8 } from 'iso-base/utils'
|
|
3
4
|
import { DID } from 'iso-did'
|
|
4
5
|
import { DIDKey } from 'iso-did/key'
|
|
@@ -38,6 +39,63 @@ function curveToSignatureType(type) {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Curve orders (n) and signature component sizes in bytes
|
|
44
|
+
*
|
|
45
|
+
* @type {Record<Curves, {n: bigint, size: number}>}
|
|
46
|
+
*/
|
|
47
|
+
const CURVES = {
|
|
48
|
+
'P-256': {
|
|
49
|
+
n: 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551n,
|
|
50
|
+
size: 32,
|
|
51
|
+
},
|
|
52
|
+
'P-384': {
|
|
53
|
+
n: 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973n,
|
|
54
|
+
size: 48,
|
|
55
|
+
},
|
|
56
|
+
'P-521': {
|
|
57
|
+
n: 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409n,
|
|
58
|
+
size: 66,
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Check if a raw `r‖s` ECDSA signature has a high S value (`s > n/2`)
|
|
64
|
+
*
|
|
65
|
+
* Signatures with the wrong length for the curve are reported as high S.
|
|
66
|
+
*
|
|
67
|
+
* @param {Curves} curve
|
|
68
|
+
* @param {Uint8Array} signature
|
|
69
|
+
*/
|
|
70
|
+
export function isHighS(curve, signature) {
|
|
71
|
+
const { n, size } = CURVES[curve]
|
|
72
|
+
if (signature.length !== size * 2) {
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
const s = BigInt(`0x${hex.encode(signature.subarray(size))}`)
|
|
76
|
+
return s > n >> 1n
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Normalize a raw `r‖s` ECDSA signature to low S (`s <= n/2`)
|
|
81
|
+
*
|
|
82
|
+
* @param {Curves} curve
|
|
83
|
+
* @param {Uint8Array} signature
|
|
84
|
+
*/
|
|
85
|
+
export function normalizeS(curve, signature) {
|
|
86
|
+
const { n, size } = CURVES[curve]
|
|
87
|
+
if (signature.length !== size * 2) {
|
|
88
|
+
throw new Error('Invalid signature length')
|
|
89
|
+
}
|
|
90
|
+
const s = BigInt(`0x${hex.encode(signature.subarray(size))}`)
|
|
91
|
+
if (s <= n >> 1n) {
|
|
92
|
+
return signature
|
|
93
|
+
}
|
|
94
|
+
const out = new Uint8Array(signature)
|
|
95
|
+
out.set(hex.decode((n - s).toString(16).padStart(size * 2, '0')), size)
|
|
96
|
+
return out
|
|
97
|
+
}
|
|
98
|
+
|
|
41
99
|
/**
|
|
42
100
|
* Create web crypto params for ECDSA.
|
|
43
101
|
*
|
|
@@ -195,6 +253,9 @@ export class ECDSASigner extends DID {
|
|
|
195
253
|
/**
|
|
196
254
|
* Sign a message
|
|
197
255
|
*
|
|
256
|
+
* Signatures are normalized to low S (`s <= n/2`) so each message has a
|
|
257
|
+
* single signature encoding per signing operation.
|
|
258
|
+
*
|
|
198
259
|
* @param {Uint8Array} message
|
|
199
260
|
*/
|
|
200
261
|
async sign(message) {
|
|
@@ -204,7 +265,7 @@ export class ECDSASigner extends DID {
|
|
|
204
265
|
/** @type {BufferSource} */ (message)
|
|
205
266
|
)
|
|
206
267
|
|
|
207
|
-
return u8(buf)
|
|
268
|
+
return normalizeS(this.curve, u8(buf))
|
|
208
269
|
}
|
|
209
270
|
|
|
210
271
|
/**
|
package/src/signers/eip191.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { hex } from 'iso-base/rfc4648'
|
|
2
2
|
import { DID } from 'iso-did'
|
|
3
3
|
import { DIDPkh } from 'iso-did/pkh'
|
|
4
|
-
import { Provider } from 'ox'
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* @typedef {import('../types.js').ISigner<string>} ISigner
|
|
8
7
|
* @typedef {import('ox').Provider.Provider} Provider
|
|
8
|
+
* @typedef {import('eventemitter3').EventEmitter} EventEmitter
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
package/src/types.ts
CHANGED
|
@@ -24,6 +24,16 @@ export interface VerifyInput {
|
|
|
24
24
|
signature: Uint8Array
|
|
25
25
|
message: Uint8Array
|
|
26
26
|
did: VerifiableDID
|
|
27
|
+
/**
|
|
28
|
+
* Only accept the canonical signature encoding. ECDSA (P-256/384/521) and
|
|
29
|
+
* EIP-191 signatures with a high S value (`s > n/2`) are rejected, and
|
|
30
|
+
* EIP-191 signatures must use 27/28 as the recovery byte.
|
|
31
|
+
*
|
|
32
|
+
* Other signature types ignore this option.
|
|
33
|
+
*
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
strict?: boolean
|
|
27
37
|
}
|
|
28
38
|
export type Verify = (input: VerifyInput) => Promise<boolean>
|
|
29
39
|
|
|
@@ -45,4 +55,14 @@ export interface IResolver {
|
|
|
45
55
|
|
|
46
56
|
export interface ResolverOptions {
|
|
47
57
|
cache?: Cache | boolean | undefined
|
|
58
|
+
/**
|
|
59
|
+
* Default `strict` value for verify calls that don't set it.
|
|
60
|
+
*
|
|
61
|
+
* Only enable this when you control all signers: other implementations
|
|
62
|
+
* produce high S ECDSA signatures and 0/1 EIP-191 recovery bytes.
|
|
63
|
+
*
|
|
64
|
+
* @see {@link VerifyInput.strict}
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
strict?: boolean
|
|
48
68
|
}
|
package/src/verifiers/ecdsa.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { verifyAsync as secpVerify } from '@noble/secp256k1'
|
|
2
2
|
import { webcrypto } from 'iso-base/crypto'
|
|
3
3
|
import { decompress, isCompressed } from 'iso-base/ec-compression'
|
|
4
|
-
import { createEcdsaParams } from '../signers/ecdsa.js'
|
|
4
|
+
import { createEcdsaParams, isHighS } from '../signers/ecdsa.js'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
*
|
|
@@ -11,7 +11,11 @@ function createVerifier(curve) {
|
|
|
11
11
|
const params = createEcdsaParams(curve)
|
|
12
12
|
|
|
13
13
|
/** @type {import('../types.js').Verify} */
|
|
14
|
-
async function fn({ signature, message, did }) {
|
|
14
|
+
async function fn({ signature, message, did, strict }) {
|
|
15
|
+
if (strict && isHighS(curve, signature)) {
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
let publicKey = did.verifiableDid.publicKey
|
|
16
20
|
if (isCompressed(publicKey)) {
|
|
17
21
|
publicKey = decompress(publicKey, params.namedCurve)
|
|
@@ -58,9 +62,9 @@ export const verifier = {
|
|
|
58
62
|
/**
|
|
59
63
|
*
|
|
60
64
|
* @param {'ES256' | 'ES384' | 'ES512' | 'ES256K'} type
|
|
61
|
-
* @param {import('../types.js').VerifyInput}
|
|
65
|
+
* @param {import('../types.js').VerifyInput} input
|
|
62
66
|
* @returns
|
|
63
67
|
*/
|
|
64
|
-
export async function verify(type,
|
|
65
|
-
return await verifier[type](
|
|
68
|
+
export async function verify(type, input) {
|
|
69
|
+
return await verifier[type](input)
|
|
66
70
|
}
|
package/src/verifiers/eip191.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { keccak_256 } from '@noble/hashes/sha3.js'
|
|
2
|
-
import { recoverPublicKeyAsync, Signature
|
|
2
|
+
import { Point, recoverPublicKeyAsync, Signature } from '@noble/secp256k1'
|
|
3
3
|
import { hex } from 'iso-base/rfc4648'
|
|
4
4
|
import { utf8 } from 'iso-base/utf8'
|
|
5
5
|
import { concat } from 'iso-base/utils'
|
|
6
|
-
import
|
|
6
|
+
import { DIDPkh } from 'iso-did/pkh'
|
|
7
|
+
import * as Address from 'ox/Address'
|
|
8
|
+
import * as PublicKey from 'ox/PublicKey'
|
|
7
9
|
|
|
8
10
|
const PREFIX = '\x19Ethereum Signed Message:\n'
|
|
9
11
|
|
|
@@ -25,19 +27,37 @@ export function getSignPayload(data) {
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/** @type {import('../types.js').Verify} */
|
|
28
|
-
export async function verify({ signature, message }) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
export async function verify({ signature, message, did, strict }) {
|
|
31
|
+
if (signature.length !== 65) {
|
|
32
|
+
throw new Error('Invalid signature length')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// personal_sign signatures carry v as 27/28, or 0/1 from some signers.
|
|
36
|
+
// EIP-155 values (v >= 35) only apply to transactions.
|
|
37
|
+
const v = signature[64]
|
|
38
|
+
if (v !== 0 && v !== 1 && v !== 27 && v !== 28) {
|
|
39
|
+
throw new Error('Invalid signature recovery byte')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const didPkh = DIDPkh.fromString(did.did)
|
|
43
|
+
const sig = Signature.fromBytes(signature.slice(0, 64))
|
|
44
|
+
// strict mode only accepts the canonical encoding: low S and v as 27/28
|
|
45
|
+
if (strict && (sig.hasHighS() || (v !== 27 && v !== 28))) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const sigRecovered = sig.addRecoveryBit(v % 27).toBytes('recovered')
|
|
50
|
+
const pubKey = await recoverPublicKeyAsync(
|
|
51
|
+
sigRecovered,
|
|
52
|
+
getSignPayload(message),
|
|
53
|
+
{ prehash: false }
|
|
54
|
+
)
|
|
55
|
+
// noble returns a compressed key, address derivation needs it uncompressed
|
|
56
|
+
const address = Address.fromPublicKey(
|
|
57
|
+
PublicKey.fromBytes(Point.fromBytes(pubKey).toBytes(false))
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return Address.isEqual(address, didPkh.address)
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
/** @type {import('../types').Verifier<'EIP191'>} */
|
|
@@ -23,7 +23,7 @@ function memoryCache() {
|
|
|
23
23
|
|
|
24
24
|
/** @type {import('../types').Cache} */
|
|
25
25
|
async function fn(input, verify) {
|
|
26
|
-
const key = `${input.did.verifiableDid.toString()}:${hex.encode(input.signature)}:${hex.encode(input.message)}`
|
|
26
|
+
const key = `${input.did.verifiableDid.toString()}:${hex.encode(input.signature)}:${hex.encode(input.message)}:${input.strict === true}`
|
|
27
27
|
if (cache.has(key)) {
|
|
28
28
|
return cache.get(key)
|
|
29
29
|
}
|
|
@@ -51,6 +51,7 @@ export class Resolver {
|
|
|
51
51
|
this.registry = registry
|
|
52
52
|
this.cache =
|
|
53
53
|
options.cache === true ? memoryCache() : options.cache || noCache
|
|
54
|
+
this.strict = options.strict === true
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/** @type {IResolver['verify']} */
|
|
@@ -60,6 +61,6 @@ export class Resolver {
|
|
|
60
61
|
throw new TypeError(`Unsupported signature type "${input.type}"`)
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
return this.cache(input, verify)
|
|
64
|
+
return this.cache({ ...input, strict: input.strict ?? this.strict }, verify)
|
|
64
65
|
}
|
|
65
66
|
}
|