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.
@@ -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":"AAwCA;;;;;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;;;;OAIG;IACH,cAFW,UAAU,wCAUpB;IAED;;;;OAIG;IACH,UAFa,aAAa,CAIzB;;CAKF;sBAtNY,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"}
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<import("@noble/ed25519").Bytes>;
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,2CAIpB;IAED;;OAEG;IACH,iBAEC;;CAKF;sBApHY,OAAO,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;oBAP9B,SAAS"}
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
- on: Provider.EventListenerFn;
529
- removeListener: Provider.EventListenerFn;
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":"AAKA;;;GAGG;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;;;;sBAW4D,CAAC;;;;;;sBAQzD,CAAC;;;;;;sBAS2B,CAAC;;;;;;;;;;;;sBAqCwQ,CAAC;;;;;;sBAAmP,CAAC;;;;;;;;;;;;;;;;;;sBAA6jE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAAqxP,CAAC;;;;;;sBAAkT,CAAC;;;;;;;;;;;;sBAAovB,CAAC;;;;;;sBAAuQ,CAAC;;;;;;sBAAwW,CAAC;;;;;;;;;;;;;;;;;;;;sCAA+4B,CAAC;6BAA2D,CAAC;sCAAmF,CAAC;;0CAA6F,CAAC;kCAAqD,CAAC;8BAAiD,CAAC;;;;qBAAkM,CAAC;yBAA8B,CAAC;4BAAwB,CAAC;;;;wBAAiI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;sBApExjb,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAoE+gC,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;;;;;;;;MAjEn7Q;IACxB,uBAAsB;IACtB,gCAAsB;IACtB,kBAA0B;IAsC5B;;;;OAIG;IACH,cAFW,UAAU,wCAQpB;IAED;;OAEG;IACH,iBAEC;CAKF;sBAxFY,OAAO,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;uBACrC,OAAO,IAAI,EAAE,QAAQ,CAAC,QAAQ;oBANvB,SAAS;yBAEJ,IAAI"}
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<import("@noble/secp256k1").Bytes>;
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,6CAKpB;IAED;;OAEG;IACH,iBAEC;;CAKF;sBA9GY,OAAO,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC;oBAL9B,SAAS"}
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"}
@@ -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
@@ -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;CACnB;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;CACpC"}
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} param1
4
+ * @param {import('../types.js').VerifyInput} input
5
5
  * @returns
6
6
  */
7
- export function verify(type: "ES256" | "ES384" | "ES512" | "ES256K", { signature, message, did }: import("../types.js").VerifyInput): Promise<boolean>;
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":"AAyDA;;;;;GAKG;AACH,6BAJW,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,+BACtC,OAAO,aAAa,EAAE,WAAW,oBAK3C;AAhBD,qFAAqF;AACrF,uBADW,OAAO,aAAa,EAAE,QAAQ,CAAC,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC,CAMhF"}
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":"AASA;;;;GAIG;AACH,sCAFW,KAAK,MAAM,EAAE,+BAIvB;AAED;;GAEG;AACH,qCAFW,UAAU,+BAKpB;;AAkBD,oDAAoD;AACpD,uBADW,OAAO,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAG/C"}
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,EAM5C;IAHC,iFAAwB;IACxB,gCACmE;;CAYtE;wBA/DY,OAAO,UAAU,EAAE,SAAS"}
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.5.1",
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.0.0",
85
- "@noble/hashes": "^2.0.0",
86
- "@noble/secp256k1": "^3.0.0",
87
- "ox": "^0.9.6",
88
- "iso-base": "^4.2.1",
89
- "iso-did": "^2.1.1"
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": "^24.5.2",
95
+ "@types/node": "^26.6.2",
95
96
  "assert": "^2.1.0",
96
- "mocha": "^11.7.2",
97
- "playwright-test": "^14.1.12",
98
- "type-fest": "^5.0.1",
99
- "typescript": "5.9.2",
100
- "viem": "^2.37.6"
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>
@@ -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
  /**
@@ -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
  }
@@ -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} param1
65
+ * @param {import('../types.js').VerifyInput} input
62
66
  * @returns
63
67
  */
64
- export async function verify(type, { signature, message, did }) {
65
- return await verifier[type]({ signature, message, did })
68
+ export async function verify(type, input) {
69
+ return await verifier[type](input)
66
70
  }
@@ -1,9 +1,11 @@
1
1
  import { keccak_256 } from '@noble/hashes/sha3.js'
2
- import { recoverPublicKeyAsync, Signature, verifyAsync } from '@noble/secp256k1'
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 * as SignatureOx from 'ox/Signature'
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
- const msgHash = getSignPayload(message)
30
- const sigRecovered = Signature.fromBytes(signature.slice(0, 64))
31
- .addRecoveryBit(SignatureOx.vToYParity(signature[64]))
32
- .toBytes('recovered')
33
- const pubKey = await recoverPublicKeyAsync(sigRecovered, msgHash, {
34
- prehash: false,
35
- })
36
-
37
- return await verifyAsync(sigRecovered, msgHash, pubKey, {
38
- prehash: false,
39
- format: 'recovered',
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
  }