@metamask-previews/eth-sig-util 9.0.0-preview-9dfb538be → 9.0.0-preview-a2a3d82

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/CHANGELOG.md CHANGED
@@ -11,10 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  - **BREAKING:** Drop CommonJS support ([#11349](https://github.com/MetaMask/core/pull/11349), [#11362](https://github.com/MetaMask/core/pull/11362))
13
13
  - This package is now ESM-only, but can still be used in CommonJS projects via `require(esm)` in modern Node.js versions (22+), or dynamic imports in older Node.js versions.
14
+ - **BREAKING:** Make `encrypt`, `encryptSafely`, `decrypt`, `decryptSafely`, and `getEncryptionPublicKey` asynchronous ([#11388](https://github.com/MetaMask/core/pull/11388))
15
+ - These functions now return a `Promise`. Callers must `await` the result.
16
+ - The encrypted data format is unchanged, so data encrypted by previous versions can still be decrypted.
17
+ - Decrypting with an invalid nonce length now throws an error from `@noble/ciphers` instead of `bad nonce size`.
14
18
  - **BREAKING:** Bump TypeScript target to ES2022 ([#11348](https://github.com/MetaMask/core/pull/11348))
15
19
  - Bump `@metamask/utils` from `^11.0.1` to `^12.0.1` ([#11349](https://github.com/MetaMask/core/pull/11349))
16
20
  - Bump `ethereum-cryptography` from `^2.1.2` to `^3.2.0` ([#11349](https://github.com/MetaMask/core/pull/11349))
17
- - Bump `@scure/base` from `~1.1.3` to `^1.2.6` ([#11349](https://github.com/MetaMask/core/pull/11349))
18
21
  - This package was migrated from `MetaMask/eth-sig-util` to the
19
22
  `MetaMask/core` monorepo ([#11327](https://github.com/MetaMask/core/pull/11327))
20
23
  - See [`MetaMask/eth-sig-util`](https://github.com/MetaMask/eth-sig-util/blob/73725fc31a51e4d5f6b72d8e5f470c0b10c9d57f/CHANGELOG.md)
@@ -23,5 +26,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
26
  ### Fixed
24
27
 
25
28
  - Correct padding calculation so that every ciphertext is a multiple of 2048 bytes ([#11386](https://github.com/MetaMask/core/pull/11386))
29
+ - Reject non-integer and unsafe-integer `chainId` and `nonce` values in `signEIP7702Authorization`, `recoverEIP7702Authorization`, and `hashEIP7702Authorization` ([#11392](https://github.com/MetaMask/core/pull/11392))
26
30
 
27
31
  [Unreleased]: https://github.com/MetaMask/core/
@@ -17,7 +17,7 @@ export declare function encrypt({ publicKey, data, version, }: {
17
17
  publicKey: string;
18
18
  data: unknown;
19
19
  version: string;
20
- }): EthEncryptedData;
20
+ }): Promise<EthEncryptedData>;
21
21
  /**
22
22
  * Encrypt a message in a way that obscures the message length.
23
23
  *
@@ -34,7 +34,7 @@ export declare function encryptSafely({ publicKey, data, version, }: {
34
34
  publicKey: string;
35
35
  data: unknown;
36
36
  version: string;
37
- }): EthEncryptedData;
37
+ }): Promise<EthEncryptedData>;
38
38
  /**
39
39
  * Decrypt a message.
40
40
  *
@@ -46,7 +46,7 @@ export declare function encryptSafely({ publicKey, data, version, }: {
46
46
  export declare function decrypt({ encryptedData, privateKey, }: {
47
47
  encryptedData: EthEncryptedData;
48
48
  privateKey: string;
49
- }): string;
49
+ }): Promise<string>;
50
50
  /**
51
51
  * Decrypt a message that has been encrypted using `encryptSafely`.
52
52
  *
@@ -58,12 +58,12 @@ export declare function decrypt({ encryptedData, privateKey, }: {
58
58
  export declare function decryptSafely({ encryptedData, privateKey, }: {
59
59
  encryptedData: EthEncryptedData;
60
60
  privateKey: string;
61
- }): string;
61
+ }): Promise<string>;
62
62
  /**
63
63
  * Get the encryption public key for the given key.
64
64
  *
65
65
  * @param privateKey - The private key to generate the encryption public key with.
66
66
  * @returns The encryption public key.
67
67
  */
68
- export declare function getEncryptionPublicKey(privateKey: string): string;
68
+ export declare function getEncryptionPublicKey(privateKey: string): Promise<string>;
69
69
  //# sourceMappingURL=encryption.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAKF;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,IAAI,EACJ,OAAO,GACR,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,gBAAgB,CAkDnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,SAAS,EACT,IAAI,EACJ,OAAO,GACR,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,gBAAgB,CAiCnB;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,EACtB,aAAa,EACb,UAAU,GACX,EAAE;IACD,aAAa,EAAE,gBAAgB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAiDT;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,aAAa,EACb,UAAU,GACX,EAAE;IACD,aAAa,EAAE,gBAAgB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAST;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAKjE"}
1
+ {"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAwBF;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAAC,EAC5B,SAAS,EACT,IAAI,EACJ,OAAO,GACR,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmD5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,EAClC,SAAS,EACT,IAAI,EACJ,OAAO,GACR,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiC5B;AAED;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAAC,EAC5B,aAAa,EACb,UAAU,GACX,EAAE;IACD,aAAa,EAAE,gBAAgB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4ClB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,EAClC,aAAa,EACb,UAAU,GACX,EAAE;IACD,aAAa,EAAE,gBAAgB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,MAAM,CAAC,CAWlB;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAIjB"}
@@ -1,8 +1,26 @@
1
- import { base64, utf8 } from '@scure/base';
2
- import * as nacl from 'tweetnacl';
1
+ import { getRandomBytes } from '@metamask/cryptography';
2
+ import { generateKeyPair, getPublicKey, getSharedSecret, } from '@metamask/cryptography/x25519';
3
+ import { base64ToBytes, bytesToBase64, bytesToString, stringToBytes, } from '@metamask/utils';
4
+ import { hsalsa, xsalsa20poly1305 } from '@noble/ciphers/salsa';
5
+ import { u8, u32 } from '@noble/ciphers/utils';
3
6
  import { isNullish } from './utils.js';
4
7
  const DEFAULT_PADDING_LENGTH = 2048;
5
- const NACL_EXTRA_BYTES = 16;
8
+ // https://github.com/dchest/tweetnacl-js/blob/master/nacl.js#L128
9
+ const HSALSA_SIGMA = u32(stringToBytes('expand 32-byte k'));
10
+ /**
11
+ * Derive the symmetric key used for `x25519-xsalsa20-poly1305`.
12
+ *
13
+ * @param privateKey - The 32-byte X25519 private key.
14
+ * @param publicKey - The 32-byte X25519 public key of the peer.
15
+ * @returns The 32-byte symmetric key.
16
+ */
17
+ async function deriveEncryptionKey(privateKey, publicKey) {
18
+ const sharedSecret = await getSharedSecret(privateKey, publicKey);
19
+ const key = new Uint32Array(8);
20
+ // https://github.com/dchest/tweetnacl-js/blob/master/nacl.js#L451
21
+ hsalsa(HSALSA_SIGMA, u32(sharedSecret), new Uint32Array(4), key);
22
+ return u8(key);
23
+ }
6
24
  /**
7
25
  * Encrypt a message.
8
26
  *
@@ -12,7 +30,7 @@ const NACL_EXTRA_BYTES = 16;
12
30
  * @param options.version - The type of encryption to use.
13
31
  * @returns The encrypted data.
14
32
  */
15
- export function encrypt({ publicKey, data, version, }) {
33
+ export async function encrypt({ publicKey, data, version, }) {
16
34
  if (isNullish(publicKey)) {
17
35
  throw new Error('Missing publicKey parameter');
18
36
  }
@@ -28,25 +46,26 @@ export function encrypt({ publicKey, data, version, }) {
28
46
  throw new Error('Message data must be given as a string');
29
47
  }
30
48
  // generate ephemeral keypair
31
- const ephemeralKeyPair = nacl.box.keyPair();
49
+ const ephemeralKeyPair = await generateKeyPair();
32
50
  // assemble encryption parameters - from string to UInt8
33
51
  let pubKeyUInt8Array;
34
52
  try {
35
- pubKeyUInt8Array = base64.decode(publicKey);
53
+ pubKeyUInt8Array = base64ToBytes(publicKey);
36
54
  }
37
55
  catch {
38
56
  throw new Error('Bad public key');
39
57
  }
40
- const msgParamsUInt8Array = utf8.decode(data);
41
- const nonce = nacl.randomBytes(nacl.box.nonceLength);
58
+ const msgParamsUInt8Array = stringToBytes(data);
59
+ const nonce = getRandomBytes(xsalsa20poly1305.nonceLength);
42
60
  // encrypt
43
- const encryptedMessage = nacl.box(msgParamsUInt8Array, nonce, pubKeyUInt8Array, ephemeralKeyPair.secretKey);
61
+ const key = await deriveEncryptionKey(ephemeralKeyPair.privateKey, pubKeyUInt8Array);
62
+ const encryptedMessage = xsalsa20poly1305(key, nonce).encrypt(msgParamsUInt8Array);
44
63
  // handle encrypted data
45
64
  const output = {
46
65
  version: 'x25519-xsalsa20-poly1305',
47
- nonce: base64.encode(nonce),
48
- ephemPublicKey: base64.encode(ephemeralKeyPair.publicKey),
49
- ciphertext: base64.encode(encryptedMessage),
66
+ nonce: bytesToBase64(nonce),
67
+ ephemPublicKey: bytesToBase64(ephemeralKeyPair.publicKey),
68
+ ciphertext: bytesToBase64(encryptedMessage),
50
69
  };
51
70
  // return encrypted msg data
52
71
  return output;
@@ -67,7 +86,7 @@ export function encrypt({ publicKey, data, version, }) {
67
86
  * @param options.version - The type of encryption to use.
68
87
  * @returns The encrypted data.
69
88
  */
70
- export function encryptSafely({ publicKey, data, version, }) {
89
+ export async function encryptSafely({ publicKey, data, version, }) {
71
90
  if (isNullish(publicKey)) {
72
91
  throw new Error('Missing publicKey parameter');
73
92
  }
@@ -89,7 +108,7 @@ export function encryptSafely({ publicKey, data, version, }) {
89
108
  };
90
109
  // calculate padding
91
110
  const ciphertextLength = Buffer.byteLength(JSON.stringify(dataWithPadding), 'utf-8') +
92
- NACL_EXTRA_BYTES;
111
+ xsalsa20poly1305.tagLength;
93
112
  const remainder = ciphertextLength % DEFAULT_PADDING_LENGTH;
94
113
  const padLength = remainder === 0 ? 0 : DEFAULT_PADDING_LENGTH - remainder;
95
114
  dataWithPadding.padding = '0'.repeat(padLength);
@@ -104,7 +123,7 @@ export function encryptSafely({ publicKey, data, version, }) {
104
123
  * @param options.privateKey - The private key to decrypt with.
105
124
  * @returns The decrypted message.
106
125
  */
107
- export function decrypt({ encryptedData, privateKey, }) {
126
+ export async function decrypt({ encryptedData, privateKey, }) {
108
127
  if (isNullish(encryptedData)) {
109
128
  throw new Error('Missing encryptedData parameter');
110
129
  }
@@ -114,24 +133,23 @@ export function decrypt({ encryptedData, privateKey, }) {
114
133
  switch (encryptedData.version) {
115
134
  case 'x25519-xsalsa20-poly1305': {
116
135
  const receiverPrivateKeyUint8Array = Buffer.from(privateKey, 'hex');
117
- const receiverEncryptionPrivateKey = nacl.box.keyPair.fromSecretKey(receiverPrivateKeyUint8Array).secretKey;
118
136
  // assemble decryption parameters
119
- const nonce = base64.decode(encryptedData.nonce);
120
- const ciphertext = base64.decode(encryptedData.ciphertext);
121
- const ephemPublicKey = base64.decode(encryptedData.ephemPublicKey);
137
+ const nonce = base64ToBytes(encryptedData.nonce);
138
+ const ciphertext = base64ToBytes(encryptedData.ciphertext);
139
+ const ephemPublicKey = base64ToBytes(encryptedData.ephemPublicKey);
122
140
  // decrypt
123
- const decryptedMessage = nacl.box.open(ciphertext, nonce, ephemPublicKey, receiverEncryptionPrivateKey);
141
+ const key = await deriveEncryptionKey(receiverPrivateKeyUint8Array, ephemPublicKey);
142
+ const cipher = xsalsa20poly1305(key, nonce);
143
+ let decryptedMessage;
144
+ try {
145
+ decryptedMessage = cipher.decrypt(ciphertext);
146
+ }
147
+ catch {
148
+ throw new Error('Decryption failed.');
149
+ }
124
150
  // return decrypted msg data
125
151
  try {
126
- if (!decryptedMessage) {
127
- throw new Error();
128
- }
129
- const output = utf8.encode(decryptedMessage);
130
- // TODO: This is probably extraneous but was kept to minimize changes during refactor
131
- if (!output) {
132
- throw new Error();
133
- }
134
- return output;
152
+ return bytesToString(decryptedMessage);
135
153
  }
136
154
  catch (error) {
137
155
  if (error instanceof Error && error.message.length) {
@@ -152,14 +170,14 @@ export function decrypt({ encryptedData, privateKey, }) {
152
170
  * @param options.privateKey - The private key to decrypt with.
153
171
  * @returns The decrypted message.
154
172
  */
155
- export function decryptSafely({ encryptedData, privateKey, }) {
173
+ export async function decryptSafely({ encryptedData, privateKey, }) {
156
174
  if (isNullish(encryptedData)) {
157
175
  throw new Error('Missing encryptedData parameter');
158
176
  }
159
177
  else if (isNullish(privateKey)) {
160
178
  throw new Error('Missing privateKey parameter');
161
179
  }
162
- const dataWithPadding = JSON.parse(decrypt({ encryptedData, privateKey }));
180
+ const dataWithPadding = JSON.parse(await decrypt({ encryptedData, privateKey }));
163
181
  return dataWithPadding.data;
164
182
  }
165
183
  /**
@@ -168,9 +186,9 @@ export function decryptSafely({ encryptedData, privateKey, }) {
168
186
  * @param privateKey - The private key to generate the encryption public key with.
169
187
  * @returns The encryption public key.
170
188
  */
171
- export function getEncryptionPublicKey(privateKey) {
189
+ export async function getEncryptionPublicKey(privateKey) {
172
190
  const privateKeyUint8Array = Buffer.from(privateKey, 'hex');
173
- const encryptionPublicKey = nacl.box.keyPair.fromSecretKey(privateKeyUint8Array).publicKey;
174
- return base64.encode(encryptionPublicKey);
191
+ const encryptionPublicKey = await getPublicKey(privateKeyUint8Array);
192
+ return bytesToBase64(encryptionPublicKey);
175
193
  }
176
194
  //# sourceMappingURL=encryption.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"encryption.js","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"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","sourcesContent":["import { base64, utf8 } from '@scure/base';\nimport * as nacl from 'tweetnacl';\n\nimport { isNullish } from './utils.js';\n\nexport type EthEncryptedData = {\n version: string;\n nonce: string;\n ephemPublicKey: string;\n ciphertext: string;\n};\n\nconst DEFAULT_PADDING_LENGTH = 2048;\nconst NACL_EXTRA_BYTES = 16;\n\n/**\n * Encrypt a message.\n *\n * @param options - The encryption options.\n * @param options.publicKey - The public key of the message recipient.\n * @param options.data - The message data.\n * @param options.version - The type of encryption to use.\n * @returns The encrypted data.\n */\nexport function encrypt({\n publicKey,\n data,\n version,\n}: {\n publicKey: string;\n data: unknown;\n version: string;\n}): EthEncryptedData {\n if (isNullish(publicKey)) {\n throw new Error('Missing publicKey parameter');\n } else if (isNullish(data)) {\n throw new Error('Missing data parameter');\n } else if (isNullish(version)) {\n throw new Error('Missing version parameter');\n }\n\n switch (version) {\n case 'x25519-xsalsa20-poly1305': {\n if (typeof data !== 'string') {\n throw new Error('Message data must be given as a string');\n }\n // generate ephemeral keypair\n const ephemeralKeyPair = nacl.box.keyPair();\n\n // assemble encryption parameters - from string to UInt8\n let pubKeyUInt8Array: Uint8Array;\n try {\n pubKeyUInt8Array = base64.decode(publicKey);\n } catch {\n throw new Error('Bad public key');\n }\n\n const msgParamsUInt8Array = utf8.decode(data);\n const nonce = nacl.randomBytes(nacl.box.nonceLength);\n\n // encrypt\n const encryptedMessage = nacl.box(\n msgParamsUInt8Array,\n nonce,\n pubKeyUInt8Array,\n ephemeralKeyPair.secretKey,\n );\n\n // handle encrypted data\n const output = {\n version: 'x25519-xsalsa20-poly1305',\n nonce: base64.encode(nonce),\n ephemPublicKey: base64.encode(ephemeralKeyPair.publicKey),\n ciphertext: base64.encode(encryptedMessage),\n };\n // return encrypted msg data\n return output;\n }\n\n default:\n throw new Error('Encryption type/version not supported');\n }\n}\n\n/**\n * Encrypt a message in a way that obscures the message length.\n *\n * The message is padded to a multiple of 2048 before being encrypted so that the length of the\n * resulting encrypted message can't be used to guess the exact length of the original message.\n *\n * @param options - The encryption options.\n * @param options.publicKey - The public key of the message recipient.\n * @param options.data - The message data.\n * @param options.version - The type of encryption to use.\n * @returns The encrypted data.\n */\nexport function encryptSafely({\n publicKey,\n data,\n version,\n}: {\n publicKey: string;\n data: unknown;\n version: string;\n}): EthEncryptedData {\n if (isNullish(publicKey)) {\n throw new Error('Missing publicKey parameter');\n } else if (isNullish(data)) {\n throw new Error('Missing data parameter');\n } else if (isNullish(version)) {\n throw new Error('Missing version parameter');\n }\n\n if (typeof data === 'object' && data && 'toJSON' in data) {\n // remove toJSON attack vector\n // TODO, check all possible children\n throw new Error(\n 'Cannot encrypt with toJSON property. Please remove toJSON property',\n );\n }\n\n // add padding\n const dataWithPadding = {\n data,\n padding: '',\n };\n\n // calculate padding\n const ciphertextLength =\n Buffer.byteLength(JSON.stringify(dataWithPadding), 'utf-8') +\n NACL_EXTRA_BYTES;\n const remainder = ciphertextLength % DEFAULT_PADDING_LENGTH;\n const padLength = remainder === 0 ? 0 : DEFAULT_PADDING_LENGTH - remainder;\n dataWithPadding.padding = '0'.repeat(padLength);\n\n const paddedMessage = JSON.stringify(dataWithPadding);\n return encrypt({ publicKey, data: paddedMessage, version });\n}\n\n/**\n * Decrypt a message.\n *\n * @param options - The decryption options.\n * @param options.encryptedData - The encrypted data.\n * @param options.privateKey - The private key to decrypt with.\n * @returns The decrypted message.\n */\nexport function decrypt({\n encryptedData,\n privateKey,\n}: {\n encryptedData: EthEncryptedData;\n privateKey: string;\n}): string {\n if (isNullish(encryptedData)) {\n throw new Error('Missing encryptedData parameter');\n } else if (isNullish(privateKey)) {\n throw new Error('Missing privateKey parameter');\n }\n\n switch (encryptedData.version) {\n case 'x25519-xsalsa20-poly1305': {\n const receiverPrivateKeyUint8Array = Buffer.from(privateKey, 'hex');\n const receiverEncryptionPrivateKey = nacl.box.keyPair.fromSecretKey(\n receiverPrivateKeyUint8Array,\n ).secretKey;\n\n // assemble decryption parameters\n const nonce = base64.decode(encryptedData.nonce);\n const ciphertext = base64.decode(encryptedData.ciphertext);\n const ephemPublicKey = base64.decode(encryptedData.ephemPublicKey);\n\n // decrypt\n const decryptedMessage = nacl.box.open(\n ciphertext,\n nonce,\n ephemPublicKey,\n receiverEncryptionPrivateKey,\n );\n\n // return decrypted msg data\n try {\n if (!decryptedMessage) {\n throw new Error();\n }\n const output = utf8.encode(decryptedMessage);\n // TODO: This is probably extraneous but was kept to minimize changes during refactor\n if (!output) {\n throw new Error();\n }\n return output;\n } catch (error) {\n if (error instanceof Error && error.message.length) {\n throw new Error(`Decryption failed: ${error.message}`);\n }\n throw new Error(`Decryption failed.`);\n }\n }\n\n default:\n throw new Error('Encryption type/version not supported.');\n }\n}\n\n/**\n * Decrypt a message that has been encrypted using `encryptSafely`.\n *\n * @param options - The decryption options.\n * @param options.encryptedData - The encrypted data.\n * @param options.privateKey - The private key to decrypt with.\n * @returns The decrypted message.\n */\nexport function decryptSafely({\n encryptedData,\n privateKey,\n}: {\n encryptedData: EthEncryptedData;\n privateKey: string;\n}): string {\n if (isNullish(encryptedData)) {\n throw new Error('Missing encryptedData parameter');\n } else if (isNullish(privateKey)) {\n throw new Error('Missing privateKey parameter');\n }\n\n const dataWithPadding = JSON.parse(decrypt({ encryptedData, privateKey }));\n return dataWithPadding.data;\n}\n\n/**\n * Get the encryption public key for the given key.\n *\n * @param privateKey - The private key to generate the encryption public key with.\n * @returns The encryption public key.\n */\nexport function getEncryptionPublicKey(privateKey: string): string {\n const privateKeyUint8Array = Buffer.from(privateKey, 'hex');\n const encryptionPublicKey =\n nacl.box.keyPair.fromSecretKey(privateKeyUint8Array).publicKey;\n return base64.encode(encryptionPublicKey);\n}\n"]}
1
+ {"version":3,"file":"encryption.js","sourceRoot":"","sources":["../src/encryption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,eAAe,GAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AASvC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AACpC,kEAAkE;AAClE,MAAM,YAAY,GAAG,GAAG,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAE5D;;;;;;GAMG;AACH,KAAK,UAAU,mBAAmB,CAChC,UAAmC,EACnC,SAAkC;IAElC,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;IAC/B,kEAAkE;IAClE,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjE,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;AACjB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAC5B,SAAS,EACT,IAAI,EACJ,OAAO,GAKR;IACC,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;SAAM,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;SAAM,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,0BAA0B,EAAE,CAAC;YAChC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC5D,CAAC;YACD,6BAA6B;YAC7B,MAAM,gBAAgB,GAAG,MAAM,eAAe,EAAE,CAAC;YAEjD,wDAAwD;YACxD,IAAI,gBAAyC,CAAC;YAC9C,IAAI,CAAC;gBACH,gBAAgB,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YAC9C,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACpC,CAAC;YAED,MAAM,mBAAmB,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAChD,MAAM,KAAK,GAAG,cAAc,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;YAE3D,UAAU;YACV,MAAM,GAAG,GAAG,MAAM,mBAAmB,CACnC,gBAAgB,CAAC,UAAU,EAC3B,gBAAgB,CACjB,CAAC;YACF,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,CAC3D,mBAAmB,CACpB,CAAC;YAEF,wBAAwB;YACxB,MAAM,MAAM,GAAG;gBACb,OAAO,EAAE,0BAA0B;gBACnC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC;gBAC3B,cAAc,EAAE,aAAa,CAAC,gBAAgB,CAAC,SAAS,CAAC;gBACzD,UAAU,EAAE,aAAa,CAAC,gBAAgB,CAAC;aAC5C,CAAC;YACF,4BAA4B;YAC5B,OAAO,MAAM,CAAC;QAChB,CAAC;QAED;YACE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAClC,SAAS,EACT,IAAI,EACJ,OAAO,GAKR;IACC,IAAI,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;SAAM,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;SAAM,IAAI,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACzD,8BAA8B;QAC9B,oCAAoC;QACpC,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;IACJ,CAAC;IAED,cAAc;IACd,MAAM,eAAe,GAAG;QACtB,IAAI;QACJ,OAAO,EAAE,EAAE;KACZ,CAAC;IAEF,oBAAoB;IACpB,MAAM,gBAAgB,GACpB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;QAC3D,gBAAgB,CAAC,SAAS,CAAC;IAC7B,MAAM,SAAS,GAAG,gBAAgB,GAAG,sBAAsB,CAAC;IAC5D,MAAM,SAAS,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,sBAAsB,GAAG,SAAS,CAAC;IAC3E,eAAe,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAEhD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAC5B,aAAa,EACb,UAAU,GAIX;IACC,IAAI,SAAS,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;SAAM,IAAI,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,QAAQ,aAAa,CAAC,OAAO,EAAE,CAAC;QAC9B,KAAK,0BAA0B,EAAE,CAAC;YAChC,MAAM,4BAA4B,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YAEpE,iCAAiC;YACjC,MAAM,KAAK,GAAG,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YACjD,MAAM,UAAU,GAAG,aAAa,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC3D,MAAM,cAAc,GAAG,aAAa,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;YAEnE,UAAU;YACV,MAAM,GAAG,GAAG,MAAM,mBAAmB,CACnC,4BAA4B,EAC5B,cAAc,CACf,CAAC;YACF,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAE5C,IAAI,gBAA4B,CAAC;YACjC,IAAI,CAAC;gBACH,gBAAgB,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;YACxC,CAAC;YAED,4BAA4B;YAC5B,IAAI,CAAC;gBACH,OAAO,aAAa,CAAC,gBAAgB,CAAC,CAAC;YACzC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBACnD,MAAM,IAAI,KAAK,CAAC,sBAAsB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBACzD,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED;YACE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAClC,aAAa,EACb,UAAU,GAIX;IACC,IAAI,SAAS,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;SAAM,IAAI,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAChC,MAAM,OAAO,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAC7C,CAAC;IACF,OAAO,eAAe,CAAC,IAAI,CAAC;AAC9B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,UAAkB;IAElB,MAAM,oBAAoB,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,mBAAmB,GAAG,MAAM,YAAY,CAAC,oBAAoB,CAAC,CAAC;IACrE,OAAO,aAAa,CAAC,mBAAmB,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["import { getRandomBytes } from '@metamask/cryptography';\nimport {\n generateKeyPair,\n getPublicKey,\n getSharedSecret,\n} from '@metamask/cryptography/x25519';\nimport {\n base64ToBytes,\n bytesToBase64,\n bytesToString,\n stringToBytes,\n} from '@metamask/utils';\nimport { hsalsa, xsalsa20poly1305 } from '@noble/ciphers/salsa';\nimport { u8, u32 } from '@noble/ciphers/utils';\n\nimport { isNullish } from './utils.js';\n\nexport type EthEncryptedData = {\n version: string;\n nonce: string;\n ephemPublicKey: string;\n ciphertext: string;\n};\n\nconst DEFAULT_PADDING_LENGTH = 2048;\n// https://github.com/dchest/tweetnacl-js/blob/master/nacl.js#L128\nconst HSALSA_SIGMA = u32(stringToBytes('expand 32-byte k'));\n\n/**\n * Derive the symmetric key used for `x25519-xsalsa20-poly1305`.\n *\n * @param privateKey - The 32-byte X25519 private key.\n * @param publicKey - The 32-byte X25519 public key of the peer.\n * @returns The 32-byte symmetric key.\n */\nasync function deriveEncryptionKey(\n privateKey: Uint8Array<ArrayBuffer>,\n publicKey: Uint8Array<ArrayBuffer>,\n): Promise<Uint8Array> {\n const sharedSecret = await getSharedSecret(privateKey, publicKey);\n const key = new Uint32Array(8);\n // https://github.com/dchest/tweetnacl-js/blob/master/nacl.js#L451\n hsalsa(HSALSA_SIGMA, u32(sharedSecret), new Uint32Array(4), key);\n return u8(key);\n}\n\n/**\n * Encrypt a message.\n *\n * @param options - The encryption options.\n * @param options.publicKey - The public key of the message recipient.\n * @param options.data - The message data.\n * @param options.version - The type of encryption to use.\n * @returns The encrypted data.\n */\nexport async function encrypt({\n publicKey,\n data,\n version,\n}: {\n publicKey: string;\n data: unknown;\n version: string;\n}): Promise<EthEncryptedData> {\n if (isNullish(publicKey)) {\n throw new Error('Missing publicKey parameter');\n } else if (isNullish(data)) {\n throw new Error('Missing data parameter');\n } else if (isNullish(version)) {\n throw new Error('Missing version parameter');\n }\n\n switch (version) {\n case 'x25519-xsalsa20-poly1305': {\n if (typeof data !== 'string') {\n throw new Error('Message data must be given as a string');\n }\n // generate ephemeral keypair\n const ephemeralKeyPair = await generateKeyPair();\n\n // assemble encryption parameters - from string to UInt8\n let pubKeyUInt8Array: Uint8Array<ArrayBuffer>;\n try {\n pubKeyUInt8Array = base64ToBytes(publicKey);\n } catch {\n throw new Error('Bad public key');\n }\n\n const msgParamsUInt8Array = stringToBytes(data);\n const nonce = getRandomBytes(xsalsa20poly1305.nonceLength);\n\n // encrypt\n const key = await deriveEncryptionKey(\n ephemeralKeyPair.privateKey,\n pubKeyUInt8Array,\n );\n const encryptedMessage = xsalsa20poly1305(key, nonce).encrypt(\n msgParamsUInt8Array,\n );\n\n // handle encrypted data\n const output = {\n version: 'x25519-xsalsa20-poly1305',\n nonce: bytesToBase64(nonce),\n ephemPublicKey: bytesToBase64(ephemeralKeyPair.publicKey),\n ciphertext: bytesToBase64(encryptedMessage),\n };\n // return encrypted msg data\n return output;\n }\n\n default:\n throw new Error('Encryption type/version not supported');\n }\n}\n\n/**\n * Encrypt a message in a way that obscures the message length.\n *\n * The message is padded to a multiple of 2048 before being encrypted so that the length of the\n * resulting encrypted message can't be used to guess the exact length of the original message.\n *\n * @param options - The encryption options.\n * @param options.publicKey - The public key of the message recipient.\n * @param options.data - The message data.\n * @param options.version - The type of encryption to use.\n * @returns The encrypted data.\n */\nexport async function encryptSafely({\n publicKey,\n data,\n version,\n}: {\n publicKey: string;\n data: unknown;\n version: string;\n}): Promise<EthEncryptedData> {\n if (isNullish(publicKey)) {\n throw new Error('Missing publicKey parameter');\n } else if (isNullish(data)) {\n throw new Error('Missing data parameter');\n } else if (isNullish(version)) {\n throw new Error('Missing version parameter');\n }\n\n if (typeof data === 'object' && data && 'toJSON' in data) {\n // remove toJSON attack vector\n // TODO, check all possible children\n throw new Error(\n 'Cannot encrypt with toJSON property. Please remove toJSON property',\n );\n }\n\n // add padding\n const dataWithPadding = {\n data,\n padding: '',\n };\n\n // calculate padding\n const ciphertextLength =\n Buffer.byteLength(JSON.stringify(dataWithPadding), 'utf-8') +\n xsalsa20poly1305.tagLength;\n const remainder = ciphertextLength % DEFAULT_PADDING_LENGTH;\n const padLength = remainder === 0 ? 0 : DEFAULT_PADDING_LENGTH - remainder;\n dataWithPadding.padding = '0'.repeat(padLength);\n\n const paddedMessage = JSON.stringify(dataWithPadding);\n return encrypt({ publicKey, data: paddedMessage, version });\n}\n\n/**\n * Decrypt a message.\n *\n * @param options - The decryption options.\n * @param options.encryptedData - The encrypted data.\n * @param options.privateKey - The private key to decrypt with.\n * @returns The decrypted message.\n */\nexport async function decrypt({\n encryptedData,\n privateKey,\n}: {\n encryptedData: EthEncryptedData;\n privateKey: string;\n}): Promise<string> {\n if (isNullish(encryptedData)) {\n throw new Error('Missing encryptedData parameter');\n } else if (isNullish(privateKey)) {\n throw new Error('Missing privateKey parameter');\n }\n\n switch (encryptedData.version) {\n case 'x25519-xsalsa20-poly1305': {\n const receiverPrivateKeyUint8Array = Buffer.from(privateKey, 'hex');\n\n // assemble decryption parameters\n const nonce = base64ToBytes(encryptedData.nonce);\n const ciphertext = base64ToBytes(encryptedData.ciphertext);\n const ephemPublicKey = base64ToBytes(encryptedData.ephemPublicKey);\n\n // decrypt\n const key = await deriveEncryptionKey(\n receiverPrivateKeyUint8Array,\n ephemPublicKey,\n );\n const cipher = xsalsa20poly1305(key, nonce);\n\n let decryptedMessage: Uint8Array;\n try {\n decryptedMessage = cipher.decrypt(ciphertext);\n } catch {\n throw new Error('Decryption failed.');\n }\n\n // return decrypted msg data\n try {\n return bytesToString(decryptedMessage);\n } catch (error) {\n if (error instanceof Error && error.message.length) {\n throw new Error(`Decryption failed: ${error.message}`);\n }\n throw new Error(`Decryption failed.`);\n }\n }\n\n default:\n throw new Error('Encryption type/version not supported.');\n }\n}\n\n/**\n * Decrypt a message that has been encrypted using `encryptSafely`.\n *\n * @param options - The decryption options.\n * @param options.encryptedData - The encrypted data.\n * @param options.privateKey - The private key to decrypt with.\n * @returns The decrypted message.\n */\nexport async function decryptSafely({\n encryptedData,\n privateKey,\n}: {\n encryptedData: EthEncryptedData;\n privateKey: string;\n}): Promise<string> {\n if (isNullish(encryptedData)) {\n throw new Error('Missing encryptedData parameter');\n } else if (isNullish(privateKey)) {\n throw new Error('Missing privateKey parameter');\n }\n\n const dataWithPadding = JSON.parse(\n await decrypt({ encryptedData, privateKey }),\n );\n return dataWithPadding.data;\n}\n\n/**\n * Get the encryption public key for the given key.\n *\n * @param privateKey - The private key to generate the encryption public key with.\n * @returns The encryption public key.\n */\nexport async function getEncryptionPublicKey(\n privateKey: string,\n): Promise<string> {\n const privateKeyUint8Array = Buffer.from(privateKey, 'hex');\n const encryptionPublicKey = await getPublicKey(privateKeyUint8Array);\n return bytesToBase64(encryptionPublicKey);\n}\n"]}
@@ -81,11 +81,13 @@ function validateEIP7702Authorization(authorization) {
81
81
  throw new Error('Missing nonce parameter');
82
82
  }
83
83
  if (typeof chainId !== 'number' ||
84
+ !Number.isSafeInteger(chainId) ||
84
85
  chainId >= 2 ** CHAIN_ID_MAX_BITLENGTH ||
85
86
  chainId < 0) {
86
87
  throw new Error(`Invalid chainId: must be a non-negative number less than 2^${CHAIN_ID_MAX_BITLENGTH}`);
87
88
  }
88
89
  if (typeof nonce !== 'number' ||
90
+ !Number.isSafeInteger(nonce) ||
89
91
  nonce >= 2 ** NONCE_MAX_BITLENGTH ||
90
92
  nonce < 0) {
91
93
  throw new Error(`Invalid nonce: must be a non-negative number less than 2^${NONCE_MAX_BITLENGTH}`);
@@ -1 +1 @@
1
- {"version":3,"file":"sign-eip7702-authorization.js","sourceRoot":"","sources":["../src/sign-eip7702-authorization.ts"],"names":[],"mappings":"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","sourcesContent":["import { encode } from '@ethereumjs/rlp';\nimport { ecsign, publicToAddress, toBuffer } from '@ethereumjs/util';\nimport { bytesToHex, Hex, isValidHexAddress } from '@metamask/utils';\nimport { keccak256 } from 'ethereum-cryptography/keccak';\n\nimport { concatSig, isNullish, recoverPublicKey } from './utils.js';\n\nconst CHAIN_ID_MAX_BITLENGTH = 256;\nconst NONCE_MAX_BITLENGTH = 64;\nconst ADDRESS_BYTE_LENGTH = 20;\n\n/**\n * The authorization struct as defined in EIP-7702.\n *\n * @property chainId - The chain ID or 0 for any chain.\n * @property contractAddress - The address of the contract being authorized.\n * @property nonce - The nonce of the signing account (at the time of submission).\n */\nexport type EIP7702Authorization = [\n chainId: number,\n contractAddress: string,\n nonce: number,\n];\n\n/**\n * Sign an authorization message with the provided private key.\n *\n * @param options - The signing options.\n * @param options.privateKey - The private key to sign with.\n * @param options.authorization - The authorization data to sign.\n * @returns The '0x'-prefixed hex encoded signature.\n */\nexport function signEIP7702Authorization({\n privateKey,\n authorization,\n}: {\n privateKey: Buffer;\n authorization: EIP7702Authorization;\n}): string {\n validateEIP7702Authorization(authorization);\n\n if (isNullish(privateKey)) {\n throw new Error('Missing privateKey parameter');\n }\n\n const messageHash = hashEIP7702Authorization(authorization);\n\n const { r, s, v } = ecsign(messageHash, privateKey);\n\n // v is either 27n or 28n so is guaranteed to be a single byte\n const vBuffer = toBuffer(v);\n\n return concatSig(vBuffer, r, s);\n}\n\n/**\n * Recover the address of the account that created the given authorization\n * signature.\n *\n * @param options - The signature recovery options.\n * @param options.signature - The '0x'-prefixed hex encoded message signature.\n * @param options.authorization - The authorization data that was signed.\n * @returns The '0x'-prefixed hex address of the signer.\n */\nexport function recoverEIP7702Authorization({\n signature,\n authorization,\n}: {\n signature: string;\n authorization: EIP7702Authorization;\n}): string {\n validateEIP7702Authorization(authorization);\n\n if (isNullish(signature)) {\n throw new Error('Missing signature parameter');\n }\n\n const messageHash = hashEIP7702Authorization(authorization);\n\n const publicKey = recoverPublicKey(messageHash, signature);\n\n const sender = publicToAddress(publicKey);\n\n return bytesToHex(sender);\n}\n\n/**\n * Hash an authorization message according to the signing scheme.\n * The message is encoded according to EIP-7702.\n *\n * @param authorization - The authorization data to hash.\n * @returns The hash of the authorization message as a Buffer.\n */\nexport function hashEIP7702Authorization(\n authorization: EIP7702Authorization,\n): Buffer {\n validateEIP7702Authorization(authorization);\n\n const encodedAuthorization = encode(authorization);\n\n const message = Buffer.concat([\n Buffer.from('05', 'hex'),\n encodedAuthorization,\n ]);\n\n return Buffer.from(keccak256(message));\n}\n\n/**\n * Validates an authorization object to ensure all required parameters are present.\n *\n * @param authorization - The authorization object to validate.\n * @throws {Error} If the authorization object or any of its required parameters are missing.\n */\nfunction validateEIP7702Authorization(\n authorization: EIP7702Authorization,\n): void {\n if (isNullish(authorization)) {\n throw new Error('Missing authorization parameter');\n }\n\n const [chainId, contractAddress, nonce] = authorization;\n\n if (isNullish(chainId)) {\n throw new Error('Missing chainId parameter');\n }\n\n if (isNullish(contractAddress)) {\n throw new Error('Missing contractAddress parameter');\n }\n\n if (isNullish(nonce)) {\n throw new Error('Missing nonce parameter');\n }\n\n if (\n typeof chainId !== 'number' ||\n chainId >= 2 ** CHAIN_ID_MAX_BITLENGTH ||\n chainId < 0\n ) {\n throw new Error(\n `Invalid chainId: must be a non-negative number less than 2^${CHAIN_ID_MAX_BITLENGTH}`,\n );\n }\n\n if (\n typeof nonce !== 'number' ||\n nonce >= 2 ** NONCE_MAX_BITLENGTH ||\n nonce < 0\n ) {\n throw new Error(\n `Invalid nonce: must be a non-negative number less than 2^${NONCE_MAX_BITLENGTH}`,\n );\n }\n\n if (!isValidHexAddress(contractAddress as Hex)) {\n throw new Error(\n `Invalid contractAddress: must be a ${ADDRESS_BYTE_LENGTH} byte hex string`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"sign-eip7702-authorization.js","sourceRoot":"","sources":["../src/sign-eip7702-authorization.ts"],"names":[],"mappings":"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","sourcesContent":["import { encode } from '@ethereumjs/rlp';\nimport { ecsign, publicToAddress, toBuffer } from '@ethereumjs/util';\nimport { bytesToHex, Hex, isValidHexAddress } from '@metamask/utils';\nimport { keccak256 } from 'ethereum-cryptography/keccak';\n\nimport { concatSig, isNullish, recoverPublicKey } from './utils.js';\n\nconst CHAIN_ID_MAX_BITLENGTH = 256;\nconst NONCE_MAX_BITLENGTH = 64;\nconst ADDRESS_BYTE_LENGTH = 20;\n\n/**\n * The authorization struct as defined in EIP-7702.\n *\n * @property chainId - The chain ID or 0 for any chain.\n * @property contractAddress - The address of the contract being authorized.\n * @property nonce - The nonce of the signing account (at the time of submission).\n */\nexport type EIP7702Authorization = [\n chainId: number,\n contractAddress: string,\n nonce: number,\n];\n\n/**\n * Sign an authorization message with the provided private key.\n *\n * @param options - The signing options.\n * @param options.privateKey - The private key to sign with.\n * @param options.authorization - The authorization data to sign.\n * @returns The '0x'-prefixed hex encoded signature.\n */\nexport function signEIP7702Authorization({\n privateKey,\n authorization,\n}: {\n privateKey: Buffer;\n authorization: EIP7702Authorization;\n}): string {\n validateEIP7702Authorization(authorization);\n\n if (isNullish(privateKey)) {\n throw new Error('Missing privateKey parameter');\n }\n\n const messageHash = hashEIP7702Authorization(authorization);\n\n const { r, s, v } = ecsign(messageHash, privateKey);\n\n // v is either 27n or 28n so is guaranteed to be a single byte\n const vBuffer = toBuffer(v);\n\n return concatSig(vBuffer, r, s);\n}\n\n/**\n * Recover the address of the account that created the given authorization\n * signature.\n *\n * @param options - The signature recovery options.\n * @param options.signature - The '0x'-prefixed hex encoded message signature.\n * @param options.authorization - The authorization data that was signed.\n * @returns The '0x'-prefixed hex address of the signer.\n */\nexport function recoverEIP7702Authorization({\n signature,\n authorization,\n}: {\n signature: string;\n authorization: EIP7702Authorization;\n}): string {\n validateEIP7702Authorization(authorization);\n\n if (isNullish(signature)) {\n throw new Error('Missing signature parameter');\n }\n\n const messageHash = hashEIP7702Authorization(authorization);\n\n const publicKey = recoverPublicKey(messageHash, signature);\n\n const sender = publicToAddress(publicKey);\n\n return bytesToHex(sender);\n}\n\n/**\n * Hash an authorization message according to the signing scheme.\n * The message is encoded according to EIP-7702.\n *\n * @param authorization - The authorization data to hash.\n * @returns The hash of the authorization message as a Buffer.\n */\nexport function hashEIP7702Authorization(\n authorization: EIP7702Authorization,\n): Buffer {\n validateEIP7702Authorization(authorization);\n\n const encodedAuthorization = encode(authorization);\n\n const message = Buffer.concat([\n Buffer.from('05', 'hex'),\n encodedAuthorization,\n ]);\n\n return Buffer.from(keccak256(message));\n}\n\n/**\n * Validates an authorization object to ensure all required parameters are present.\n *\n * @param authorization - The authorization object to validate.\n * @throws {Error} If the authorization object or any of its required parameters are missing.\n */\nfunction validateEIP7702Authorization(\n authorization: EIP7702Authorization,\n): void {\n if (isNullish(authorization)) {\n throw new Error('Missing authorization parameter');\n }\n\n const [chainId, contractAddress, nonce] = authorization;\n\n if (isNullish(chainId)) {\n throw new Error('Missing chainId parameter');\n }\n\n if (isNullish(contractAddress)) {\n throw new Error('Missing contractAddress parameter');\n }\n\n if (isNullish(nonce)) {\n throw new Error('Missing nonce parameter');\n }\n\n if (\n typeof chainId !== 'number' ||\n !Number.isSafeInteger(chainId) ||\n chainId >= 2 ** CHAIN_ID_MAX_BITLENGTH ||\n chainId < 0\n ) {\n throw new Error(\n `Invalid chainId: must be a non-negative number less than 2^${CHAIN_ID_MAX_BITLENGTH}`,\n );\n }\n\n if (\n typeof nonce !== 'number' ||\n !Number.isSafeInteger(nonce) ||\n nonce >= 2 ** NONCE_MAX_BITLENGTH ||\n nonce < 0\n ) {\n throw new Error(\n `Invalid nonce: must be a non-negative number less than 2^${NONCE_MAX_BITLENGTH}`,\n );\n }\n\n if (!isValidHexAddress(contractAddress as Hex)) {\n throw new Error(\n `Invalid contractAddress: must be a ${ADDRESS_BYTE_LENGTH} byte hex string`,\n );\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/eth-sig-util",
3
- "version": "9.0.0-preview-9dfb538be",
3
+ "version": "9.0.0-preview-a2a3d82",
4
4
  "description": "A few useful functions for signing ethereum data",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -66,10 +66,10 @@
66
66
  "@ethereumjs/rlp": "^4.0.1",
67
67
  "@ethereumjs/util": "^8.1.0",
68
68
  "@metamask/abi-utils": "^3.0.0",
69
+ "@metamask/cryptography": "^1.1.2",
69
70
  "@metamask/utils": "^12.0.1",
70
- "@scure/base": "^1.2.6",
71
- "ethereum-cryptography": "^3.2.0",
72
- "tweetnacl": "^1.0.3"
71
+ "@noble/ciphers": "^1.3.0",
72
+ "ethereum-cryptography": "^3.2.0"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@metamask/auto-changelog": "^7.1.0",