@libp2p/crypto 3.0.4-856ccd708 → 3.0.4-ddaa59a60

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.
Files changed (80) hide show
  1. package/dist/index.min.js +14 -72
  2. package/dist/src/aes/cipher-mode.d.ts +2 -0
  3. package/dist/src/aes/cipher-mode.d.ts.map +1 -0
  4. package/dist/src/aes/cipher-mode.js +13 -0
  5. package/dist/src/aes/cipher-mode.js.map +1 -0
  6. package/dist/src/aes/ciphers-browser.d.ts +7 -0
  7. package/dist/src/aes/ciphers-browser.d.ts.map +1 -0
  8. package/dist/src/aes/ciphers-browser.js +26 -0
  9. package/dist/src/aes/ciphers-browser.js.map +1 -0
  10. package/dist/src/aes/ciphers.d.ts +5 -0
  11. package/dist/src/aes/ciphers.d.ts.map +1 -0
  12. package/dist/src/aes/ciphers.js +4 -0
  13. package/dist/src/aes/ciphers.js.map +1 -0
  14. package/dist/src/aes/index.d.ts +50 -0
  15. package/dist/src/aes/index.d.ts.map +1 -0
  16. package/dist/src/aes/index.js +61 -0
  17. package/dist/src/aes/index.js.map +1 -0
  18. package/dist/src/index.d.ts +2 -0
  19. package/dist/src/index.d.ts.map +1 -1
  20. package/dist/src/index.js +2 -0
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/keys/ed25519-browser.d.ts +1 -1
  23. package/dist/src/keys/ed25519-browser.d.ts.map +1 -1
  24. package/dist/src/keys/index.d.ts +2 -4
  25. package/dist/src/keys/index.d.ts.map +1 -1
  26. package/dist/src/keys/index.js +12 -7
  27. package/dist/src/keys/index.js.map +1 -1
  28. package/dist/src/keys/jwk2pem.d.ts +8 -0
  29. package/dist/src/keys/jwk2pem.d.ts.map +1 -0
  30. package/dist/src/keys/jwk2pem.js +14 -0
  31. package/dist/src/keys/jwk2pem.js.map +1 -0
  32. package/dist/src/keys/rsa-browser.d.ts +2 -0
  33. package/dist/src/keys/rsa-browser.d.ts.map +1 -1
  34. package/dist/src/keys/rsa-browser.js +25 -0
  35. package/dist/src/keys/rsa-browser.js.map +1 -1
  36. package/dist/src/keys/rsa-class.d.ts +5 -6
  37. package/dist/src/keys/rsa-class.d.ts.map +1 -1
  38. package/dist/src/keys/rsa-class.js +25 -11
  39. package/dist/src/keys/rsa-class.js.map +1 -1
  40. package/dist/src/keys/rsa-utils.d.ts +2 -15
  41. package/dist/src/keys/rsa-utils.d.ts.map +1 -1
  42. package/dist/src/keys/rsa-utils.js +39 -304
  43. package/dist/src/keys/rsa-utils.js.map +1 -1
  44. package/dist/src/keys/rsa.d.ts +2 -0
  45. package/dist/src/keys/rsa.d.ts.map +1 -1
  46. package/dist/src/keys/rsa.js +22 -2
  47. package/dist/src/keys/rsa.js.map +1 -1
  48. package/dist/src/pbkdf2.d.ts +1 -1
  49. package/dist/src/pbkdf2.d.ts.map +1 -1
  50. package/dist/src/pbkdf2.js +10 -14
  51. package/dist/src/pbkdf2.js.map +1 -1
  52. package/dist/src/util.d.ts +7 -0
  53. package/dist/src/util.d.ts.map +1 -1
  54. package/dist/src/util.js +25 -0
  55. package/dist/src/util.js.map +1 -1
  56. package/dist/src/webcrypto.d.ts +1 -3
  57. package/dist/src/webcrypto.d.ts.map +1 -1
  58. package/dist/src/webcrypto.js +11 -4
  59. package/dist/src/webcrypto.js.map +1 -1
  60. package/package.json +15 -8
  61. package/src/aes/cipher-mode.ts +15 -0
  62. package/src/aes/ciphers-browser.ts +31 -0
  63. package/src/aes/ciphers.ts +4 -0
  64. package/src/aes/index.ts +70 -0
  65. package/src/index.ts +2 -0
  66. package/src/keys/ed25519-browser.ts +1 -1
  67. package/src/keys/index.ts +12 -10
  68. package/src/keys/jwk2pem.ts +21 -0
  69. package/src/keys/rsa-browser.ts +29 -0
  70. package/src/keys/rsa-class.ts +28 -11
  71. package/src/keys/rsa-utils.ts +39 -373
  72. package/src/keys/rsa.ts +23 -2
  73. package/src/pbkdf2.ts +15 -17
  74. package/src/util.ts +29 -0
  75. package/src/webcrypto.ts +18 -5
  76. package/dist/src/webcrypto-browser.d.ts +0 -5
  77. package/dist/src/webcrypto-browser.d.ts.map +0 -1
  78. package/dist/src/webcrypto-browser.js +0 -17
  79. package/dist/src/webcrypto-browser.js.map +0 -1
  80. package/src/webcrypto-browser.ts +0 -24
