@libp2p/crypto 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.min.js +7 -7
- package/dist/src/aes/cipher-mode.d.ts.map +1 -1
- package/dist/src/aes/cipher-mode.js +2 -2
- package/dist/src/aes/cipher-mode.js.map +1 -1
- package/dist/src/aes/ciphers-browser.d.ts +4 -5
- package/dist/src/aes/ciphers-browser.d.ts.map +1 -1
- package/dist/src/aes/ciphers-browser.js.map +1 -1
- package/dist/src/aes/index.d.ts.map +1 -1
- package/dist/src/ciphers/aes-gcm.browser.d.ts.map +1 -1
- package/dist/src/ciphers/aes-gcm.d.ts.map +1 -1
- package/dist/src/hmac/index-browser.d.ts +1 -1
- package/dist/src/hmac/index-browser.d.ts.map +1 -1
- package/dist/src/hmac/index-browser.js.map +1 -1
- package/dist/src/hmac/index.d.ts +4 -3
- package/dist/src/hmac/index.d.ts.map +1 -1
- package/dist/src/hmac/index.js.map +1 -1
- package/dist/src/keys/ecdh-browser.d.ts.map +1 -1
- package/dist/src/keys/ecdh-browser.js +6 -6
- package/dist/src/keys/ecdh-browser.js.map +1 -1
- package/dist/src/keys/ecdh.js +2 -2
- package/dist/src/keys/ecdh.js.map +1 -1
- package/dist/src/keys/ed25519-browser.d.ts +3 -8
- package/dist/src/keys/ed25519-browser.d.ts.map +1 -1
- package/dist/src/keys/ed25519-browser.js.map +1 -1
- package/dist/src/keys/ed25519-class.d.ts +3 -3
- package/dist/src/keys/ed25519-class.d.ts.map +1 -1
- package/dist/src/keys/ed25519-class.js +4 -4
- package/dist/src/keys/ed25519-class.js.map +1 -1
- package/dist/src/keys/ed25519.d.ts +3 -8
- package/dist/src/keys/ed25519.d.ts.map +1 -1
- package/dist/src/keys/ed25519.js +16 -3
- package/dist/src/keys/ed25519.js.map +1 -1
- package/dist/src/keys/exporter.d.ts +2 -1
- package/dist/src/keys/exporter.d.ts.map +1 -1
- package/dist/src/keys/exporter.js.map +1 -1
- package/dist/src/keys/importer.d.ts.map +1 -1
- package/dist/src/keys/index.js +4 -4
- package/dist/src/keys/index.js.map +1 -1
- package/dist/src/keys/interface.d.ts +19 -0
- package/dist/src/keys/interface.d.ts.map +1 -1
- package/dist/src/keys/jwk2pem.d.ts +6 -2
- package/dist/src/keys/jwk2pem.d.ts.map +1 -1
- package/dist/src/keys/jwk2pem.js.map +1 -1
- package/dist/src/keys/key-stretcher.d.ts +2 -12
- package/dist/src/keys/key-stretcher.d.ts.map +1 -1
- package/dist/src/keys/key-stretcher.js +3 -3
- package/dist/src/keys/key-stretcher.js.map +1 -1
- package/dist/src/keys/keys.d.ts.map +1 -1
- package/dist/src/keys/keys.js.map +1 -1
- package/dist/src/keys/rsa-browser.d.ts +3 -8
- package/dist/src/keys/rsa-browser.d.ts.map +1 -1
- package/dist/src/keys/rsa-browser.js +2 -2
- package/dist/src/keys/rsa-browser.js.map +1 -1
- package/dist/src/keys/rsa-class.d.ts +5 -5
- package/dist/src/keys/rsa-class.d.ts.map +1 -1
- package/dist/src/keys/rsa-class.js +3 -3
- package/dist/src/keys/rsa-class.js.map +1 -1
- package/dist/src/keys/rsa-utils.d.ts.map +1 -1
- package/dist/src/keys/rsa-utils.js +3 -3
- package/dist/src/keys/rsa-utils.js.map +1 -1
- package/dist/src/keys/rsa.d.ts +3 -4
- package/dist/src/keys/rsa.d.ts.map +1 -1
- package/dist/src/keys/rsa.js +2 -2
- package/dist/src/keys/rsa.js.map +1 -1
- package/dist/src/keys/secp256k1-class.d.ts +2 -1
- package/dist/src/keys/secp256k1-class.d.ts.map +1 -1
- package/dist/src/keys/secp256k1-class.js +3 -3
- package/dist/src/keys/secp256k1-class.js.map +1 -1
- package/dist/src/keys/secp256k1.d.ts.map +1 -1
- package/dist/src/keys/secp256k1.js +6 -6
- package/dist/src/keys/secp256k1.js.map +1 -1
- package/dist/src/pbkdf2.js +2 -2
- package/dist/src/pbkdf2.js.map +1 -1
- package/dist/src/random-bytes.js +2 -2
- package/dist/src/random-bytes.js.map +1 -1
- package/dist/src/util.d.ts.map +1 -1
- package/dist/src/util.js.map +1 -1
- package/dist/typedoc-urls.json +4 -0
- package/package.json +6 -8
- package/src/aes/cipher-mode.ts +3 -3
- package/src/aes/ciphers-browser.ts +6 -2
- package/src/aes/index.ts +1 -1
- package/src/ciphers/aes-gcm.browser.ts +3 -3
- package/src/ciphers/aes-gcm.ts +5 -5
- package/src/hmac/index-browser.ts +2 -2
- package/src/hmac/index.ts +6 -1
- package/src/keys/ecdh-browser.ts +12 -12
- package/src/keys/ecdh.ts +2 -2
- package/src/keys/ed25519-browser.ts +6 -5
- package/src/keys/ed25519-class.ts +23 -22
- package/src/keys/ed25519.ts +25 -9
- package/src/keys/exporter.ts +2 -1
- package/src/keys/importer.ts +1 -1
- package/src/keys/index.ts +6 -6
- package/src/keys/interface.ts +15 -0
- package/src/keys/jwk2pem.ts +8 -3
- package/src/keys/key-stretcher.ts +6 -5
- package/src/keys/keys.ts +1 -1
- package/src/keys/rsa-browser.ts +12 -11
- package/src/keys/rsa-class.ts +24 -23
- package/src/keys/rsa-utils.ts +5 -5
- package/src/keys/rsa.ts +6 -6
- package/src/keys/secp256k1-class.ts +20 -19
- package/src/keys/secp256k1.ts +14 -14
- package/src/pbkdf2.ts +2 -2
- package/src/random-bytes.ts +2 -2
- package/src/util.ts +2 -2
package/src/aes/cipher-mode.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CodeError } from '@libp2p/interfaces/errors'
|
|
2
2
|
|
|
3
3
|
const CIPHER_MODES = {
|
|
4
4
|
16: 'aes-128-ctr',
|
|
5
5
|
32: 'aes-256-ctr'
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export function cipherMode (key: Uint8Array) {
|
|
8
|
+
export function cipherMode (key: Uint8Array): string {
|
|
9
9
|
if (key.length === 16 || key.length === 32) {
|
|
10
10
|
return CIPHER_MODES[key.length]
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const modes = Object.entries(CIPHER_MODES).map(([k, v]) => `${k} (${v})`).join(' / ')
|
|
14
|
-
throw
|
|
14
|
+
throw new CodeError(`Invalid key length ${key.length} bytes. Must be ${modes}`, 'ERR_INVALID_KEY_LENGTH')
|
|
15
15
|
}
|
|
@@ -5,7 +5,11 @@ import forge from 'node-forge/lib/forge.js'
|
|
|
5
5
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
6
6
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export interface Cipher {
|
|
9
|
+
update: (data: Uint8Array) => Uint8Array
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createCipheriv (mode: any, key: Uint8Array, iv: Uint8Array): Cipher {
|
|
9
13
|
const cipher2 = forge.cipher.createCipher('AES-CTR', uint8ArrayToString(key, 'ascii'))
|
|
10
14
|
cipher2.start({ iv: uint8ArrayToString(iv, 'ascii') })
|
|
11
15
|
return {
|
|
@@ -16,7 +20,7 @@ export function createCipheriv (mode: any, key: Uint8Array, iv: Uint8Array) {
|
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
export function createDecipheriv (mode: any, key: Uint8Array, iv: Uint8Array) {
|
|
23
|
+
export function createDecipheriv (mode: any, key: Uint8Array, iv: Uint8Array): Cipher {
|
|
20
24
|
const cipher2 = forge.cipher.createDecipher('AES-CTR', uint8ArrayToString(key, 'ascii'))
|
|
21
25
|
cipher2.start({ iv: uint8ArrayToString(iv, 'ascii') })
|
|
22
26
|
return {
|
package/src/aes/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface AESCipher {
|
|
|
6
6
|
decrypt: (data: Uint8Array) => Promise<Uint8Array>
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export async function create (key: Uint8Array, iv: Uint8Array) { // eslint-disable-line require-await
|
|
9
|
+
export async function create (key: Uint8Array, iv: Uint8Array): Promise<AESCipher> { // eslint-disable-line require-await
|
|
10
10
|
const mode = cipherMode(key)
|
|
11
11
|
const cipher = ciphers.createCipheriv(mode, key, iv)
|
|
12
12
|
const decipher = ciphers.createDecipheriv(mode, key, iv)
|
|
@@ -5,7 +5,7 @@ import type { CreateOptions, AESCipher } from './interface.js'
|
|
|
5
5
|
|
|
6
6
|
// Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples
|
|
7
7
|
|
|
8
|
-
export function create (opts?: CreateOptions) {
|
|
8
|
+
export function create (opts?: CreateOptions): AESCipher {
|
|
9
9
|
const algorithm = opts?.algorithm ?? 'AES-GCM'
|
|
10
10
|
let keyLength = opts?.keyLength ?? 16
|
|
11
11
|
const nonceLength = opts?.nonceLength ?? 12
|
|
@@ -20,7 +20,7 @@ export function create (opts?: CreateOptions) {
|
|
|
20
20
|
* Uses the provided password to derive a pbkdf2 key. The key
|
|
21
21
|
* will then be used to encrypt the data.
|
|
22
22
|
*/
|
|
23
|
-
async function encrypt (data: Uint8Array, password: string | Uint8Array) { // eslint-disable-line require-await
|
|
23
|
+
async function encrypt (data: Uint8Array, password: string | Uint8Array): Promise<Uint8Array> { // eslint-disable-line require-await
|
|
24
24
|
const salt = crypto.getRandomValues(new Uint8Array(saltLength))
|
|
25
25
|
const nonce = crypto.getRandomValues(new Uint8Array(nonceLength))
|
|
26
26
|
const aesGcm = { name: algorithm, iv: nonce }
|
|
@@ -45,7 +45,7 @@ export function create (opts?: CreateOptions) {
|
|
|
45
45
|
* this decryption cipher must be the same as those used to create
|
|
46
46
|
* the encryption cipher.
|
|
47
47
|
*/
|
|
48
|
-
async function decrypt (data: Uint8Array, password: string | Uint8Array) {
|
|
48
|
+
async function decrypt (data: Uint8Array, password: string | Uint8Array): Promise<Uint8Array> {
|
|
49
49
|
const salt = data.subarray(0, saltLength)
|
|
50
50
|
const nonce = data.subarray(saltLength, saltLength + nonceLength)
|
|
51
51
|
const ciphertext = data.subarray(saltLength + nonceLength)
|
package/src/ciphers/aes-gcm.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { CreateOptions, AESCipher } from './interface.js'
|
|
|
5
5
|
|
|
6
6
|
// Based off of code from https://github.com/luke-park/SecureCompatibleEncryptionExamples
|
|
7
7
|
|
|
8
|
-
export function create (opts?: CreateOptions) {
|
|
8
|
+
export function create (opts?: CreateOptions): AESCipher {
|
|
9
9
|
const algorithm = opts?.algorithm ?? 'aes-128-gcm'
|
|
10
10
|
const keyLength = opts?.keyLength ?? 16
|
|
11
11
|
const nonceLength = opts?.nonceLength ?? 12
|
|
@@ -14,7 +14,7 @@ export function create (opts?: CreateOptions) {
|
|
|
14
14
|
const iterations = opts?.iterations ?? 32767
|
|
15
15
|
const algorithmTagLength = opts?.algorithmTagLength ?? 16
|
|
16
16
|
|
|
17
|
-
async function encryptWithKey (data: Uint8Array, key: Uint8Array) { // eslint-disable-line require-await
|
|
17
|
+
async function encryptWithKey (data: Uint8Array, key: Uint8Array): Promise<Uint8Array> { // eslint-disable-line require-await
|
|
18
18
|
const nonce = crypto.randomBytes(nonceLength)
|
|
19
19
|
|
|
20
20
|
// Create the cipher instance.
|
|
@@ -31,7 +31,7 @@ export function create (opts?: CreateOptions) {
|
|
|
31
31
|
* Uses the provided password to derive a pbkdf2 key. The key
|
|
32
32
|
* will then be used to encrypt the data.
|
|
33
33
|
*/
|
|
34
|
-
async function encrypt (data: Uint8Array, password: string | Uint8Array) { // eslint-disable-line require-await
|
|
34
|
+
async function encrypt (data: Uint8Array, password: string | Uint8Array): Promise<Uint8Array> { // eslint-disable-line require-await
|
|
35
35
|
// Generate a 128-bit salt using a CSPRNG.
|
|
36
36
|
const salt = crypto.randomBytes(saltLength)
|
|
37
37
|
|
|
@@ -53,7 +53,7 @@ export function create (opts?: CreateOptions) {
|
|
|
53
53
|
* this decryption cipher must be the same as those used to create
|
|
54
54
|
* the encryption cipher.
|
|
55
55
|
*/
|
|
56
|
-
async function decryptWithKey (ciphertextAndNonce: Uint8Array, key: Uint8Array) { // eslint-disable-line require-await
|
|
56
|
+
async function decryptWithKey (ciphertextAndNonce: Uint8Array, key: Uint8Array): Promise<Uint8Array> { // eslint-disable-line require-await
|
|
57
57
|
// Create Uint8Arrays of nonce, ciphertext and tag.
|
|
58
58
|
const nonce = ciphertextAndNonce.subarray(0, nonceLength)
|
|
59
59
|
const ciphertext = ciphertextAndNonce.subarray(nonceLength, ciphertextAndNonce.length - algorithmTagLength)
|
|
@@ -77,7 +77,7 @@ export function create (opts?: CreateOptions) {
|
|
|
77
77
|
* @param {Uint8Array} data - The data to decrypt
|
|
78
78
|
* @param {string|Uint8Array} password - A plain password
|
|
79
79
|
*/
|
|
80
|
-
async function decrypt (data: Uint8Array, password: string | Uint8Array) { // eslint-disable-line require-await
|
|
80
|
+
async function decrypt (data: Uint8Array, password: string | Uint8Array): Promise<Uint8Array> { // eslint-disable-line require-await
|
|
81
81
|
// Create Uint8Arrays of salt and ciphertextAndNonce.
|
|
82
82
|
const salt = data.subarray(0, saltLength)
|
|
83
83
|
const ciphertextAndNonce = data.subarray(saltLength)
|
|
@@ -7,12 +7,12 @@ const hashTypes = {
|
|
|
7
7
|
SHA512: 'SHA-512'
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
const sign = async (key: CryptoKey, data: Uint8Array) => {
|
|
10
|
+
const sign = async (key: CryptoKey, data: Uint8Array): Promise<Uint8Array> => {
|
|
11
11
|
const buf = await webcrypto.get().subtle.sign({ name: 'HMAC' }, key, data)
|
|
12
12
|
return new Uint8Array(buf, 0, buf.byteLength)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export async function create (hashType: 'SHA1' | 'SHA256' | 'SHA512', secret: Uint8Array) {
|
|
15
|
+
export async function create (hashType: 'SHA1' | 'SHA256' | 'SHA512', secret: Uint8Array): Promise<{ digest: (data: Uint8Array) => Promise<Uint8Array>, length: number }> {
|
|
16
16
|
const hash = hashTypes[hashType]
|
|
17
17
|
|
|
18
18
|
const key = await webcrypto.get().subtle.importKey(
|
package/src/hmac/index.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import crypto from 'crypto'
|
|
2
2
|
import lengths from './lengths.js'
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export interface HMAC {
|
|
5
|
+
digest: (data: Uint8Array) => Promise<Uint8Array>
|
|
6
|
+
length: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function create (hash: 'SHA1' | 'SHA256' | 'SHA512', secret: Uint8Array): Promise<HMAC> {
|
|
5
10
|
const res = {
|
|
6
11
|
async digest (data: Uint8Array) { // eslint-disable-line require-await
|
|
7
12
|
const hmac = crypto.createHmac(hash.toLowerCase(), secret)
|
package/src/keys/ecdh-browser.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CodeError } from '@libp2p/interfaces/errors'
|
|
2
2
|
import webcrypto from '../webcrypto.js'
|
|
3
3
|
import { base64urlToBuffer } from '../util.js'
|
|
4
4
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
5
5
|
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
|
|
6
6
|
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
7
|
-
import type { ECDHKey, ECDHKeyPair } from './interface.js'
|
|
7
|
+
import type { ECDHKey, ECDHKeyPair, JWKEncodedPrivateKey, JWKEncodedPublicKey } from './interface.js'
|
|
8
8
|
|
|
9
9
|
const bits = {
|
|
10
10
|
'P-256': 256,
|
|
@@ -15,9 +15,9 @@ const bits = {
|
|
|
15
15
|
const curveTypes = Object.keys(bits)
|
|
16
16
|
const names = curveTypes.join(' / ')
|
|
17
17
|
|
|
18
|
-
export async function generateEphmeralKeyPair (curve: string) {
|
|
18
|
+
export async function generateEphmeralKeyPair (curve: string): Promise<ECDHKey> {
|
|
19
19
|
if (curve !== 'P-256' && curve !== 'P-384' && curve !== 'P-521') {
|
|
20
|
-
throw
|
|
20
|
+
throw new CodeError(`Unknown curve: ${curve}. Must be ${names}`, 'ERR_INVALID_CURVE')
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const pair = await webcrypto.get().subtle.generateKey(
|
|
@@ -30,7 +30,7 @@ export async function generateEphmeralKeyPair (curve: string) {
|
|
|
30
30
|
)
|
|
31
31
|
|
|
32
32
|
// forcePrivate is used for testing only
|
|
33
|
-
const genSharedKey = async (theirPub: Uint8Array, forcePrivate?: ECDHKeyPair) => {
|
|
33
|
+
const genSharedKey = async (theirPub: Uint8Array, forcePrivate?: ECDHKeyPair): Promise<Uint8Array> => {
|
|
34
34
|
let privateKey
|
|
35
35
|
|
|
36
36
|
if (forcePrivate != null) {
|
|
@@ -92,13 +92,13 @@ const curveLengths = {
|
|
|
92
92
|
// Marshal converts a jwk encoded ECDH public key into the
|
|
93
93
|
// form specified in section 4.3.6 of ANSI X9.62. (This is the format
|
|
94
94
|
// go-ipfs uses)
|
|
95
|
-
function marshalPublicKey (jwk: JsonWebKey) {
|
|
95
|
+
function marshalPublicKey (jwk: JsonWebKey): Uint8Array {
|
|
96
96
|
if (jwk.crv == null || jwk.x == null || jwk.y == null) {
|
|
97
|
-
throw
|
|
97
|
+
throw new CodeError('JWK was missing components', 'ERR_INVALID_PARAMETERS')
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
if (jwk.crv !== 'P-256' && jwk.crv !== 'P-384' && jwk.crv !== 'P-521') {
|
|
101
|
-
throw
|
|
101
|
+
throw new CodeError(`Unknown curve: ${jwk.crv}. Must be ${names}`, 'ERR_INVALID_CURVE')
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const byteLen = curveLengths[jwk.crv]
|
|
@@ -111,15 +111,15 @@ function marshalPublicKey (jwk: JsonWebKey) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// Unmarshal converts a point, serialized by Marshal, into an jwk encoded key
|
|
114
|
-
function unmarshalPublicKey (curve: string, key: Uint8Array) {
|
|
114
|
+
function unmarshalPublicKey (curve: string, key: Uint8Array): JWKEncodedPublicKey {
|
|
115
115
|
if (curve !== 'P-256' && curve !== 'P-384' && curve !== 'P-521') {
|
|
116
|
-
throw
|
|
116
|
+
throw new CodeError(`Unknown curve: ${curve}. Must be ${names}`, 'ERR_INVALID_CURVE')
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const byteLen = curveLengths[curve]
|
|
120
120
|
|
|
121
121
|
if (!uint8ArrayEquals(key.subarray(0, 1), Uint8Array.from([4]))) {
|
|
122
|
-
throw
|
|
122
|
+
throw new CodeError('Cannot unmarshal public key - invalid key format', 'ERR_INVALID_KEY_FORMAT')
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
return {
|
|
@@ -131,7 +131,7 @@ function unmarshalPublicKey (curve: string, key: Uint8Array) {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
const unmarshalPrivateKey = (curve: string, key: ECDHKeyPair) => ({
|
|
134
|
+
const unmarshalPrivateKey = (curve: string, key: ECDHKeyPair): JWKEncodedPrivateKey => ({
|
|
135
135
|
...unmarshalPublicKey(curve, key.public),
|
|
136
136
|
d: uint8ArrayToString(key.private, 'base64url')
|
|
137
137
|
})
|
package/src/keys/ecdh.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import crypto from 'crypto'
|
|
2
|
-
import
|
|
2
|
+
import { CodeError } from '@libp2p/interfaces/errors'
|
|
3
3
|
import type { ECDHKey, ECDHKeyPair } from './interface.js'
|
|
4
4
|
|
|
5
5
|
const curves = {
|
|
@@ -13,7 +13,7 @@ const names = curveTypes.join(' / ')
|
|
|
13
13
|
|
|
14
14
|
export async function generateEphmeralKeyPair (curve: string): Promise<ECDHKey> { // eslint-disable-line require-await
|
|
15
15
|
if (curve !== 'P-256' && curve !== 'P-384' && curve !== 'P-521') {
|
|
16
|
-
throw
|
|
16
|
+
throw new CodeError(`Unknown curve: ${curve}. Must be ${names}`, 'ERR_INVALID_CURVE')
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const ecdh = crypto.createECDH(curves[curve])
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as ed from '@noble/ed25519'
|
|
2
|
+
import type { Uint8ArrayKeyPair } from './interface'
|
|
2
3
|
|
|
3
4
|
const PUBLIC_KEY_BYTE_LENGTH = 32
|
|
4
5
|
const PRIVATE_KEY_BYTE_LENGTH = 64 // private key is actually 32 bytes but for historical reasons we concat private and public keys
|
|
@@ -7,7 +8,7 @@ const KEYS_BYTE_LENGTH = 32
|
|
|
7
8
|
export { PUBLIC_KEY_BYTE_LENGTH as publicKeyLength }
|
|
8
9
|
export { PRIVATE_KEY_BYTE_LENGTH as privateKeyLength }
|
|
9
10
|
|
|
10
|
-
export async function generateKey () {
|
|
11
|
+
export async function generateKey (): Promise<Uint8ArrayKeyPair> {
|
|
11
12
|
// the actual private key (32 bytes)
|
|
12
13
|
const privateKeyRaw = ed.utils.randomPrivateKey()
|
|
13
14
|
const publicKey = await ed.getPublicKey(privateKeyRaw)
|
|
@@ -24,7 +25,7 @@ export async function generateKey () {
|
|
|
24
25
|
/**
|
|
25
26
|
* Generate keypair from a 32 byte uint8array
|
|
26
27
|
*/
|
|
27
|
-
export async function generateKeyFromSeed (seed: Uint8Array) {
|
|
28
|
+
export async function generateKeyFromSeed (seed: Uint8Array): Promise<Uint8ArrayKeyPair> {
|
|
28
29
|
if (seed.length !== KEYS_BYTE_LENGTH) {
|
|
29
30
|
throw new TypeError('"seed" must be 32 bytes in length.')
|
|
30
31
|
} else if (!(seed instanceof Uint8Array)) {
|
|
@@ -43,17 +44,17 @@ export async function generateKeyFromSeed (seed: Uint8Array) {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
export async function hashAndSign (privateKey: Uint8Array, msg: Uint8Array) {
|
|
47
|
+
export async function hashAndSign (privateKey: Uint8Array, msg: Uint8Array): Promise<Uint8Array> {
|
|
47
48
|
const privateKeyRaw = privateKey.subarray(0, KEYS_BYTE_LENGTH)
|
|
48
49
|
|
|
49
50
|
return await ed.sign(msg, privateKeyRaw)
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
export async function hashAndVerify (publicKey: Uint8Array, sig: Uint8Array, msg: Uint8Array) {
|
|
53
|
+
export async function hashAndVerify (publicKey: Uint8Array, sig: Uint8Array, msg: Uint8Array): Promise<boolean> {
|
|
53
54
|
return await ed.verify(sig, msg, publicKey)
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
function concatKeys (privateKeyRaw: Uint8Array, publicKey: Uint8Array) {
|
|
57
|
+
function concatKeys (privateKeyRaw: Uint8Array, publicKey: Uint8Array): Uint8Array {
|
|
57
58
|
const privateKey = new Uint8Array(PRIVATE_KEY_BYTE_LENGTH)
|
|
58
59
|
for (let i = 0; i < KEYS_BYTE_LENGTH; i++) {
|
|
59
60
|
privateKey[i] = privateKeyRaw[i]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CodeError } from '@libp2p/interfaces/errors'
|
|
2
2
|
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
3
3
|
import { sha256 } from 'multiformats/hashes/sha2'
|
|
4
4
|
import { base58btc } from 'multiformats/bases/base58'
|
|
@@ -6,6 +6,7 @@ import { identity } from 'multiformats/hashes/identity'
|
|
|
6
6
|
import * as crypto from './ed25519.js'
|
|
7
7
|
import * as pbm from './keys.js'
|
|
8
8
|
import { exporter } from './exporter.js'
|
|
9
|
+
import type { Multibase } from 'multiformats'
|
|
9
10
|
|
|
10
11
|
export class Ed25519PublicKey {
|
|
11
12
|
private readonly _key: Uint8Array
|
|
@@ -14,26 +15,26 @@ export class Ed25519PublicKey {
|
|
|
14
15
|
this._key = ensureKey(key, crypto.publicKeyLength)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
async verify (data: Uint8Array, sig: Uint8Array) { // eslint-disable-line require-await
|
|
18
|
+
async verify (data: Uint8Array, sig: Uint8Array): Promise<boolean> { // eslint-disable-line require-await
|
|
18
19
|
return await crypto.hashAndVerify(this._key, sig, data)
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
marshal () {
|
|
22
|
+
marshal (): Uint8Array {
|
|
22
23
|
return this._key
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
get bytes () {
|
|
26
|
+
get bytes (): Uint8Array {
|
|
26
27
|
return pbm.PublicKey.encode({
|
|
27
28
|
Type: pbm.KeyType.Ed25519,
|
|
28
29
|
Data: this.marshal()
|
|
29
30
|
}).subarray()
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
equals (key: any) {
|
|
33
|
+
equals (key: any): boolean {
|
|
33
34
|
return uint8ArrayEquals(this.bytes, key.bytes)
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
async hash () {
|
|
37
|
+
async hash (): Promise<Uint8Array> {
|
|
37
38
|
const { bytes } = await sha256.digest(this.bytes)
|
|
38
39
|
|
|
39
40
|
return bytes
|
|
@@ -51,30 +52,30 @@ export class Ed25519PrivateKey {
|
|
|
51
52
|
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
async sign (message: Uint8Array) { // eslint-disable-line require-await
|
|
55
|
+
async sign (message: Uint8Array): Promise<Uint8Array> { // eslint-disable-line require-await
|
|
55
56
|
return await crypto.hashAndSign(this._key, message)
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
get public () {
|
|
59
|
+
get public (): Ed25519PublicKey {
|
|
59
60
|
return new Ed25519PublicKey(this._publicKey)
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
marshal () {
|
|
63
|
+
marshal (): Uint8Array {
|
|
63
64
|
return this._key
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
get bytes () {
|
|
67
|
+
get bytes (): Uint8Array {
|
|
67
68
|
return pbm.PrivateKey.encode({
|
|
68
69
|
Type: pbm.KeyType.Ed25519,
|
|
69
70
|
Data: this.marshal()
|
|
70
71
|
}).subarray()
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
equals (key: any) {
|
|
74
|
+
equals (key: any): boolean {
|
|
74
75
|
return uint8ArrayEquals(this.bytes, key.bytes)
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
async hash () {
|
|
78
|
+
async hash (): Promise<Uint8Array> {
|
|
78
79
|
const { bytes } = await sha256.digest(this.bytes)
|
|
79
80
|
|
|
80
81
|
return bytes
|
|
@@ -89,24 +90,24 @@ export class Ed25519PrivateKey {
|
|
|
89
90
|
*
|
|
90
91
|
* @returns {Promise<string>}
|
|
91
92
|
*/
|
|
92
|
-
async id () {
|
|
93
|
-
const encoding =
|
|
93
|
+
async id (): Promise<string> {
|
|
94
|
+
const encoding = identity.digest(this.public.bytes)
|
|
94
95
|
return base58btc.encode(encoding.bytes).substring(1)
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
99
|
* Exports the key into a password protected `format`
|
|
99
100
|
*/
|
|
100
|
-
async export (password: string, format = 'libp2p-key') {
|
|
101
|
+
async export (password: string, format = 'libp2p-key'): Promise<Multibase<'m'>> {
|
|
101
102
|
if (format === 'libp2p-key') {
|
|
102
103
|
return await exporter(this.bytes, password)
|
|
103
104
|
} else {
|
|
104
|
-
throw
|
|
105
|
+
throw new CodeError(`export format '${format}' is not supported`, 'ERR_INVALID_EXPORT_FORMAT')
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
export function unmarshalEd25519PrivateKey (bytes: Uint8Array) {
|
|
110
|
+
export function unmarshalEd25519PrivateKey (bytes: Uint8Array): Ed25519PrivateKey {
|
|
110
111
|
// Try the old, redundant public key version
|
|
111
112
|
if (bytes.length > crypto.privateKeyLength) {
|
|
112
113
|
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
|
@@ -121,25 +122,25 @@ export function unmarshalEd25519PrivateKey (bytes: Uint8Array) {
|
|
|
121
122
|
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
export function unmarshalEd25519PublicKey (bytes: Uint8Array) {
|
|
125
|
+
export function unmarshalEd25519PublicKey (bytes: Uint8Array): Ed25519PublicKey {
|
|
125
126
|
bytes = ensureKey(bytes, crypto.publicKeyLength)
|
|
126
127
|
return new Ed25519PublicKey(bytes)
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
export async function generateKeyPair () {
|
|
130
|
+
export async function generateKeyPair (): Promise<Ed25519PrivateKey> {
|
|
130
131
|
const { privateKey, publicKey } = await crypto.generateKey()
|
|
131
132
|
return new Ed25519PrivateKey(privateKey, publicKey)
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
export async function generateKeyPairFromSeed (seed: Uint8Array) {
|
|
135
|
+
export async function generateKeyPairFromSeed (seed: Uint8Array): Promise<Ed25519PrivateKey> {
|
|
135
136
|
const { privateKey, publicKey } = await crypto.generateKeyFromSeed(seed)
|
|
136
137
|
return new Ed25519PrivateKey(privateKey, publicKey)
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
function ensureKey (key: Uint8Array, length: number) {
|
|
140
|
+
function ensureKey (key: Uint8Array, length: number): Uint8Array {
|
|
140
141
|
key = Uint8Array.from(key ?? [])
|
|
141
142
|
if (key.length !== length) {
|
|
142
|
-
throw
|
|
143
|
+
throw new CodeError(`Key must be a Uint8Array of length ${length}, got ${key.length}`, 'ERR_INVALID_KEY_TYPE')
|
|
143
144
|
}
|
|
144
145
|
return key
|
|
145
146
|
}
|
package/src/keys/ed25519.ts
CHANGED
|
@@ -2,6 +2,7 @@ import crypto from 'crypto'
|
|
|
2
2
|
import { promisify } from 'util'
|
|
3
3
|
import { toString as uint8arrayToString } from 'uint8arrays/to-string'
|
|
4
4
|
import { fromString as uint8arrayFromString } from 'uint8arrays/from-string'
|
|
5
|
+
import type { Uint8ArrayKeyPair } from './interface.js'
|
|
5
6
|
|
|
6
7
|
const keypair = promisify(crypto.generateKeyPair)
|
|
7
8
|
|
|
@@ -13,13 +14,28 @@ const SIGNATURE_BYTE_LENGTH = 64
|
|
|
13
14
|
export { PUBLIC_KEY_BYTE_LENGTH as publicKeyLength }
|
|
14
15
|
export { PRIVATE_KEY_BYTE_LENGTH as privateKeyLength }
|
|
15
16
|
|
|
16
|
-
function derivePublicKey (privateKey: Uint8Array) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
function derivePublicKey (privateKey: Uint8Array): Uint8Array {
|
|
18
|
+
const keyObject = crypto.createPrivateKey({
|
|
19
|
+
format: 'jwk',
|
|
20
|
+
key: {
|
|
21
|
+
crv: 'Ed25519',
|
|
22
|
+
x: '',
|
|
23
|
+
d: uint8arrayToString(privateKey, 'base64url'),
|
|
24
|
+
kty: 'OKP'
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
const jwk = keyObject.export({
|
|
28
|
+
format: 'jwk'
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
if (jwk.x == null || jwk.x === '') {
|
|
32
|
+
throw new Error('Could not export JWK public key')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return uint8arrayFromString(jwk.x, 'base64url')
|
|
20
36
|
}
|
|
21
37
|
|
|
22
|
-
export async function generateKey () {
|
|
38
|
+
export async function generateKey (): Promise<Uint8ArrayKeyPair> {
|
|
23
39
|
const key = await keypair('ed25519', {
|
|
24
40
|
publicKeyEncoding: { type: 'spki', format: 'jwk' },
|
|
25
41
|
privateKeyEncoding: { type: 'pkcs8', format: 'jwk' }
|
|
@@ -39,7 +55,7 @@ export async function generateKey () {
|
|
|
39
55
|
/**
|
|
40
56
|
* Generate keypair from a 32 byte uint8array
|
|
41
57
|
*/
|
|
42
|
-
export async function generateKeyFromSeed (seed: Uint8Array) {
|
|
58
|
+
export async function generateKeyFromSeed (seed: Uint8Array): Promise<Uint8ArrayKeyPair> {
|
|
43
59
|
if (seed.length !== KEYS_BYTE_LENGTH) {
|
|
44
60
|
throw new TypeError('"seed" must be 32 bytes in length.')
|
|
45
61
|
} else if (!(seed instanceof Uint8Array)) {
|
|
@@ -55,7 +71,7 @@ export async function generateKeyFromSeed (seed: Uint8Array) {
|
|
|
55
71
|
}
|
|
56
72
|
}
|
|
57
73
|
|
|
58
|
-
export async function hashAndSign (key: Uint8Array, msg: Uint8Array) {
|
|
74
|
+
export async function hashAndSign (key: Uint8Array, msg: Uint8Array): Promise<Buffer> {
|
|
59
75
|
if (!(key instanceof Uint8Array)) {
|
|
60
76
|
throw new TypeError('"key" must be a node.js Buffer, or Uint8Array.')
|
|
61
77
|
}
|
|
@@ -86,7 +102,7 @@ export async function hashAndSign (key: Uint8Array, msg: Uint8Array) {
|
|
|
86
102
|
return crypto.sign(null, msg, obj)
|
|
87
103
|
}
|
|
88
104
|
|
|
89
|
-
export async function hashAndVerify (key: Uint8Array, sig: Uint8Array, msg: Uint8Array) {
|
|
105
|
+
export async function hashAndVerify (key: Uint8Array, sig: Uint8Array, msg: Uint8Array): Promise<boolean> {
|
|
90
106
|
if (key.byteLength !== PUBLIC_KEY_BYTE_LENGTH) {
|
|
91
107
|
throw new TypeError('"key" must be 32 bytes in length.')
|
|
92
108
|
} else if (!(key instanceof Uint8Array)) {
|
|
@@ -111,7 +127,7 @@ export async function hashAndVerify (key: Uint8Array, sig: Uint8Array, msg: Uint
|
|
|
111
127
|
return crypto.verify(null, msg, obj, sig)
|
|
112
128
|
}
|
|
113
129
|
|
|
114
|
-
function concatKeys (privateKeyRaw: Uint8Array, publicKey: Uint8Array) {
|
|
130
|
+
function concatKeys (privateKeyRaw: Uint8Array, publicKey: Uint8Array): Uint8Array {
|
|
115
131
|
const privateKey = new Uint8Array(PRIVATE_KEY_BYTE_LENGTH)
|
|
116
132
|
for (let i = 0; i < KEYS_BYTE_LENGTH; i++) {
|
|
117
133
|
privateKey[i] = privateKeyRaw[i]
|
package/src/keys/exporter.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Multibase } from 'multiformats'
|
|
1
2
|
import { base64 } from 'multiformats/bases/base64'
|
|
2
3
|
import * as ciphers from '../ciphers/aes-gcm.js'
|
|
3
4
|
|
|
@@ -6,7 +7,7 @@ import * as ciphers from '../ciphers/aes-gcm.js'
|
|
|
6
7
|
* The PrivateKey is encrypted via a password derived PBKDF2 key
|
|
7
8
|
* leveraging the aes-gcm cipher algorithm.
|
|
8
9
|
*/
|
|
9
|
-
export async function exporter (privateKey: Uint8Array, password: string) {
|
|
10
|
+
export async function exporter (privateKey: Uint8Array, password: string): Promise<Multibase<'m'>> {
|
|
10
11
|
const cipher = ciphers.create()
|
|
11
12
|
const encryptedKey = await cipher.encrypt(privateKey, password)
|
|
12
13
|
return base64.encode(encryptedKey)
|
package/src/keys/importer.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as ciphers from '../ciphers/aes-gcm.js'
|
|
|
6
6
|
* with the given password. The privateKey must have been exported
|
|
7
7
|
* using the same password and underlying cipher (aes-gcm)
|
|
8
8
|
*/
|
|
9
|
-
export async function importer (privateKey: string, password: string) {
|
|
9
|
+
export async function importer (privateKey: string, password: string): Promise<Uint8Array> {
|
|
10
10
|
const encryptedKey = base64.decode(privateKey)
|
|
11
11
|
const cipher = ciphers.create()
|
|
12
12
|
return await cipher.decrypt(encryptedKey, password)
|
package/src/keys/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import 'node-forge/lib/asn1.js'
|
|
|
3
3
|
import 'node-forge/lib/pbe.js'
|
|
4
4
|
// @ts-expect-error types are missing
|
|
5
5
|
import forge from 'node-forge/lib/forge.js'
|
|
6
|
-
import
|
|
6
|
+
import { CodeError } from '@libp2p/interfaces/errors'
|
|
7
7
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
8
8
|
import { keyStretcher } from './key-stretcher.js'
|
|
9
9
|
import generateEphemeralKeyPair from './ephemeral-keys.js'
|
|
@@ -25,12 +25,12 @@ export const supportedKeys = {
|
|
|
25
25
|
secp256k1: Secp256k1
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function unsupportedKey (type: string) {
|
|
28
|
+
function unsupportedKey (type: string): CodeError<Record<string, never>> {
|
|
29
29
|
const supported = Object.keys(supportedKeys).join(' / ')
|
|
30
|
-
return
|
|
30
|
+
return new CodeError(`invalid or unsupported key type ${type}. Must be ${supported}`, 'ERR_UNSUPPORTED_KEY_TYPE')
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function typeToKey (type: string) {
|
|
33
|
+
function typeToKey (type: string): typeof RSA | typeof Ed25519 | typeof Secp256k1 {
|
|
34
34
|
type = type.toLowerCase()
|
|
35
35
|
|
|
36
36
|
if (type === 'rsa' || type === 'ed25519' || type === 'secp256k1') {
|
|
@@ -49,7 +49,7 @@ export async function generateKeyPair (type: KeyTypes, bits?: number): Promise<P
|
|
|
49
49
|
// seed is a 32 byte uint8array
|
|
50
50
|
export async function generateKeyPairFromSeed (type: KeyTypes, seed: Uint8Array, bits?: number): Promise<PrivateKey> { // eslint-disable-line require-await
|
|
51
51
|
if (type.toLowerCase() !== 'ed25519') {
|
|
52
|
-
throw
|
|
52
|
+
throw new CodeError('Seed key derivation is unimplemented for RSA or secp256k1', 'ERR_UNSUPPORTED_KEY_DERIVATION_TYPE')
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
return await Ed25519.generateKeyPairFromSeed(seed)
|
|
@@ -121,7 +121,7 @@ export async function importKey (encryptedKey: string, password: string): Promis
|
|
|
121
121
|
// Only rsa supports pem right now
|
|
122
122
|
const key = forge.pki.decryptRsaPrivateKey(encryptedKey, password)
|
|
123
123
|
if (key === null) {
|
|
124
|
-
throw
|
|
124
|
+
throw new CodeError('Cannot read the key, most likely the password is wrong or not a RSA key', 'ERR_CANNOT_DECRYPT_PEM')
|
|
125
125
|
}
|
|
126
126
|
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
|
|
127
127
|
der = uint8ArrayFromString(der.getBytes(), 'ascii')
|
package/src/keys/interface.ts
CHANGED
|
@@ -18,3 +18,18 @@ export interface ECDHKey {
|
|
|
18
18
|
key: Uint8Array
|
|
19
19
|
genSharedKey: (theirPub: Uint8Array, forcePrivate?: ECDHKeyPair) => Promise<Uint8Array>
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
export interface JWKEncodedPublicKey { kty: string, crv: 'P-256' | 'P-384' | 'P-521', x: string, y: string, ext: boolean }
|
|
23
|
+
|
|
24
|
+
export interface JWKEncodedPrivateKey extends JWKEncodedPublicKey { d: string}
|
|
25
|
+
|
|
26
|
+
export interface EnhancedKey {
|
|
27
|
+
iv: Uint8Array
|
|
28
|
+
cipherKey: Uint8Array
|
|
29
|
+
macKey: Uint8Array
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface EnhancedKeyPair {
|
|
33
|
+
k1: EnhancedKey
|
|
34
|
+
k2: EnhancedKey
|
|
35
|
+
}
|
package/src/keys/jwk2pem.ts
CHANGED
|
@@ -3,14 +3,19 @@ import 'node-forge/lib/rsa.js'
|
|
|
3
3
|
import forge from 'node-forge/lib/forge.js'
|
|
4
4
|
import { base64urlToBigInteger } from '../util.js'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export interface JWK {
|
|
7
|
+
encrypt: (msg: string) => string
|
|
8
|
+
decrypt: (msg: string) => string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function convert (key: any, types: string[]): Array<typeof forge.jsbn.BigInteger> {
|
|
7
12
|
return types.map(t => base64urlToBigInteger(key[t]))
|
|
8
13
|
}
|
|
9
14
|
|
|
10
|
-
export function jwk2priv (key: JsonWebKey) {
|
|
15
|
+
export function jwk2priv (key: JsonWebKey): JWK {
|
|
11
16
|
return forge.pki.setRsaPrivateKey(...convert(key, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']))
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
export function jwk2pub (key: JsonWebKey) {
|
|
19
|
+
export function jwk2pub (key: JsonWebKey): JWK {
|
|
15
20
|
return forge.pki.setRsaPublicKey(...convert(key, ['n', 'e']))
|
|
16
21
|
}
|