@libp2p/crypto 0.0.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/dist/src/aes/cipher-mode.d.ts +2 -0
- package/dist/src/aes/cipher-mode.d.ts.map +1 -0
- package/dist/src/aes/cipher-mode.js +13 -0
- package/dist/src/aes/cipher-mode.js.map +1 -0
- package/dist/src/aes/ciphers-browser.d.ts +8 -0
- package/dist/src/aes/ciphers-browser.d.ts.map +1 -0
- package/dist/src/aes/ciphers-browser.js +26 -0
- package/dist/src/aes/ciphers-browser.js.map +1 -0
- package/dist/src/aes/ciphers.d.ts +5 -0
- package/dist/src/aes/ciphers.d.ts.map +1 -0
- package/dist/src/aes/ciphers.js +4 -0
- package/dist/src/aes/ciphers.js.map +1 -0
- package/dist/src/aes/index.d.ts +6 -0
- package/dist/src/aes/index.d.ts.map +1 -0
- package/dist/src/aes/index.js +17 -0
- package/dist/src/aes/index.js.map +1 -0
- package/dist/src/ciphers/aes-gcm.browser.d.ts +3 -0
- package/dist/src/ciphers/aes-gcm.browser.d.ts.map +1 -0
- package/dist/src/ciphers/aes-gcm.browser.js +61 -0
- package/dist/src/ciphers/aes-gcm.browser.js.map +1 -0
- package/dist/src/ciphers/aes-gcm.d.ts +3 -0
- package/dist/src/ciphers/aes-gcm.d.ts.map +1 -0
- package/dist/src/ciphers/aes-gcm.js +83 -0
- package/dist/src/ciphers/aes-gcm.js.map +1 -0
- package/dist/src/ciphers/interface.d.ts +14 -0
- package/dist/src/ciphers/interface.d.ts.map +1 -0
- package/dist/src/ciphers/interface.js +2 -0
- package/dist/src/ciphers/interface.js.map +1 -0
- package/dist/src/hmac/index-browser.d.ts +5 -0
- package/dist/src/hmac/index-browser.d.ts.map +1 -0
- package/dist/src/hmac/index-browser.js +25 -0
- package/dist/src/hmac/index-browser.js.map +1 -0
- package/dist/src/hmac/index.d.ts +5 -0
- package/dist/src/hmac/index.d.ts.map +1 -0
- package/dist/src/hmac/index.js +14 -0
- package/dist/src/hmac/index.js.map +1 -0
- package/dist/src/hmac/lengths.d.ts +7 -0
- package/dist/src/hmac/lengths.d.ts.map +1 -0
- package/dist/src/hmac/lengths.js +6 -0
- package/dist/src/hmac/lengths.js.map +1 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +11 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/keys/ecdh-browser.d.ts +3 -0
- package/dist/src/keys/ecdh-browser.d.ts.map +1 -0
- package/dist/src/keys/ecdh-browser.js +97 -0
- package/dist/src/keys/ecdh-browser.js.map +1 -0
- package/dist/src/keys/ecdh.d.ts +3 -0
- package/dist/src/keys/ecdh.d.ts.map +1 -0
- package/dist/src/keys/ecdh.js +26 -0
- package/dist/src/keys/ecdh.js.map +1 -0
- package/dist/src/keys/ed25519-class.d.ts +39 -0
- package/dist/src/keys/ed25519-class.d.ts.map +1 -0
- package/dist/src/keys/ed25519-class.js +119 -0
- package/dist/src/keys/ed25519-class.js.map +1 -0
- package/dist/src/keys/ed25519.d.ts +18 -0
- package/dist/src/keys/ed25519.d.ts.map +1 -0
- package/dist/src/keys/ed25519.js +52 -0
- package/dist/src/keys/ed25519.js.map +1 -0
- package/dist/src/keys/ephemeral-keys.d.ts +9 -0
- package/dist/src/keys/ephemeral-keys.d.ts.map +1 -0
- package/dist/src/keys/ephemeral-keys.js +9 -0
- package/dist/src/keys/ephemeral-keys.js.map +1 -0
- package/dist/src/keys/exporter.d.ts +7 -0
- package/dist/src/keys/exporter.d.ts.map +1 -0
- package/dist/src/keys/exporter.js +13 -0
- package/dist/src/keys/exporter.js.map +1 -0
- package/dist/src/keys/importer.d.ts +7 -0
- package/dist/src/keys/importer.d.ts.map +1 -0
- package/dist/src/keys/importer.js +13 -0
- package/dist/src/keys/importer.js.map +1 -0
- package/dist/src/keys/index.d.ts +33 -0
- package/dist/src/keys/index.d.ts.map +1 -0
- package/dist/src/keys/index.js +111 -0
- package/dist/src/keys/index.js.map +1 -0
- package/dist/src/keys/interface.d.ts +17 -0
- package/dist/src/keys/interface.d.ts.map +1 -0
- package/dist/src/keys/interface.js +2 -0
- package/dist/src/keys/interface.js.map +1 -0
- package/dist/src/keys/jwk2pem.d.ts +4 -0
- package/dist/src/keys/jwk2pem.d.ts.map +1 -0
- package/dist/src/keys/jwk2pem.js +14 -0
- package/dist/src/keys/jwk2pem.js.map +1 -0
- package/dist/src/keys/key-stretcher.d.ts +17 -0
- package/dist/src/keys/key-stretcher.d.ts.map +1 -0
- package/dist/src/keys/key-stretcher.js +65 -0
- package/dist/src/keys/key-stretcher.js.map +1 -0
- package/dist/src/keys/keys.d.ts +225 -0
- package/dist/src/keys/keys.d.ts.map +1 -0
- package/dist/src/keys/keys.js +345 -0
- package/dist/src/keys/keys.js.map +1 -0
- package/dist/src/keys/rsa-browser.d.ts +17 -0
- package/dist/src/keys/rsa-browser.d.ts.map +1 -0
- package/dist/src/keys/rsa-browser.js +99 -0
- package/dist/src/keys/rsa-browser.js.map +1 -0
- package/dist/src/keys/rsa-class.d.ts +42 -0
- package/dist/src/keys/rsa-class.d.ts.map +1 -0
- package/dist/src/keys/rsa-class.js +126 -0
- package/dist/src/keys/rsa-class.js.map +1 -0
- package/dist/src/keys/rsa-utils.d.ts +7 -0
- package/dist/src/keys/rsa-utils.d.ts.map +1 -0
- package/dist/src/keys/rsa-utils.js +65 -0
- package/dist/src/keys/rsa-utils.js.map +1 -0
- package/dist/src/keys/rsa.d.ts +13 -0
- package/dist/src/keys/rsa.d.ts.map +1 -0
- package/dist/src/keys/rsa.js +58 -0
- package/dist/src/keys/rsa.js.map +1 -0
- package/dist/src/keys/secp256k1-class.d.ts +36 -0
- package/dist/src/keys/secp256k1-class.d.ts.map +1 -0
- package/dist/src/keys/secp256k1-class.js +95 -0
- package/dist/src/keys/secp256k1-class.js.map +1 -0
- package/dist/src/keys/secp256k1.d.ts +17 -0
- package/dist/src/keys/secp256k1.d.ts.map +1 -0
- package/dist/src/keys/secp256k1.js +65 -0
- package/dist/src/keys/secp256k1.js.map +1 -0
- package/dist/src/pbkdf2.d.ts +5 -0
- package/dist/src/pbkdf2.d.ts.map +1 -0
- package/dist/src/pbkdf2.js +30 -0
- package/dist/src/pbkdf2.js.map +1 -0
- package/dist/src/random-bytes.d.ts +2 -0
- package/dist/src/random-bytes.d.ts.map +1 -0
- package/dist/src/random-bytes.js +9 -0
- package/dist/src/random-bytes.js.map +1 -0
- package/dist/src/util.d.ts +9 -0
- package/dist/src/util.d.ts.map +1 -0
- package/dist/src/util.js +37 -0
- package/dist/src/util.js.map +1 -0
- package/dist/src/webcrypto.d.ts +5 -0
- package/dist/src/webcrypto.d.ts.map +1 -0
- package/dist/src/webcrypto.js +17 -0
- package/dist/src/webcrypto.js.map +1 -0
- package/package.json +123 -4
- package/src/aes/cipher-mode.ts +15 -0
- package/src/aes/ciphers-browser.ts +28 -0
- package/src/aes/ciphers.ts +4 -0
- package/src/aes/index.ts +25 -0
- package/src/ciphers/aes-gcm.browser.ts +74 -0
- package/src/ciphers/aes-gcm.ts +102 -0
- package/src/ciphers/interface.ts +15 -0
- package/src/hmac/index-browser.ts +35 -0
- package/src/hmac/index.ts +15 -0
- package/src/hmac/lengths.ts +6 -0
- package/src/index.ts +11 -0
- package/src/keys/ecdh-browser.ts +138 -0
- package/src/keys/ecdh.ts +33 -0
- package/src/keys/ed25519-class.ts +145 -0
- package/src/keys/ed25519.ts +63 -0
- package/src/keys/ephemeral-keys.ts +9 -0
- package/src/keys/exporter.ts +13 -0
- package/src/keys/importer.ts +13 -0
- package/src/keys/index.ts +126 -0
- package/src/keys/interface.ts +20 -0
- package/src/keys/jwk2pem.ts +16 -0
- package/src/keys/key-stretcher.ts +77 -0
- package/src/keys/keys.d.ts +146 -0
- package/src/keys/keys.js +366 -0
- package/src/keys/keys.proto +15 -0
- package/src/keys/rsa-browser.ts +156 -0
- package/src/keys/rsa-class.ts +155 -0
- package/src/keys/rsa-utils.ts +74 -0
- package/src/keys/rsa.ts +69 -0
- package/src/keys/secp256k1-class.ts +118 -0
- package/src/keys/secp256k1.ts +69 -0
- package/src/pbkdf2.ts +39 -0
- package/src/random-bytes.ts +9 -0
- package/src/util.ts +42 -0
- package/src/webcrypto.ts +24 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import errcode from 'err-code'
|
|
2
|
+
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
3
|
+
import { sha256 } from 'multiformats/hashes/sha2'
|
|
4
|
+
import { base58btc } from 'multiformats/bases/base58'
|
|
5
|
+
import { identity } from 'multiformats/hashes/identity'
|
|
6
|
+
import * as crypto from './ed25519.js'
|
|
7
|
+
import * as pbm from './keys.js'
|
|
8
|
+
import { exporter } from './exporter.js'
|
|
9
|
+
|
|
10
|
+
export class Ed25519PublicKey {
|
|
11
|
+
private readonly _key: Uint8Array
|
|
12
|
+
|
|
13
|
+
constructor (key: Uint8Array) {
|
|
14
|
+
this._key = ensureKey(key, crypto.publicKeyLength)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async verify (data: Uint8Array, sig: Uint8Array) { // eslint-disable-line require-await
|
|
18
|
+
return await crypto.hashAndVerify(this._key, sig, data)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
marshal () {
|
|
22
|
+
return this._key
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get bytes () {
|
|
26
|
+
return pbm.PublicKey.encode({
|
|
27
|
+
Type: pbm.KeyType.Ed25519,
|
|
28
|
+
Data: this.marshal()
|
|
29
|
+
}).finish()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
equals (key: Ed25519PublicKey) {
|
|
33
|
+
return uint8ArrayEquals(this.bytes, key.bytes)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async hash () {
|
|
37
|
+
const { bytes } = await sha256.digest(this.bytes)
|
|
38
|
+
|
|
39
|
+
return bytes
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class Ed25519PrivateKey {
|
|
44
|
+
private readonly _key: Uint8Array
|
|
45
|
+
private readonly _publicKey: Uint8Array
|
|
46
|
+
|
|
47
|
+
// key - 64 byte Uint8Array containing private key
|
|
48
|
+
// publicKey - 32 byte Uint8Array containing public key
|
|
49
|
+
constructor (key: Uint8Array, publicKey: Uint8Array) {
|
|
50
|
+
this._key = ensureKey(key, crypto.privateKeyLength)
|
|
51
|
+
this._publicKey = ensureKey(publicKey, crypto.publicKeyLength)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async sign (message: Uint8Array) { // eslint-disable-line require-await
|
|
55
|
+
return await crypto.hashAndSign(this._key, message)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get public () {
|
|
59
|
+
return new Ed25519PublicKey(this._publicKey)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
marshal () {
|
|
63
|
+
return this._key
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get bytes () {
|
|
67
|
+
return pbm.PrivateKey.encode({
|
|
68
|
+
Type: pbm.KeyType.Ed25519,
|
|
69
|
+
Data: this.marshal()
|
|
70
|
+
}).finish()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
equals (key: Ed25519PrivateKey) {
|
|
74
|
+
return uint8ArrayEquals(this.bytes, key.bytes)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async hash () {
|
|
78
|
+
const { bytes } = await sha256.digest(this.bytes)
|
|
79
|
+
|
|
80
|
+
return bytes
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Gets the ID of the key.
|
|
85
|
+
*
|
|
86
|
+
* The key id is the base58 encoding of the identity multihash containing its public key.
|
|
87
|
+
* The public key is a protobuf encoding containing a type and the DER encoding
|
|
88
|
+
* of the PKCS SubjectPublicKeyInfo.
|
|
89
|
+
*
|
|
90
|
+
* @returns {Promise<string>}
|
|
91
|
+
*/
|
|
92
|
+
async id () {
|
|
93
|
+
const encoding = await identity.digest(this.public.bytes)
|
|
94
|
+
return base58btc.encode(encoding.bytes).substring(1)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Exports the key into a password protected `format`
|
|
99
|
+
*/
|
|
100
|
+
async export (password: string, format = 'libp2p-key') { // eslint-disable-line require-await
|
|
101
|
+
if (format === 'libp2p-key') {
|
|
102
|
+
return await exporter(this.bytes, password)
|
|
103
|
+
} else {
|
|
104
|
+
throw errcode(new Error(`export format '${format}' is not supported`), 'ERR_INVALID_EXPORT_FORMAT')
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function unmarshalEd25519PrivateKey (bytes: Uint8Array) {
|
|
110
|
+
// Try the old, redundant public key version
|
|
111
|
+
if (bytes.length > crypto.privateKeyLength) {
|
|
112
|
+
bytes = ensureKey(bytes, crypto.privateKeyLength + crypto.publicKeyLength)
|
|
113
|
+
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
|
114
|
+
const publicKeyBytes = bytes.slice(crypto.privateKeyLength, bytes.length)
|
|
115
|
+
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
bytes = ensureKey(bytes, crypto.privateKeyLength)
|
|
119
|
+
const privateKeyBytes = bytes.slice(0, crypto.privateKeyLength)
|
|
120
|
+
const publicKeyBytes = bytes.slice(crypto.publicKeyLength)
|
|
121
|
+
return new Ed25519PrivateKey(privateKeyBytes, publicKeyBytes)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function unmarshalEd25519PublicKey (bytes: Uint8Array) {
|
|
125
|
+
bytes = ensureKey(bytes, crypto.publicKeyLength)
|
|
126
|
+
return new Ed25519PublicKey(bytes)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function generateKeyPair () {
|
|
130
|
+
const { privateKey, publicKey } = await crypto.generateKey()
|
|
131
|
+
return new Ed25519PrivateKey(privateKey, publicKey)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function generateKeyPairFromSeed (seed: Uint8Array) {
|
|
135
|
+
const { privateKey, publicKey } = await crypto.generateKeyFromSeed(seed)
|
|
136
|
+
return new Ed25519PrivateKey(privateKey, publicKey)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function ensureKey (key: Uint8Array, length: number) {
|
|
140
|
+
key = Uint8Array.from(key ?? [])
|
|
141
|
+
if (key.length !== length) {
|
|
142
|
+
throw errcode(new Error(`Key must be a Uint8Array of length ${length}, got ${key.length}`), 'ERR_INVALID_KEY_TYPE')
|
|
143
|
+
}
|
|
144
|
+
return key
|
|
145
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as ed from '@noble/ed25519'
|
|
2
|
+
|
|
3
|
+
const PUBLIC_KEY_BYTE_LENGTH = 32
|
|
4
|
+
const PRIVATE_KEY_BYTE_LENGTH = 64 // private key is actually 32 bytes but for historical reasons we concat private and public keys
|
|
5
|
+
const KEYS_BYTE_LENGTH = 32
|
|
6
|
+
|
|
7
|
+
export { PUBLIC_KEY_BYTE_LENGTH as publicKeyLength }
|
|
8
|
+
export { PRIVATE_KEY_BYTE_LENGTH as privateKeyLength }
|
|
9
|
+
|
|
10
|
+
export async function generateKey () {
|
|
11
|
+
// the actual private key (32 bytes)
|
|
12
|
+
const privateKeyRaw = ed.utils.randomPrivateKey()
|
|
13
|
+
const publicKey = await ed.getPublicKey(privateKeyRaw)
|
|
14
|
+
|
|
15
|
+
// concatenated the public key to the private key
|
|
16
|
+
const privateKey = concatKeys(privateKeyRaw, publicKey)
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
privateKey,
|
|
20
|
+
publicKey
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generate keypair from a 32 byte uint8array
|
|
26
|
+
*/
|
|
27
|
+
export async function generateKeyFromSeed (seed: Uint8Array) {
|
|
28
|
+
if (seed.length !== KEYS_BYTE_LENGTH) {
|
|
29
|
+
throw new TypeError('"seed" must be 32 bytes in length.')
|
|
30
|
+
} else if (!(seed instanceof Uint8Array)) {
|
|
31
|
+
throw new TypeError('"seed" must be a node.js Buffer, or Uint8Array.')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// based on node forges algorithm, the seed is used directly as private key
|
|
35
|
+
const privateKeyRaw = seed
|
|
36
|
+
const publicKey = await ed.getPublicKey(privateKeyRaw)
|
|
37
|
+
|
|
38
|
+
const privateKey = concatKeys(privateKeyRaw, publicKey)
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
privateKey,
|
|
42
|
+
publicKey
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function hashAndSign (privateKey: Uint8Array, msg: Uint8Array) {
|
|
47
|
+
const privateKeyRaw = privateKey.slice(0, KEYS_BYTE_LENGTH)
|
|
48
|
+
|
|
49
|
+
return await ed.sign(msg, privateKeyRaw)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function hashAndVerify (publicKey: Uint8Array, sig: Uint8Array, msg: Uint8Array) {
|
|
53
|
+
return await ed.verify(sig, msg, publicKey)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function concatKeys (privateKeyRaw: Uint8Array, publicKey: Uint8Array) {
|
|
57
|
+
const privateKey = new Uint8Array(PRIVATE_KEY_BYTE_LENGTH)
|
|
58
|
+
for (let i = 0; i < KEYS_BYTE_LENGTH; i++) {
|
|
59
|
+
privateKey[i] = privateKeyRaw[i]
|
|
60
|
+
privateKey[KEYS_BYTE_LENGTH + i] = publicKey[i]
|
|
61
|
+
}
|
|
62
|
+
return privateKey
|
|
63
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { generateEphmeralKeyPair } from './ecdh.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates an ephemeral public key and returns a function that will compute
|
|
5
|
+
* the shared secret key.
|
|
6
|
+
*
|
|
7
|
+
* Focuses only on ECDH now, but can be made more general in the future.
|
|
8
|
+
*/
|
|
9
|
+
export default generateEphmeralKeyPair
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { base64 } from 'multiformats/bases/base64'
|
|
2
|
+
import * as ciphers from '../ciphers/aes-gcm.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Exports the given PrivateKey as a base64 encoded string.
|
|
6
|
+
* The PrivateKey is encrypted via a password derived PBKDF2 key
|
|
7
|
+
* leveraging the aes-gcm cipher algorithm.
|
|
8
|
+
*/
|
|
9
|
+
export async function exporter (privateKey: Uint8Array, password: string) {
|
|
10
|
+
const cipher = ciphers.create()
|
|
11
|
+
const encryptedKey = await cipher.encrypt(privateKey, password)
|
|
12
|
+
return base64.encode(encryptedKey)
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { base64 } from 'multiformats/bases/base64'
|
|
2
|
+
import * as ciphers from '../ciphers/aes-gcm.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Attempts to decrypt a base64 encoded PrivateKey string
|
|
6
|
+
* with the given password. The privateKey must have been exported
|
|
7
|
+
* using the same password and underlying cipher (aes-gcm)
|
|
8
|
+
*/
|
|
9
|
+
export async function importer (privateKey: string, password: string) {
|
|
10
|
+
const encryptedKey = base64.decode(privateKey)
|
|
11
|
+
const cipher = ciphers.create()
|
|
12
|
+
return await cipher.decrypt(encryptedKey, password)
|
|
13
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as keysPBM from './keys.js'
|
|
2
|
+
import 'node-forge/lib/asn1.js'
|
|
3
|
+
import 'node-forge/lib/pbe.js'
|
|
4
|
+
// @ts-expect-error types are missing
|
|
5
|
+
import forge from 'node-forge/lib/forge.js'
|
|
6
|
+
import errcode from 'err-code'
|
|
7
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
8
|
+
import { keyStretcher } from './key-stretcher.js'
|
|
9
|
+
import generateEphemeralKeyPair from './ephemeral-keys.js'
|
|
10
|
+
import { importer } from './importer.js'
|
|
11
|
+
import * as RSA from './rsa-class.js'
|
|
12
|
+
import * as Ed25519 from './ed25519-class.js'
|
|
13
|
+
import * as Secp256k1 from './secp256k1-class.js'
|
|
14
|
+
|
|
15
|
+
export { keyStretcher }
|
|
16
|
+
export { generateEphemeralKeyPair }
|
|
17
|
+
export { keysPBM }
|
|
18
|
+
|
|
19
|
+
export const supportedKeys = {
|
|
20
|
+
rsa: RSA,
|
|
21
|
+
ed25519: Ed25519,
|
|
22
|
+
secp256k1: Secp256k1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function unsupportedKey (type: string) {
|
|
26
|
+
const supported = Object.keys(supportedKeys).join(' / ')
|
|
27
|
+
return errcode(new Error(`invalid or unsupported key type ${type}. Must be ${supported}`), 'ERR_UNSUPPORTED_KEY_TYPE')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function typeToKey (type: string) {
|
|
31
|
+
type = type.toLowerCase()
|
|
32
|
+
|
|
33
|
+
if (type === 'rsa' || type === 'ed25519' || type === 'secp256k1') {
|
|
34
|
+
return supportedKeys[type]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw unsupportedKey(type)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Generates a keypair of the given type and bitsize
|
|
41
|
+
export async function generateKeyPair (type: 'RSA' | 'Ed25519' | 'secp256k1', bits?: number) { // eslint-disable-line require-await
|
|
42
|
+
return await typeToKey(type).generateKeyPair(bits ?? 2048)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Generates a keypair of the given type and bitsize
|
|
46
|
+
// seed is a 32 byte uint8array
|
|
47
|
+
export async function generateKeyPairFromSeed (type: 'RSA' | 'Ed25519' | 'secp256k1', seed: Uint8Array, bits?: number) { // eslint-disable-line require-await
|
|
48
|
+
if (type.toLowerCase() !== 'ed25519') {
|
|
49
|
+
throw errcode(new Error('Seed key derivation is unimplemented for RSA or secp256k1'), 'ERR_UNSUPPORTED_KEY_DERIVATION_TYPE')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return await Ed25519.generateKeyPairFromSeed(seed)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Converts a protobuf serialized public key into its
|
|
56
|
+
// representative object
|
|
57
|
+
export function unmarshalPublicKey (buf: Uint8Array) {
|
|
58
|
+
const decoded = keysPBM.PublicKey.decode(buf)
|
|
59
|
+
const data = decoded.Data
|
|
60
|
+
|
|
61
|
+
switch (decoded.Type) {
|
|
62
|
+
case keysPBM.KeyType.RSA:
|
|
63
|
+
return supportedKeys.rsa.unmarshalRsaPublicKey(data)
|
|
64
|
+
case keysPBM.KeyType.Ed25519:
|
|
65
|
+
return supportedKeys.ed25519.unmarshalEd25519PublicKey(data)
|
|
66
|
+
case keysPBM.KeyType.Secp256k1:
|
|
67
|
+
return supportedKeys.secp256k1.unmarshalSecp256k1PublicKey(data)
|
|
68
|
+
default:
|
|
69
|
+
throw unsupportedKey(decoded.Type)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Converts a public key object into a protobuf serialized public key
|
|
74
|
+
export function marshalPublicKey (key: { bytes: Uint8Array }, type?: string) {
|
|
75
|
+
type = (type ?? 'rsa').toLowerCase()
|
|
76
|
+
typeToKey(type) // check type
|
|
77
|
+
return key.bytes
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Converts a protobuf serialized private key into its
|
|
81
|
+
// representative object
|
|
82
|
+
export async function unmarshalPrivateKey (buf: Uint8Array) { // eslint-disable-line require-await
|
|
83
|
+
const decoded = keysPBM.PrivateKey.decode(buf)
|
|
84
|
+
const data = decoded.Data
|
|
85
|
+
|
|
86
|
+
switch (decoded.Type) {
|
|
87
|
+
case keysPBM.KeyType.RSA:
|
|
88
|
+
return await supportedKeys.rsa.unmarshalRsaPrivateKey(data)
|
|
89
|
+
case keysPBM.KeyType.Ed25519:
|
|
90
|
+
return supportedKeys.ed25519.unmarshalEd25519PrivateKey(data)
|
|
91
|
+
case keysPBM.KeyType.Secp256k1:
|
|
92
|
+
return supportedKeys.secp256k1.unmarshalSecp256k1PrivateKey(data)
|
|
93
|
+
default:
|
|
94
|
+
throw unsupportedKey(decoded.Type)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Converts a private key object into a protobuf serialized private key
|
|
99
|
+
export function marshalPrivateKey (key: { bytes: Uint8Array }, type?: string) {
|
|
100
|
+
type = (type ?? 'rsa').toLowerCase()
|
|
101
|
+
typeToKey(type) // check type
|
|
102
|
+
return key.bytes
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
* @param {string} encryptedKey
|
|
108
|
+
* @param {string} password
|
|
109
|
+
*/
|
|
110
|
+
export async function importKey (encryptedKey: string, password: string) { // eslint-disable-line require-await
|
|
111
|
+
try {
|
|
112
|
+
const key = await importer(encryptedKey, password)
|
|
113
|
+
return await unmarshalPrivateKey(key)
|
|
114
|
+
} catch (_) {
|
|
115
|
+
// Ignore and try the old pem decrypt
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Only rsa supports pem right now
|
|
119
|
+
const key = forge.pki.decryptRsaPrivateKey(encryptedKey, password)
|
|
120
|
+
if (key === null) {
|
|
121
|
+
throw errcode(new Error('Cannot read the key, most likely the password is wrong or not a RSA key'), 'ERR_CANNOT_DECRYPT_PEM')
|
|
122
|
+
}
|
|
123
|
+
let der = forge.asn1.toDer(forge.pki.privateKeyToAsn1(key))
|
|
124
|
+
der = uint8ArrayFromString(der.getBytes(), 'ascii')
|
|
125
|
+
return await supportedKeys.rsa.unmarshalRsaPrivateKey(der)
|
|
126
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
export interface JWKKeyPair {
|
|
3
|
+
privateKey: JsonWebKey
|
|
4
|
+
publicKey: JsonWebKey
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface Uint8ArrayKeyPair {
|
|
8
|
+
privateKey: Uint8Array
|
|
9
|
+
publicKey: Uint8Array
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ECDHKeyPair {
|
|
13
|
+
private: Uint8Array
|
|
14
|
+
public: Uint8Array
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ECDHKey {
|
|
18
|
+
key: Uint8Array
|
|
19
|
+
genSharedKey: (theirPub: Uint8Array, forcePrivate?: ECDHKeyPair) => Promise<Uint8Array>
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import 'node-forge/lib/rsa.js'
|
|
2
|
+
// @ts-expect-error types are missing
|
|
3
|
+
import forge from 'node-forge/lib/forge.js'
|
|
4
|
+
import { base64urlToBigInteger } from '../util'
|
|
5
|
+
|
|
6
|
+
function convert (key: any, types: string[]) {
|
|
7
|
+
return types.map(t => base64urlToBigInteger(key[t]))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function jwk2priv (key: JsonWebKey) {
|
|
11
|
+
return forge.pki.setRsaPrivateKey(...convert(key, ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function jwk2pub (key: JsonWebKey) {
|
|
15
|
+
return forge.pki.setRsaPublicKey(...convert(key, ['n', 'e']))
|
|
16
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import errcode from 'err-code'
|
|
2
|
+
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
|
|
3
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
4
|
+
import * as hmac from '../hmac/index.js'
|
|
5
|
+
|
|
6
|
+
const cipherMap = {
|
|
7
|
+
'AES-128': {
|
|
8
|
+
ivSize: 16,
|
|
9
|
+
keySize: 16
|
|
10
|
+
},
|
|
11
|
+
'AES-256': {
|
|
12
|
+
ivSize: 16,
|
|
13
|
+
keySize: 32
|
|
14
|
+
},
|
|
15
|
+
Blowfish: {
|
|
16
|
+
ivSize: 8,
|
|
17
|
+
keySize: 32
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generates a set of keys for each party by stretching the shared key.
|
|
23
|
+
* (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey)
|
|
24
|
+
*/
|
|
25
|
+
export async function keyStretcher (cipherType: 'AES-128' | 'AES-256' | 'Blowfish', hash: 'SHA1' | 'SHA256' | 'SHA512', secret: Uint8Array) {
|
|
26
|
+
const cipher = cipherMap[cipherType]
|
|
27
|
+
|
|
28
|
+
if (cipher == null) {
|
|
29
|
+
const allowed = Object.keys(cipherMap).join(' / ')
|
|
30
|
+
throw errcode(new Error(`unknown cipher type '${cipherType}'. Must be ${allowed}`), 'ERR_INVALID_CIPHER_TYPE')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (hash == null) {
|
|
34
|
+
throw errcode(new Error('missing hash type'), 'ERR_MISSING_HASH_TYPE')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cipherKeySize = cipher.keySize
|
|
38
|
+
const ivSize = cipher.ivSize
|
|
39
|
+
const hmacKeySize = 20
|
|
40
|
+
const seed = uint8ArrayFromString('key expansion')
|
|
41
|
+
const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize)
|
|
42
|
+
|
|
43
|
+
const m = await hmac.create(hash, secret)
|
|
44
|
+
let a = await m.digest(seed)
|
|
45
|
+
|
|
46
|
+
const result = []
|
|
47
|
+
let j = 0
|
|
48
|
+
|
|
49
|
+
while (j < resultLength) {
|
|
50
|
+
const b = await m.digest(uint8ArrayConcat([a, seed]))
|
|
51
|
+
let todo = b.length
|
|
52
|
+
|
|
53
|
+
if (j + todo > resultLength) {
|
|
54
|
+
todo = resultLength - j
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
result.push(b)
|
|
58
|
+
j += todo
|
|
59
|
+
a = await m.digest(a)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const half = resultLength / 2
|
|
63
|
+
const resultBuffer = uint8ArrayConcat(result)
|
|
64
|
+
const r1 = resultBuffer.slice(0, half)
|
|
65
|
+
const r2 = resultBuffer.slice(half, resultLength)
|
|
66
|
+
|
|
67
|
+
const createKey = (res: Uint8Array) => ({
|
|
68
|
+
iv: res.slice(0, ivSize),
|
|
69
|
+
cipherKey: res.slice(ivSize, ivSize + cipherKeySize),
|
|
70
|
+
macKey: res.slice(ivSize + cipherKeySize)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
k1: createKey(r1),
|
|
75
|
+
k2: createKey(r2)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import $protobuf from 'protobufjs'
|
|
2
|
+
|
|
3
|
+
/** KeyType enum. */
|
|
4
|
+
export enum KeyType {
|
|
5
|
+
RSA = 0,
|
|
6
|
+
Ed25519 = 1,
|
|
7
|
+
Secp256k1 = 2
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface IPublicKey {
|
|
11
|
+
|
|
12
|
+
/** PublicKey Type. */
|
|
13
|
+
Type: KeyType
|
|
14
|
+
|
|
15
|
+
/** PublicKey Data. */
|
|
16
|
+
Data: Uint8Array
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Represents a PublicKey. */
|
|
20
|
+
export class PublicKey implements IPublicKey {
|
|
21
|
+
/**
|
|
22
|
+
* Constructs a new PublicKey.
|
|
23
|
+
*
|
|
24
|
+
* @param [p] - Properties to set
|
|
25
|
+
*/
|
|
26
|
+
constructor (p?: IPublicKey);
|
|
27
|
+
|
|
28
|
+
/** PublicKey Type. */
|
|
29
|
+
public Type: KeyType
|
|
30
|
+
|
|
31
|
+
/** PublicKey Data. */
|
|
32
|
+
public Data: Uint8Array
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Encodes the specified PublicKey message. Does not implicitly {@link PublicKey.verify|verify} messages.
|
|
36
|
+
*
|
|
37
|
+
* @param m - PublicKey message or plain object to encode
|
|
38
|
+
* @param [w] - Writer to encode to
|
|
39
|
+
* @returns Writer
|
|
40
|
+
*/
|
|
41
|
+
public static encode (m: IPublicKey, w?: $protobuf.Writer): $protobuf.Writer;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Decodes a PublicKey message from the specified reader or buffer.
|
|
45
|
+
*
|
|
46
|
+
* @param r - Reader or buffer to decode from
|
|
47
|
+
* @param [l] - Message length if known beforehand
|
|
48
|
+
* @returns PublicKey
|
|
49
|
+
* @throws {Error} If the payload is not a reader or valid buffer
|
|
50
|
+
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
|
51
|
+
*/
|
|
52
|
+
public static decode (r: ($protobuf.Reader|Uint8Array), l?: number): PublicKey;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a PublicKey message from a plain object. Also converts values to their respective internal types.
|
|
56
|
+
*
|
|
57
|
+
* @param d - Plain object
|
|
58
|
+
* @returns PublicKey
|
|
59
|
+
*/
|
|
60
|
+
public static fromObject (d: { [k: string]: any }): PublicKey;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Creates a plain object from a PublicKey message. Also converts values to other types if specified.
|
|
64
|
+
*
|
|
65
|
+
* @param m - PublicKey
|
|
66
|
+
* @param [o] - Conversion options
|
|
67
|
+
* @returns Plain object
|
|
68
|
+
*/
|
|
69
|
+
public static toObject (m: PublicKey, o?: $protobuf.IConversionOptions): { [k: string]: any };
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Converts this PublicKey to JSON.
|
|
73
|
+
*
|
|
74
|
+
* @returns JSON object
|
|
75
|
+
*/
|
|
76
|
+
public toJSON (): { [k: string]: any };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface IPrivateKey {
|
|
80
|
+
|
|
81
|
+
/** PrivateKey Type. */
|
|
82
|
+
Type: KeyType
|
|
83
|
+
|
|
84
|
+
/** PrivateKey Data. */
|
|
85
|
+
Data: Uint8Array
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Represents a PrivateKey. */
|
|
89
|
+
export class PrivateKey implements IPrivateKey {
|
|
90
|
+
/**
|
|
91
|
+
* Constructs a new PrivateKey.
|
|
92
|
+
*
|
|
93
|
+
* @param [p] - Properties to set
|
|
94
|
+
*/
|
|
95
|
+
constructor (p?: IPrivateKey);
|
|
96
|
+
|
|
97
|
+
/** PrivateKey Type. */
|
|
98
|
+
public Type: KeyType
|
|
99
|
+
|
|
100
|
+
/** PrivateKey Data. */
|
|
101
|
+
public Data: Uint8Array
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Encodes the specified PrivateKey message. Does not implicitly {@link PrivateKey.verify|verify} messages.
|
|
105
|
+
*
|
|
106
|
+
* @param m - PrivateKey message or plain object to encode
|
|
107
|
+
* @param [w] - Writer to encode to
|
|
108
|
+
* @returns Writer
|
|
109
|
+
*/
|
|
110
|
+
public static encode (m: IPrivateKey, w?: $protobuf.Writer): $protobuf.Writer;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Decodes a PrivateKey message from the specified reader or buffer.
|
|
114
|
+
*
|
|
115
|
+
* @param r - Reader or buffer to decode from
|
|
116
|
+
* @param [l] - Message length if known beforehand
|
|
117
|
+
* @returns PrivateKey
|
|
118
|
+
* @throws {Error} If the payload is not a reader or valid buffer
|
|
119
|
+
* @throws {$protobuf.util.ProtocolError} If required fields are missing
|
|
120
|
+
*/
|
|
121
|
+
public static decode (r: ($protobuf.Reader|Uint8Array), l?: number): PrivateKey;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Creates a PrivateKey message from a plain object. Also converts values to their respective internal types.
|
|
125
|
+
*
|
|
126
|
+
* @param d - Plain object
|
|
127
|
+
* @returns PrivateKey
|
|
128
|
+
*/
|
|
129
|
+
public static fromObject (d: { [k: string]: any }): PrivateKey;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Creates a plain object from a PrivateKey message. Also converts values to other types if specified.
|
|
133
|
+
*
|
|
134
|
+
* @param m - PrivateKey
|
|
135
|
+
* @param [o] - Conversion options
|
|
136
|
+
* @returns Plain object
|
|
137
|
+
*/
|
|
138
|
+
public static toObject (m: PrivateKey, o?: $protobuf.IConversionOptions): { [k: string]: any };
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Converts this PrivateKey to JSON.
|
|
142
|
+
*
|
|
143
|
+
* @returns JSON object
|
|
144
|
+
*/
|
|
145
|
+
public toJSON (): { [k: string]: any };
|
|
146
|
+
}
|