@@ -1,408 +1,74 @@
1
+ import 'node-forge/lib/asn1.js'
2
+ import 'node-forge/lib/rsa.js'
1
3
  import { CodeError } from '@libp2p/interface'
2
- import { pbkdf2Async } from '@noble/hashes/pbkdf2'
3
- import { sha512 } from '@noble/hashes/sha512'
4
- import * as asn1js from 'asn1js'
4
+ // @ts-expect-error types are missing
5
+ import forge from 'node-forge/lib/forge.js'
5
6
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
6
7
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
7
- import randomBytes from '../random-bytes.js'
8
- import webcrypto from '../webcrypto.js'
9
- import { type RsaPrivateKey, unmarshalRsaPrivateKey } from './rsa-class.js'
8
+ import { bigIntegerToUintBase64url, base64urlToBigInteger } from './../util.js'
10
9
 
11
- /**
12
- * Convert a PKCS#1 in ASN1 DER format to a JWK key
13
- */
10
+ // Convert a PKCS#1 in ASN1 DER format to a JWK key
14
11
  export function pkcs1ToJwk (bytes: Uint8Array): JsonWebKey {
15
- const { result } = asn1js.fromBER(bytes)
12
+ const asn1 = forge.asn1.fromDer(uint8ArrayToString(bytes, 'ascii'))
13
+ const privateKey = forge.pki.privateKeyFromAsn1(asn1)
16
14
 
17
- // @ts-expect-error this looks fragile but DER is a canonical format so we are
18
- // safe to have deeply property chains like this
19
- const values: asn1js.Integer[] = result.valueBlock.value
20
-
21
- const key = {
22
- n: uint8ArrayToString(bnToBuf(values[1].toBigInt()), 'base64url'),
23
- e: uint8ArrayToString(bnToBuf(values[2].toBigInt()), 'base64url'),
24
- d: uint8ArrayToString(bnToBuf(values[3].toBigInt()), 'base64url'),
25
- p: uint8ArrayToString(bnToBuf(values[4].toBigInt()), 'base64url'),
26
- q: uint8ArrayToString(bnToBuf(values[5].toBigInt()), 'base64url'),
27
- dp: uint8ArrayToString(bnToBuf(values[6].toBigInt()), 'base64url'),
28
- dq: uint8ArrayToString(bnToBuf(values[7].toBigInt()), 'base64url'),
29
- qi: uint8ArrayToString(bnToBuf(values[8].toBigInt()), 'base64url'),
15
+ // https://tools.ietf.org/html/rfc7518#section-6.3.1
16
+ return {
30
17
  kty: 'RSA',
18
+ n: bigIntegerToUintBase64url(privateKey.n),
19
+ e: bigIntegerToUintBase64url(privateKey.e),
20
+ d: bigIntegerToUintBase64url(privateKey.d),
21
+ p: bigIntegerToUintBase64url(privateKey.p),
22
+ q: bigIntegerToUintBase64url(privateKey.q),
23
+ dp: bigIntegerToUintBase64url(privateKey.dP),
24
+ dq: bigIntegerToUintBase64url(privateKey.dQ),
25
+ qi: bigIntegerToUintBase64url(privateKey.qInv),
31
26
  alg: 'RS256'
32
27
  }
33
-
34
- return key
35
28
  }
36
29
 
37
- /**
38
- * Convert a JWK key into PKCS#1 in ASN1 DER format
39
- */
30
+ // Convert a JWK key into PKCS#1 in ASN1 DER format
40
31
  export function jwkToPkcs1 (jwk: JsonWebKey): Uint8Array {
41
32
  if (jwk.n == null || jwk.e == null || jwk.d == null || jwk.p == null || jwk.q == null || jwk.dp == null || jwk.dq == null || jwk.qi == null) {
42
33
  throw new CodeError('JWK was missing components', 'ERR_INVALID_PARAMETERS')
43
34
  }
44
35
 
45
- const root = new asn1js.Sequence({
46
- value: [
47
- new asn1js.Integer({ value: 0 }),
48
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.n, 'base64url'))),
49
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.e, 'base64url'))),
50
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.d, 'base64url'))),
51
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.p, 'base64url'))),
52
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.q, 'base64url'))),
53
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.dp, 'base64url'))),
54
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.dq, 'base64url'))),
55
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.qi, 'base64url')))
56
- ]
36
+ const asn1 = forge.pki.privateKeyToAsn1({
37
+ n: base64urlToBigInteger(jwk.n),
38
+ e: base64urlToBigInteger(jwk.e),
39
+ d: base64urlToBigInteger(jwk.d),
40
+ p: base64urlToBigInteger(jwk.p),
41
+ q: base64urlToBigInteger(jwk.q),
42
+ dP: base64urlToBigInteger(jwk.dp),
43
+ dQ: base64urlToBigInteger(jwk.dq),
44
+ qInv: base64urlToBigInteger(jwk.qi)
57
45
  })
58
46
 
59
- const der = root.toBER()
60
-
61
- return new Uint8Array(der, 0, der.byteLength)
47
+ return uint8ArrayFromString(forge.asn1.toDer(asn1).getBytes(), 'ascii')
62
48
  }
63
49
 
64
- /**
65
- * Convert a PKCIX in ASN1 DER format to a JWK key
66
- */
50
+ // Convert a PKCIX in ASN1 DER format to a JWK key
67
51
  export function pkixToJwk (bytes: Uint8Array): JsonWebKey {
68
- const { result } = asn1js.fromBER(bytes)
69
-
70
- // @ts-expect-error this looks fragile but DER is a canonical format so we are
71
- // safe to have deeply property chains like this
72
- const values: asn1js.Integer[] = result.valueBlock.value[1].valueBlock.value[0].valueBlock.value
52
+ const asn1 = forge.asn1.fromDer(uint8ArrayToString(bytes, 'ascii'))
53
+ const publicKey = forge.pki.publicKeyFromAsn1(asn1)
73
54
 
74
55
  return {
75
56
  kty: 'RSA',
76
- n: uint8ArrayToString(bnToBuf(values[0].toBigInt()), 'base64url'),
77
- e: uint8ArrayToString(bnToBuf(values[1].toBigInt()), 'base64url')
57
+ n: bigIntegerToUintBase64url(publicKey.n),
58
+ e: bigIntegerToUintBase64url(publicKey.e)
78
59
  }
79
60
  }
80
61
 
81
- /**
82
- * Convert a JWK key to PKCIX in ASN1 DER format
83
- */
62
+ // Convert a JWK key to PKCIX in ASN1 DER format
84
63
  export function jwkToPkix (jwk: JsonWebKey): Uint8Array {
85
64
  if (jwk.n == null || jwk.e == null) {
86
65
  throw new CodeError('JWK was missing components', 'ERR_INVALID_PARAMETERS')
87
66
  }
88
67
 
89
- const root = new asn1js.Sequence({
90
- value: [
91
- new asn1js.Sequence({
92
- value: [
93
- // rsaEncryption
94
- new asn1js.ObjectIdentifier({
95
- value: '1.2.840.113549.1.1.1'
96
- }),
97
- new asn1js.Null()
98
- ]
99
- }),
100
- // this appears to be a bug in asn1js.js - this should really be a Sequence
101
- // and not a BitString but it generates the same bytes as node-forge so 🤷‍♂️
102
- new asn1js.BitString({
103
- valueHex: new asn1js.Sequence({
104
- value: [
105
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.n, 'base64url'))),
106
- asn1js.Integer.fromBigInt(bufToBn(uint8ArrayFromString(jwk.e, 'base64url')))
107
- ]
108
- }).toBER()
109
- })
110
- ]
111
- })
112
-
113
- const der = root.toBER()
114
-
115
- return new Uint8Array(der, 0, der.byteLength)
116
- }
117
-
118
- function bnToBuf (bn: bigint): Uint8Array {
119
- let hex = bn.toString(16)
120
-
121
- if (hex.length % 2 > 0) {
122
- hex = `0${hex}`
123
- }
124
-
125
- const len = hex.length / 2
126
- const u8 = new Uint8Array(len)
127
-
128
- let i = 0
129
- let j = 0
130
-
131
- while (i < len) {
132
- u8[i] = parseInt(hex.slice(j, j + 2), 16)
133
- i += 1
134
- j += 2
135
- }
136
-
137
- return u8
138
- }
139
-
140
- function bufToBn (u8: Uint8Array): bigint {
141
- const hex: string[] = []
142
-
143
- u8.forEach(function (i) {
144
- let h = i.toString(16)
145
-
146
- if (h.length % 2 > 0) {
147
- h = `0${h}`
148
- }
149
-
150
- hex.push(h)
151
- })
152
-
153
- return BigInt('0x' + hex.join(''))
154
- }
155
-
156
- const SALT_LENGTH = 16
157
- const KEY_SIZE = 32
158
- const ITERATIONS = 10000
159
-
160
- export async function exportToPem (privateKey: RsaPrivateKey, password: string): Promise<string> {
161
- const crypto = webcrypto.get()
162
-
163
- // PrivateKeyInfo
164
- const keyWrapper = new asn1js.Sequence({
165
- value: [
166
- // version (0)
167
- new asn1js.Integer({ value: 0 }),
168
-
169
- // privateKeyAlgorithm
170
- new asn1js.Sequence({
171
- value: [
172
- // rsaEncryption OID
173
- new asn1js.ObjectIdentifier({
174
- value: '1.2.840.113549.1.1.1'
175
- }),
176
- new asn1js.Null()
177
- ]
178
- }),
179
-
180
- // PrivateKey
181
- new asn1js.OctetString({
182
- valueHex: privateKey.marshal()
183
- })
184
- ]
68
+ const asn1 = forge.pki.publicKeyToAsn1({
69
+ n: base64urlToBigInteger(jwk.n),
70
+ e: base64urlToBigInteger(jwk.e)
185
71
  })
186
72
 
187
- const keyBuf = keyWrapper.toBER()
188
- const keyArr = new Uint8Array(keyBuf, 0, keyBuf.byteLength)
189
- const salt = randomBytes(SALT_LENGTH)
190
-
191
- const encryptionKey = await pbkdf2Async(
192
- sha512,
193
- password,
194
- salt, {
195
- c: ITERATIONS,
196
- dkLen: KEY_SIZE
197
- }
198
- )
199
-
200
- const iv = randomBytes(16)
201
- const cryptoKey = await crypto.subtle.importKey('raw', encryptionKey, 'AES-CBC', false, ['encrypt'])
202
- const encrypted = await crypto.subtle.encrypt({
203
- name: 'AES-CBC',
204
- iv
205
- }, cryptoKey, keyArr)
206
-
207
- const pbkdf2Params = new asn1js.Sequence({
208
- value: [
209
- // salt
210
- new asn1js.OctetString({ valueHex: salt }),
211
-
212
- // iteration count
213
- new asn1js.Integer({ value: ITERATIONS }),
214
-
215
- // key length
216
- new asn1js.Integer({ value: KEY_SIZE }),
217
-
218
- // AlgorithmIdentifier
219
- new asn1js.Sequence({
220
- value: [
221
- // hmacWithSHA512
222
- new asn1js.ObjectIdentifier({ value: '1.2.840.113549.2.11' }),
223
- new asn1js.Null()
224
- ]
225
- })
226
- ]
227
- })
228
-
229
- const encryptionAlgorithm = new asn1js.Sequence({
230
- value: [
231
- // pkcs5PBES2
232
- new asn1js.ObjectIdentifier({
233
- value: '1.2.840.113549.1.5.13'
234
- }),
235
- new asn1js.Sequence({
236
- value: [
237
- // keyDerivationFunc
238
- new asn1js.Sequence({
239
- value: [
240
- // pkcs5PBKDF2
241
- new asn1js.ObjectIdentifier({
242
- value: '1.2.840.113549.1.5.12'
243
- }),
244
- // PBKDF2-params
245
- pbkdf2Params
246
- ]
247
- }),
248
-
249
- // encryptionScheme
250
- new asn1js.Sequence({
251
- value: [
252
- // aes256-CBC
253
- new asn1js.ObjectIdentifier({
254
- value: '2.16.840.1.101.3.4.1.42'
255
- }),
256
- // iv
257
- new asn1js.OctetString({
258
- valueHex: iv
259
- })
260
- ]
261
- })
262
- ]
263
- })
264
- ]
265
- })
266
-
267
- const finalWrapper = new asn1js.Sequence({
268
- value: [
269
- encryptionAlgorithm,
270
- new asn1js.OctetString({ valueHex: encrypted })
271
- ]
272
- })
273
-
274
- const finalWrapperBuf = finalWrapper.toBER()
275
- const finalWrapperArr = new Uint8Array(finalWrapperBuf, 0, finalWrapperBuf.byteLength)
276
-
277
- return [
278
- '-----BEGIN ENCRYPTED PRIVATE KEY-----',
279
- ...uint8ArrayToString(finalWrapperArr, 'base64pad').split(/(.{64})/).filter(Boolean),
280
- '-----END ENCRYPTED PRIVATE KEY-----'
281
- ].join('\n')
282
- }
283
-
284
- export async function importFromPem (pem: string, password: string): Promise<RsaPrivateKey> {
285
- const crypto = webcrypto.get()
286
- let plaintext: Uint8Array
287
-
288
- if (pem.includes('-----BEGIN ENCRYPTED PRIVATE KEY-----')) {
289
- const key = uint8ArrayFromString(
290
- pem
291
- .replace('-----BEGIN ENCRYPTED PRIVATE KEY-----', '')
292
- .replace('-----END ENCRYPTED PRIVATE KEY-----', '')
293
- .replace(/\n/g, '')
294
- .trim(),
295
- 'base64pad'
296
- )
297
-
298
- const { result } = asn1js.fromBER(key)
299
-
300
- const {
301
- iv,
302
- salt,
303
- iterations,
304
- keySize,
305
- cipherText
306
- } = findEncryptedPEMData(result)
307
-
308
- const encryptionKey = await pbkdf2Async(
309
- sha512,
310
- password,
311
- salt, {
312
- c: iterations,
313
- dkLen: keySize
314
- }
315
- )
316
-
317
- const cryptoKey = await crypto.subtle.importKey('raw', encryptionKey, 'AES-CBC', false, ['decrypt'])
318
- const decrypted = toUint8Array(await crypto.subtle.decrypt({
319
- name: 'AES-CBC',
320
- iv
321
- }, cryptoKey, cipherText))
322
-
323
- const { result: decryptedResult } = asn1js.fromBER(decrypted)
324
- plaintext = findPEMData(decryptedResult)
325
- } else if (pem.includes('-----BEGIN PRIVATE KEY-----')) {
326
- const key = uint8ArrayFromString(
327
- pem
328
- .replace('-----BEGIN PRIVATE KEY-----', '')
329
- .replace('-----END PRIVATE KEY-----', '')
330
- .replace(/\n/g, '')
331
- .trim(),
332
- 'base64pad'
333
- )
334
-
335
- const { result } = asn1js.fromBER(key)
336
-
337
- plaintext = findPEMData(result)
338
- } else {
339
- throw new CodeError('Could not parse private key from PEM data', 'ERR_INVALID_PARAMETERS')
340
- }
341
-
342
- return unmarshalRsaPrivateKey(plaintext)
343
- }
344
-
345
- function findEncryptedPEMData (root: any): { cipherText: Uint8Array, iv: Uint8Array, salt: Uint8Array, iterations: number, keySize: number } {
346
- const encryptionAlgorithm = root.valueBlock.value[0]
347
- const scheme = encryptionAlgorithm.valueBlock.value[0].toString()
348
-
349
- if (scheme !== 'OBJECT IDENTIFIER : 1.2.840.113549.1.5.13') {
350
- throw new CodeError('Only pkcs5PBES2 encrypted private keys are supported', 'ERR_INVALID_PARAMS')
351
- }
352
-
353
- const keyDerivationFunc = encryptionAlgorithm.valueBlock.value[1].valueBlock.value[0]
354
- const keyDerivationFuncName = keyDerivationFunc.valueBlock.value[0].toString()
355
-
356
- if (keyDerivationFuncName !== 'OBJECT IDENTIFIER : 1.2.840.113549.1.5.12') {
357
- throw new CodeError('Only pkcs5PBKDF2 key derivation functions are supported', 'ERR_INVALID_PARAMS')
358
- }
359
-
360
- const pbkdf2Params = keyDerivationFunc.valueBlock.value[1]
361
-
362
- const salt = toUint8Array(pbkdf2Params.valueBlock.value[0].getValue())
363
-
364
- let iterations = ITERATIONS
365
- let keySize = KEY_SIZE
366
-
367
- if (pbkdf2Params.valueBlock.value.length === 3) {
368
- iterations = Number((pbkdf2Params.valueBlock.value[1] as asn1js.Integer).toBigInt())
369
- keySize = Number((pbkdf2Params.valueBlock.value[2]).toBigInt())
370
- } else if (pbkdf2Params.valueBlock.value.length === 2) {
371
- throw new CodeError('Could not derive key size and iterations from PEM file - please use @libp2p/rsa to re-import your key', 'ERR_INVALID_PARAMS')
372
- }
373
-
374
- const encryptionScheme = encryptionAlgorithm.valueBlock.value[1].valueBlock.value[1]
375
- const encryptionSchemeName = encryptionScheme.valueBlock.value[0].toString()
376
-
377
- if (encryptionSchemeName === 'OBJECT IDENTIFIER : 1.2.840.113549.3.7') {
378
- // des-EDE3-CBC
379
- } else if (encryptionSchemeName === 'OBJECT IDENTIFIER : 1.3.14.3.2.7') {
380
- // des-CBC
381
- } else if (encryptionSchemeName === 'OBJECT IDENTIFIER : 2.16.840.1.101.3.4.1.2') {
382
- // aes128-CBC
383
- } else if (encryptionSchemeName === 'OBJECT IDENTIFIER : 2.16.840.1.101.3.4.1.22') {
384
- // aes192-CBC
385
- } else if (encryptionSchemeName === 'OBJECT IDENTIFIER : 2.16.840.1.101.3.4.1.42') {
386
- // aes256-CBC
387
- } else {
388
- throw new CodeError('Only AES-CBC encryption schemes are supported', 'ERR_INVALID_PARAMS')
389
- }
390
-
391
- const iv = toUint8Array(encryptionScheme.valueBlock.value[1].getValue())
392
-
393
- return {
394
- cipherText: toUint8Array(root.valueBlock.value[1].getValue()),
395
- salt,
396
- iterations,
397
- keySize,
398
- iv
399
- }
400
- }
401
-
402
- function findPEMData (seq: any): Uint8Array {
403
- return toUint8Array(seq.valueBlock.value[2].getValue())
404
- }
405
-
406
- function toUint8Array (buf: ArrayBuffer): Uint8Array {
407
- return new Uint8Array(buf, 0, buf.byteLength)
73
+ return uint8ArrayFromString(forge.asn1.toDer(asn1).getBytes(), 'ascii')
408
74
  }
package/src/keys/rsa.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import crypto from 'crypto'
2
2
  import { promisify } from 'util'
3
3
  import { CodeError } from '@libp2p/interface'
4
- import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
5
4
  import randomBytes from '../random-bytes.js'
6
5
  import * as utils from './rsa-utils.js'
7
6
  import type { JWKKeyPair } from './interface.js'
@@ -74,12 +73,34 @@ export async function hashAndVerify (key: JsonWebKey, sig: Uint8Array, msg: Uint
74
73
  return hash.verify({ format: 'jwk', key }, sig)
75
74
  }
76
75
 
76
+ const padding = crypto.constants.RSA_PKCS1_PADDING
77
+
78
+ export function encrypt (key: JsonWebKey, bytes: Uint8Array | Uint8ArrayList): Uint8Array {
79
+ if (bytes instanceof Uint8Array) {
80
+ // @ts-expect-error node types are missing jwk as a format
81
+ return crypto.publicEncrypt({ format: 'jwk', key, padding }, bytes)
82
+ } else {
83
+ // @ts-expect-error node types are missing jwk as a format
84
+ return crypto.publicEncrypt({ format: 'jwk', key, padding }, bytes.subarray())
85
+ }
86
+ }
87
+
88
+ export function decrypt (key: JsonWebKey, bytes: Uint8Array | Uint8ArrayList): Uint8Array {
89
+ if (bytes instanceof Uint8Array) {
90
+ // @ts-expect-error node types are missing jwk as a format
91
+ return crypto.privateDecrypt({ format: 'jwk', key, padding }, bytes)
92
+ } else {
93
+ // @ts-expect-error node types are missing jwk as a format
94
+ return crypto.privateDecrypt({ format: 'jwk', key, padding }, bytes.subarray())
95
+ }
96
+ }
97
+
77
98
  export function keySize (jwk: JsonWebKey): number {
78
99
  if (jwk.kty !== 'RSA') {
79
100
  throw new CodeError('invalid key type', 'ERR_INVALID_KEY_TYPE')
80
101
  } else if (jwk.n == null) {
81
102
  throw new CodeError('invalid key modulus', 'ERR_INVALID_KEY_MODULUS')
82
103
  }
83
- const modulus = uint8ArrayFromString(jwk.n, 'base64url')
104
+ const modulus = Buffer.from(jwk.n, 'base64')
84
105
  return modulus.length * 8
85
106
  }
package/src/pbkdf2.ts CHANGED
@@ -1,41 +1,39 @@
1
1
  import { CodeError } from '@libp2p/interface'
2
- import { pbkdf2 as pbkdf2Sync } from '@noble/hashes/pbkdf2'
3
- import { sha1 } from '@noble/hashes/sha1'
4
- import { sha256 } from '@noble/hashes/sha256'
5
- import { sha512 } from '@noble/hashes/sha512'
6
- import { base64 } from 'multiformats/bases/base64'
2
+ // @ts-expect-error types are missing
3
+ import forgePbkdf2 from 'node-forge/lib/pbkdf2.js'
4
+ // @ts-expect-error types are missing
5
+ import forgeUtil from 'node-forge/lib/util.js'
7
6
 
8
7
  /**
9
- * Maps an IPFS hash name to its @noble/hashes equivalent.
8
+ * Maps an IPFS hash name to its node-forge equivalent.
10
9
  *
11
10
  * See https://github.com/multiformats/multihash/blob/master/hashtable.csv
12
11
  *
13
12
  * @private
14
13
  */
15
14
  const hashName = {
16
- sha1,
17
- 'sha2-256': sha256,
18
- 'sha2-512': sha512
15
+ sha1: 'sha1',
16
+ 'sha2-256': 'sha256',
17
+ 'sha2-512': 'sha512'
19
18
  }
20
19
 
21
20
  /**
22
21
  * Computes the Password-Based Key Derivation Function 2.
23
22
  */
24
- export default function pbkdf2 (password: string, salt: string | Uint8Array, iterations: number, keySize: number, hash: string): string {
23
+ export default function pbkdf2 (password: string, salt: string, iterations: number, keySize: number, hash: string): string {
25
24
  if (hash !== 'sha1' && hash !== 'sha2-256' && hash !== 'sha2-512') {
26
25
  const types = Object.keys(hashName).join(' / ')
27
26
  throw new CodeError(`Hash '${hash}' is unknown or not supported. Must be ${types}`, 'ERR_UNSUPPORTED_HASH_TYPE')
28
27
  }
29
28
 
30
29
  const hasher = hashName[hash]
31
- const dek = pbkdf2Sync(
32
- hasher,
30
+ const dek = forgePbkdf2(
33
31
  password,
34
- salt, {
35
- c: iterations,
36
- dkLen: keySize
37
- }
32
+ salt,
33
+ iterations,
34
+ keySize,
35
+ hasher
38
36
  )
39
37
 
40
- return base64.encode(dek).substring(1)
38
+ return forgeUtil.encode64(dek, null)
41
39
  }
package/src/util.ts CHANGED
@@ -1,5 +1,34 @@
1
+ import 'node-forge/lib/util.js'
2
+ import 'node-forge/lib/jsbn.js'
3
+ // @ts-expect-error types are missing
4
+ import forge from 'node-forge/lib/forge.js'
1
5
  import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
2
6
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
7
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
8
+
9
+ export function bigIntegerToUintBase64url (num: { abs(): any }, len?: number): string {
10
+ // Call `.abs()` to convert to unsigned
11
+ let buf = Uint8Array.from(num.abs().toByteArray()) // toByteArray converts to big endian
12
+
13
+ // toByteArray() gives us back a signed array, which will include a leading 0
14
+ // byte if the most significant bit of the number is 1:
15
+ // https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-integer
16
+ // Our number will always be positive so we should remove the leading padding.
17
+ buf = buf[0] === 0 ? buf.subarray(1) : buf
18
+
19
+ if (len != null) {
20
+ if (buf.length > len) throw new Error('byte array longer than desired length')
21
+ buf = uint8ArrayConcat([new Uint8Array(len - buf.length), buf])
22
+ }
23
+
24
+ return uint8ArrayToString(buf, 'base64url')
25
+ }
26
+
27
+ // Convert a base64url encoded string to a BigInteger
28
+ export function base64urlToBigInteger (str: string): typeof forge.jsbn.BigInteger {
29
+ const buf = base64urlToBuffer(str)
30
+ return new forge.jsbn.BigInteger(uint8ArrayToString(buf, 'base16'), 16)
31
+ }
3
32
 
4
33
  export function base64urlToBuffer (str: string, len?: number): Uint8Array {
5
34
  let buf = uint8ArrayFromString(str, 'base64urlpad')
package/src/webcrypto.ts CHANGED
@@ -1,11 +1,24 @@
1
1
  /* eslint-env browser */
2
2
 
3
- import { webcrypto } from 'crypto'
4
-
5
- // globalThis `SubtleCrypto` shipped in node.js 19.x, Electron currently uses
6
- // v18.x so this override file is necessary until Electron updates
3
+ // Check native crypto exists and is enabled (In insecure context `self.crypto`
4
+ // exists but `self.crypto.subtle` does not).
7
5
  export default {
8
6
  get (win = globalThis) {
9
- return webcrypto
7
+ const nativeCrypto = win.crypto
8
+
9
+ if (nativeCrypto == null || nativeCrypto.subtle == null) {
10
+ throw Object.assign(
11
+ new Error(
12
+ 'Missing Web Crypto API. ' +
13
+ 'The most likely cause of this error is that this page is being accessed ' +
14
+ 'from an insecure context (i.e. not HTTPS). For more information and ' +
15
+ 'possible resolutions see ' +
16
+ 'https://github.com/libp2p/js-libp2p/blob/main/packages/crypto/README.md#web-crypto-api'
17
+ ),
18
+ { code: 'ERR_MISSING_WEB_CRYPTO' }
19
+ )
20
+ }
21
+
22
+ return nativeCrypto
10
23
  }
11
24
  }
@@ -1,5 +0,0 @@
1
- declare const _default: {
2
- get(win?: typeof globalThis): Crypto;
3
- };
4
- export default _default;
5
- //# sourceMappingURL=webcrypto-browser.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"webcrypto-browser.d.ts","sourceRoot":"","sources":["../../src/webcrypto-browser.ts"],"names":[],"mappings":";;;AAIA,wBAmBC"}
@@ -1,17 +0,0 @@
1
- /* eslint-env browser */
2
- // Check native crypto exists and is enabled (In insecure context `self.crypto`
3
- // exists but `self.crypto.subtle` does not).
4
- export default {
5
- get(win = globalThis) {
6
- const nativeCrypto = win.crypto;
7
- if (nativeCrypto == null || nativeCrypto.subtle == null) {
8
- throw Object.assign(new Error('Missing Web Crypto API. ' +
9
- 'The most likely cause of this error is that this page is being accessed ' +
10
- 'from an insecure context (i.e. not HTTPS). For more information and ' +
11
- 'possible resolutions see ' +
12
- 'https://github.com/libp2p/js-libp2p/blob/main/packages/crypto/README.md#web-crypto-api'), { code: 'ERR_MISSING_WEB_CRYPTO' });
13
- }
14
- return nativeCrypto;
15
- }
16
- };
17
- //# sourceMappingURL=webcrypto-browser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"webcrypto-browser.js","sourceRoot":"","sources":["../../src/webcrypto-browser.ts"],"names":[],"mappings":"AAAA,wBAAwB;AAExB,+EAA+E;AAC/E,6CAA6C;AAC7C,eAAe;IACb,GAAG,CAAE,GAAG,GAAG,UAAU;QACnB,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAA;QAE/B,IAAI,YAAY,IAAI,IAAI,IAAI,YAAY,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YACxD,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CACP,0BAA0B;gBAC1B,0EAA0E;gBAC1E,sEAAsE;gBACtE,2BAA2B;gBAC3B,wFAAwF,CACzF,EACD,EAAE,IAAI,EAAE,wBAAwB,EAAE,CACnC,CAAA;QACH,CAAC;QAED,OAAO,YAAY,CAAA;IACrB,CAAC;CACF,CAAA"}
@@ -1,24 +0,0 @@
1
- /* eslint-env browser */
2
-
3
- // Check native crypto exists and is enabled (In insecure context `self.crypto`
4
- // exists but `self.crypto.subtle` does not).
5
- export default {
6
- get (win = globalThis) {
7
- const nativeCrypto = win.crypto
8
-
9
- if (nativeCrypto == null || nativeCrypto.subtle == null) {
10
- throw Object.assign(
11
- new Error(
12
- 'Missing Web Crypto API. ' +
13
- 'The most likely cause of this error is that this page is being accessed ' +
14
- 'from an insecure context (i.e. not HTTPS). For more information and ' +
15
- 'possible resolutions see ' +
16
- 'https://github.com/libp2p/js-libp2p/blob/main/packages/crypto/README.md#web-crypto-api'
17
- ),
18
- { code: 'ERR_MISSING_WEB_CRYPTO' }
19
- )
20
- }
21
-
22
- return nativeCrypto
23
- }
24
- }