@libp2p/crypto 5.0.15 → 5.1.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/dist/index.min.js +1 -1
- package/dist/src/keys/ecdsa/ecdsa.d.ts +27 -0
- package/dist/src/keys/ecdsa/ecdsa.d.ts.map +1 -0
- package/dist/src/keys/ecdsa/ecdsa.js +72 -0
- package/dist/src/keys/ecdsa/ecdsa.js.map +1 -0
- package/dist/src/keys/ecdsa/index.d.ts +10 -0
- package/dist/src/keys/ecdsa/index.d.ts.map +1 -0
- package/dist/src/keys/ecdsa/index.js +39 -0
- package/dist/src/keys/ecdsa/index.js.map +1 -0
- package/dist/src/keys/ecdsa/utils.d.ts +11 -0
- package/dist/src/keys/ecdsa/utils.d.ts.map +1 -0
- package/dist/src/keys/ecdsa/utils.js +165 -0
- package/dist/src/keys/ecdsa/utils.js.map +1 -0
- package/dist/src/keys/index.d.ts +13 -3
- package/dist/src/keys/index.d.ts.map +1 -1
- package/dist/src/keys/index.js +101 -11
- package/dist/src/keys/index.js.map +1 -1
- package/dist/src/keys/keys.d.ts +2 -1
- package/dist/src/keys/keys.d.ts.map +1 -1
- package/dist/src/keys/keys.js +2 -0
- package/dist/src/keys/keys.js.map +1 -1
- package/dist/src/keys/rsa/der.d.ts +2 -1
- package/dist/src/keys/rsa/der.d.ts.map +1 -1
- package/dist/src/keys/rsa/der.js +53 -10
- package/dist/src/keys/rsa/der.js.map +1 -1
- package/dist/src/keys/rsa/index.browser.d.ts +1 -0
- package/dist/src/keys/rsa/index.browser.d.ts.map +1 -1
- package/dist/src/keys/rsa/index.browser.js +1 -0
- package/dist/src/keys/rsa/index.browser.js.map +1 -1
- package/dist/src/keys/rsa/index.d.ts +1 -0
- package/dist/src/keys/rsa/index.d.ts.map +1 -1
- package/dist/src/keys/rsa/index.js +1 -0
- package/dist/src/keys/rsa/index.js.map +1 -1
- package/dist/src/keys/rsa/rsa.d.ts +4 -4
- package/dist/src/keys/rsa/rsa.d.ts.map +1 -1
- package/dist/src/keys/rsa/rsa.js +10 -10
- package/dist/src/keys/rsa/rsa.js.map +1 -1
- package/dist/src/keys/rsa/utils.d.ts +12 -2
- package/dist/src/keys/rsa/utils.d.ts.map +1 -1
- package/dist/src/keys/rsa/utils.js +41 -16
- package/dist/src/keys/rsa/utils.js.map +1 -1
- package/dist/src/keys/secp256k1/index.browser.d.ts +4 -0
- package/dist/src/keys/secp256k1/index.browser.d.ts.map +1 -1
- package/dist/src/keys/secp256k1/index.browser.js +4 -0
- package/dist/src/keys/secp256k1/index.browser.js.map +1 -1
- package/dist/src/keys/secp256k1/index.d.ts +4 -0
- package/dist/src/keys/secp256k1/index.d.ts.map +1 -1
- package/dist/src/keys/secp256k1/index.js +4 -0
- package/dist/src/keys/secp256k1/index.js.map +1 -1
- package/dist/typedoc-urls.json +4 -0
- package/package.json +2 -2
- package/src/keys/ecdsa/ecdsa.ts +91 -0
- package/src/keys/ecdsa/index.ts +50 -0
- package/src/keys/ecdsa/utils.ts +212 -0
- package/src/keys/index.ts +132 -15
- package/src/keys/keys.proto +1 -0
- package/src/keys/keys.ts +4 -2
- package/src/keys/rsa/der.ts +68 -11
- package/src/keys/rsa/index.browser.ts +1 -0
- package/src/keys/rsa/index.ts +2 -0
- package/src/keys/rsa/rsa.ts +10 -10
- package/src/keys/rsa/utils.ts +48 -16
- package/src/keys/secp256k1/index.browser.ts +6 -0
- package/src/keys/secp256k1/index.ts +6 -0
package/dist/typedoc-urls.json
CHANGED
|
@@ -16,10 +16,14 @@
|
|
|
16
16
|
"generateKeyPairFromSeed": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.generateKeyPairFromSeed.html",
|
|
17
17
|
"./keys:generateKeyPairFromSeed": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.generateKeyPairFromSeed.html",
|
|
18
18
|
"keyStretcher": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.keyStretcher.html",
|
|
19
|
+
"privateKeyFromCryptoKeyPair": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyFromCryptoKeyPair.html",
|
|
20
|
+
"./keys:privateKeyFromCryptoKeyPair": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyFromCryptoKeyPair.html",
|
|
19
21
|
"privateKeyFromProtobuf": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyFromProtobuf.html",
|
|
20
22
|
"./keys:privateKeyFromProtobuf": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyFromProtobuf.html",
|
|
21
23
|
"privateKeyFromRaw": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyFromRaw.html",
|
|
22
24
|
"./keys:privateKeyFromRaw": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyFromRaw.html",
|
|
25
|
+
"privateKeyToCryptoKeyPair": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyToCryptoKeyPair.html",
|
|
26
|
+
"./keys:privateKeyToCryptoKeyPair": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyToCryptoKeyPair.html",
|
|
23
27
|
"privateKeyToProtobuf": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyToProtobuf.html",
|
|
24
28
|
"./keys:privateKeyToProtobuf": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.privateKeyToProtobuf.html",
|
|
25
29
|
"publicKeyFromMultihash": "https://libp2p.github.io/js-libp2p/functions/_libp2p_crypto.keys.publicKeyFromMultihash.html",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/crypto",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Crypto primitives for libp2p",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/crypto#readme",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"generate": "protons ./src/keys/keys.proto"
|
|
93
93
|
},
|
|
94
94
|
"dependencies": {
|
|
95
|
-
"@libp2p/interface": "^2.
|
|
95
|
+
"@libp2p/interface": "^2.8.0",
|
|
96
96
|
"@noble/curves": "^1.7.0",
|
|
97
97
|
"@noble/hashes": "^1.6.1",
|
|
98
98
|
"multiformats": "^13.3.1",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { base58btc } from 'multiformats/bases/base58'
|
|
2
|
+
import { CID } from 'multiformats/cid'
|
|
3
|
+
import { identity } from 'multiformats/hashes/identity'
|
|
4
|
+
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
5
|
+
import { publicKeyToProtobuf } from '../index.js'
|
|
6
|
+
import { privateKeyToPKIMessage, publicKeyToPKIMessage } from './utils.js'
|
|
7
|
+
import { hashAndVerify, hashAndSign } from './index.js'
|
|
8
|
+
import type { ECDSAPublicKey as ECDSAPublicKeyInterface, ECDSAPrivateKey as ECDSAPrivateKeyInterface } from '@libp2p/interface'
|
|
9
|
+
import type { Digest } from 'multiformats/hashes/digest'
|
|
10
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
11
|
+
|
|
12
|
+
export class ECDSAPublicKey implements ECDSAPublicKeyInterface {
|
|
13
|
+
public readonly type = 'ECDSA'
|
|
14
|
+
public readonly jwk: JsonWebKey
|
|
15
|
+
private _raw?: Uint8Array
|
|
16
|
+
|
|
17
|
+
constructor (jwk: JsonWebKey) {
|
|
18
|
+
this.jwk = jwk
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get raw (): Uint8Array {
|
|
22
|
+
if (this._raw == null) {
|
|
23
|
+
this._raw = publicKeyToPKIMessage(this.jwk)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return this._raw
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
toMultihash (): Digest<0x0, number> {
|
|
30
|
+
return identity.digest(publicKeyToProtobuf(this))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
toCID (): CID<unknown, 114, 0x0, 1> {
|
|
34
|
+
return CID.createV1(114, this.toMultihash())
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
toString (): string {
|
|
38
|
+
return base58btc.encode(this.toMultihash().bytes).substring(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
equals (key?: any): boolean {
|
|
42
|
+
if (key == null || !(key.raw instanceof Uint8Array)) {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return uint8ArrayEquals(this.raw, key.raw)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async verify (data: Uint8Array | Uint8ArrayList, sig: Uint8Array): Promise<boolean> {
|
|
50
|
+
return hashAndVerify(this.jwk, sig, data)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export class ECDSAPrivateKey implements ECDSAPrivateKeyInterface {
|
|
55
|
+
public readonly type = 'ECDSA'
|
|
56
|
+
public readonly jwk: JsonWebKey
|
|
57
|
+
public readonly publicKey: ECDSAPublicKey
|
|
58
|
+
private _raw?: Uint8Array
|
|
59
|
+
|
|
60
|
+
constructor (jwk: JsonWebKey) {
|
|
61
|
+
this.jwk = jwk
|
|
62
|
+
this.publicKey = new ECDSAPublicKey({
|
|
63
|
+
crv: jwk.crv,
|
|
64
|
+
ext: jwk.ext,
|
|
65
|
+
key_ops: ['verify'],
|
|
66
|
+
kty: 'EC',
|
|
67
|
+
x: jwk.x,
|
|
68
|
+
y: jwk.y
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get raw (): Uint8Array {
|
|
73
|
+
if (this._raw == null) {
|
|
74
|
+
this._raw = privateKeyToPKIMessage(this.jwk)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return this._raw
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
equals (key?: any): boolean {
|
|
81
|
+
if (key == null || !(key.raw instanceof Uint8Array)) {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return uint8ArrayEquals(this.raw, key.raw)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async sign (message: Uint8Array | Uint8ArrayList): Promise<Uint8Array> {
|
|
89
|
+
return hashAndSign(this.jwk, message)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { JWKKeyPair } from '../interface.js'
|
|
2
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
3
|
+
|
|
4
|
+
export type Curve = 'P-256' | 'P-384' | 'P-521'
|
|
5
|
+
|
|
6
|
+
export const ECDSA_P_256_OID = '1.2.840.10045.3.1.7'
|
|
7
|
+
export const ECDSA_P_384_OID = '1.3.132.0.34'
|
|
8
|
+
export const ECDSA_P_521_OID = '1.3.132.0.35'
|
|
9
|
+
|
|
10
|
+
export async function generateECDSAKey (curve: Curve = 'P-256'): Promise<JWKKeyPair> {
|
|
11
|
+
const keyPair = await crypto.subtle.generateKey({
|
|
12
|
+
name: 'ECDSA',
|
|
13
|
+
namedCurve: curve
|
|
14
|
+
}, true, ['sign', 'verify'])
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
publicKey: await crypto.subtle.exportKey('jwk', keyPair.publicKey),
|
|
18
|
+
privateKey: await crypto.subtle.exportKey('jwk', keyPair.privateKey)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function hashAndSign (key: JsonWebKey, msg: Uint8Array | Uint8ArrayList): Promise<Uint8Array> {
|
|
23
|
+
const privateKey = await crypto.subtle.importKey('jwk', key, {
|
|
24
|
+
name: 'ECDSA',
|
|
25
|
+
namedCurve: key.crv ?? 'P-256'
|
|
26
|
+
}, false, ['sign'])
|
|
27
|
+
|
|
28
|
+
const signature = await crypto.subtle.sign({
|
|
29
|
+
name: 'ECDSA',
|
|
30
|
+
hash: {
|
|
31
|
+
name: 'SHA-256'
|
|
32
|
+
}
|
|
33
|
+
}, privateKey, msg.subarray())
|
|
34
|
+
|
|
35
|
+
return new Uint8Array(signature, 0, signature.byteLength)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function hashAndVerify (key: JsonWebKey, sig: Uint8Array, msg: Uint8Array | Uint8ArrayList): Promise<boolean> {
|
|
39
|
+
const publicKey = await crypto.subtle.importKey('jwk', key, {
|
|
40
|
+
name: 'ECDSA',
|
|
41
|
+
namedCurve: key.crv ?? 'P-256'
|
|
42
|
+
}, false, ['verify'])
|
|
43
|
+
|
|
44
|
+
return crypto.subtle.verify({
|
|
45
|
+
name: 'ECDSA',
|
|
46
|
+
hash: {
|
|
47
|
+
name: 'SHA-256'
|
|
48
|
+
}
|
|
49
|
+
}, publicKey, sig, msg.subarray())
|
|
50
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { InvalidParametersError } from '@libp2p/interface'
|
|
2
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
3
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
4
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
5
|
+
import { decodeDer, encodeBitString, encodeInteger, encodeOctetString, encodeSequence } from '../rsa/der.js'
|
|
6
|
+
import { ECDSAPrivateKey as ECDSAPrivateKeyClass, ECDSAPublicKey as ECDSAPublicKeyClass } from './ecdsa.js'
|
|
7
|
+
import { generateECDSAKey } from './index.js'
|
|
8
|
+
import type { Curve } from '../ecdh/index.js'
|
|
9
|
+
import type { ECDSAPublicKey, ECDSAPrivateKey } from '@libp2p/interface'
|
|
10
|
+
|
|
11
|
+
// 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
|
|
12
|
+
const OID_256 = Uint8Array.from([0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07])
|
|
13
|
+
// 1.3.132.0.34 secp384r1 (SECG (Certicom) named elliptic curve)
|
|
14
|
+
const OID_384 = Uint8Array.from([0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22])
|
|
15
|
+
// 1.3.132.0.35 secp521r1 (SECG (Certicom) named elliptic curve)
|
|
16
|
+
const OID_521 = Uint8Array.from([0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23])
|
|
17
|
+
|
|
18
|
+
const P_256_KEY_JWK = {
|
|
19
|
+
ext: true,
|
|
20
|
+
kty: 'EC',
|
|
21
|
+
crv: 'P-256'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const P_384_KEY_JWK = {
|
|
25
|
+
ext: true,
|
|
26
|
+
kty: 'EC',
|
|
27
|
+
crv: 'P-384'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const P_521_KEY_JWK = {
|
|
31
|
+
ext: true,
|
|
32
|
+
kty: 'EC',
|
|
33
|
+
crv: 'P-521'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const P_256_KEY_LENGTH = 32
|
|
37
|
+
const P_384_KEY_LENGTH = 48
|
|
38
|
+
const P_521_KEY_LENGTH = 66
|
|
39
|
+
|
|
40
|
+
export function unmarshalECDSAPrivateKey (bytes: Uint8Array): ECDSAPrivateKey {
|
|
41
|
+
const message = decodeDer(bytes)
|
|
42
|
+
|
|
43
|
+
return pkiMessageToECDSAPrivateKey(message)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function pkiMessageToECDSAPrivateKey (message: any): ECDSAPrivateKey {
|
|
47
|
+
const privateKey = message[1]
|
|
48
|
+
const d = uint8ArrayToString(privateKey, 'base64url')
|
|
49
|
+
const coordinates: Uint8Array = message[2][1][0]
|
|
50
|
+
const offset = 1
|
|
51
|
+
let x: string
|
|
52
|
+
let y: string
|
|
53
|
+
|
|
54
|
+
if (privateKey.byteLength === P_256_KEY_LENGTH) {
|
|
55
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_256_KEY_LENGTH), 'base64url')
|
|
56
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_256_KEY_LENGTH), 'base64url')
|
|
57
|
+
|
|
58
|
+
return new ECDSAPrivateKeyClass({
|
|
59
|
+
...P_256_KEY_JWK,
|
|
60
|
+
key_ops: ['sign'],
|
|
61
|
+
d,
|
|
62
|
+
x,
|
|
63
|
+
y
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (privateKey.byteLength === P_384_KEY_LENGTH) {
|
|
68
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_384_KEY_LENGTH), 'base64url')
|
|
69
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_384_KEY_LENGTH), 'base64url')
|
|
70
|
+
|
|
71
|
+
return new ECDSAPrivateKeyClass({
|
|
72
|
+
...P_384_KEY_JWK,
|
|
73
|
+
key_ops: ['sign'],
|
|
74
|
+
d,
|
|
75
|
+
x,
|
|
76
|
+
y
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (privateKey.byteLength === P_521_KEY_LENGTH) {
|
|
81
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_521_KEY_LENGTH), 'base64url')
|
|
82
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_521_KEY_LENGTH), 'base64url')
|
|
83
|
+
|
|
84
|
+
return new ECDSAPrivateKeyClass({
|
|
85
|
+
...P_521_KEY_JWK,
|
|
86
|
+
key_ops: ['sign'],
|
|
87
|
+
d,
|
|
88
|
+
x,
|
|
89
|
+
y
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
throw new InvalidParametersError(`Private key length was wrong length, got ${privateKey.byteLength}, expected 32, 48 or 66`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function unmarshalECDSAPublicKey (bytes: Uint8Array): ECDSAPublicKey {
|
|
97
|
+
const message = decodeDer(bytes)
|
|
98
|
+
|
|
99
|
+
return pkiMessageToECDSAPublicKey(message)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function pkiMessageToECDSAPublicKey (message: any): ECDSAPublicKey {
|
|
103
|
+
const coordinates = message[1][1][0]
|
|
104
|
+
const offset = 1
|
|
105
|
+
let x: string
|
|
106
|
+
let y: string
|
|
107
|
+
|
|
108
|
+
if (coordinates.byteLength === ((P_256_KEY_LENGTH * 2) + 1)) {
|
|
109
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_256_KEY_LENGTH), 'base64url')
|
|
110
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_256_KEY_LENGTH), 'base64url')
|
|
111
|
+
|
|
112
|
+
return new ECDSAPublicKeyClass({
|
|
113
|
+
...P_256_KEY_JWK,
|
|
114
|
+
key_ops: ['verify'],
|
|
115
|
+
x,
|
|
116
|
+
y
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (coordinates.byteLength === ((P_384_KEY_LENGTH * 2) + 1)) {
|
|
121
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_384_KEY_LENGTH), 'base64url')
|
|
122
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_384_KEY_LENGTH), 'base64url')
|
|
123
|
+
|
|
124
|
+
return new ECDSAPublicKeyClass({
|
|
125
|
+
...P_384_KEY_JWK,
|
|
126
|
+
key_ops: ['verify'],
|
|
127
|
+
x,
|
|
128
|
+
y
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (coordinates.byteLength === ((P_521_KEY_LENGTH * 2) + 1)) {
|
|
133
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_521_KEY_LENGTH), 'base64url')
|
|
134
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_521_KEY_LENGTH), 'base64url')
|
|
135
|
+
|
|
136
|
+
return new ECDSAPublicKeyClass({
|
|
137
|
+
...P_521_KEY_JWK,
|
|
138
|
+
key_ops: ['verify'],
|
|
139
|
+
x,
|
|
140
|
+
y
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
throw new InvalidParametersError(`coordinates were wrong length, got ${coordinates.byteLength}, expected 65, 97 or 133`)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function privateKeyToPKIMessage (privateKey: JsonWebKey): Uint8Array {
|
|
148
|
+
return encodeSequence([
|
|
149
|
+
encodeInteger(Uint8Array.from([1])), // header
|
|
150
|
+
encodeOctetString(uint8ArrayFromString(privateKey.d ?? '', 'base64url')), // body
|
|
151
|
+
encodeSequence([ // PKIProtection
|
|
152
|
+
getOID(privateKey.crv)
|
|
153
|
+
], 0xA0),
|
|
154
|
+
encodeSequence([ // extraCerts
|
|
155
|
+
encodeBitString(
|
|
156
|
+
new Uint8ArrayList(
|
|
157
|
+
Uint8Array.from([0x04]),
|
|
158
|
+
uint8ArrayFromString(privateKey.x ?? '', 'base64url'),
|
|
159
|
+
uint8ArrayFromString(privateKey.y ?? '', 'base64url')
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
], 0xA1)
|
|
163
|
+
]).subarray()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function publicKeyToPKIMessage (publicKey: JsonWebKey): Uint8Array {
|
|
167
|
+
return encodeSequence([
|
|
168
|
+
encodeInteger(Uint8Array.from([1])), // header
|
|
169
|
+
encodeSequence([ // PKIProtection
|
|
170
|
+
getOID(publicKey.crv)
|
|
171
|
+
], 0xA0),
|
|
172
|
+
encodeSequence([ // extraCerts
|
|
173
|
+
encodeBitString(
|
|
174
|
+
new Uint8ArrayList(
|
|
175
|
+
Uint8Array.from([0x04]),
|
|
176
|
+
uint8ArrayFromString(publicKey.x ?? '', 'base64url'),
|
|
177
|
+
uint8ArrayFromString(publicKey.y ?? '', 'base64url')
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
], 0xA1)
|
|
181
|
+
]).subarray()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getOID (curve?: string): Uint8Array {
|
|
185
|
+
if (curve === 'P-256') {
|
|
186
|
+
return OID_256
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (curve === 'P-384') {
|
|
190
|
+
return OID_384
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (curve === 'P-521') {
|
|
194
|
+
return OID_521
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
throw new InvalidParametersError(`Invalid curve ${curve}`)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export async function generateECDSAKeyPair (curve: Curve = 'P-256'): Promise<ECDSAPrivateKey> {
|
|
201
|
+
const key = await generateECDSAKey(curve)
|
|
202
|
+
|
|
203
|
+
return new ECDSAPrivateKeyClass(key.privateKey)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function ensureECDSAKey (key: Uint8Array, length: number): Uint8Array {
|
|
207
|
+
key = Uint8Array.from(key ?? [])
|
|
208
|
+
if (key.length !== length) {
|
|
209
|
+
throw new InvalidParametersError(`Key must be a Uint8Array of length ${length}, got ${key.length}`)
|
|
210
|
+
}
|
|
211
|
+
return key
|
|
212
|
+
}
|
package/src/keys/index.ts
CHANGED
|
@@ -8,12 +8,20 @@
|
|
|
8
8
|
* For encryption / decryption support, RSA keys should be used.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { UnsupportedKeyTypeError } from '@libp2p/interface'
|
|
11
|
+
import { InvalidParametersError, UnsupportedKeyTypeError } from '@libp2p/interface'
|
|
12
|
+
import { ECDSAPrivateKey as ECDSAPrivateKeyClass } from './ecdsa/ecdsa.js'
|
|
13
|
+
import { ECDSA_P_256_OID, ECDSA_P_384_OID, ECDSA_P_521_OID } from './ecdsa/index.js'
|
|
14
|
+
import { generateECDSAKeyPair, pkiMessageToECDSAPrivateKey, pkiMessageToECDSAPublicKey, unmarshalECDSAPrivateKey, unmarshalECDSAPublicKey } from './ecdsa/utils.js'
|
|
15
|
+
import { privateKeyLength as ed25519PrivateKeyLength, publicKeyLength as ed25519PublicKeyLength } from './ed25519/index.js'
|
|
12
16
|
import { generateEd25519KeyPair, generateEd25519KeyPairFromSeed, unmarshalEd25519PrivateKey, unmarshalEd25519PublicKey } from './ed25519/utils.js'
|
|
13
17
|
import * as pb from './keys.js'
|
|
14
|
-
import {
|
|
18
|
+
import { decodeDer } from './rsa/der.js'
|
|
19
|
+
import { RSAES_PKCS1_V1_5_OID } from './rsa/index.js'
|
|
20
|
+
import { pkcs1ToRSAPrivateKey, pkixToRSAPublicKey, generateRSAKeyPair, pkcs1MessageToRSAPrivateKey, pkixMessageToRSAPublicKey, jwkToRSAPrivateKey } from './rsa/utils.js'
|
|
21
|
+
import { privateKeyLength as secp256k1PrivateKeyLength, publicKeyLength as secp256k1PublicKeyLength } from './secp256k1/index.js'
|
|
15
22
|
import { generateSecp256k1KeyPair, unmarshalSecp256k1PrivateKey, unmarshalSecp256k1PublicKey } from './secp256k1/utils.js'
|
|
16
|
-
import type {
|
|
23
|
+
import type { Curve } from './ecdsa/index.js'
|
|
24
|
+
import type { PrivateKey, PublicKey, KeyType, RSAPrivateKey, Secp256k1PrivateKey, Ed25519PrivateKey, Secp256k1PublicKey, Ed25519PublicKey, ECDSAPrivateKey, ECDSAPublicKey } from '@libp2p/interface'
|
|
17
25
|
import type { MultihashDigest } from 'multiformats'
|
|
18
26
|
import type { Digest } from 'multiformats/hashes/digest'
|
|
19
27
|
|
|
@@ -27,9 +35,10 @@ export { keyStretcher } from './key-stretcher.js'
|
|
|
27
35
|
*/
|
|
28
36
|
export async function generateKeyPair (type: 'Ed25519'): Promise<Ed25519PrivateKey>
|
|
29
37
|
export async function generateKeyPair (type: 'secp256k1'): Promise<Secp256k1PrivateKey>
|
|
38
|
+
export async function generateKeyPair (type: 'ECDSA', curve?: Curve): Promise<ECDSAPrivateKey>
|
|
30
39
|
export async function generateKeyPair (type: 'RSA', bits?: number): Promise<RSAPrivateKey>
|
|
31
40
|
export async function generateKeyPair (type: KeyType, bits?: number): Promise<PrivateKey>
|
|
32
|
-
export async function generateKeyPair (type: KeyType, bits?: number): Promise<unknown> {
|
|
41
|
+
export async function generateKeyPair (type: KeyType, bits?: number | string): Promise<unknown> {
|
|
33
42
|
if (type === 'Ed25519') {
|
|
34
43
|
return generateEd25519KeyPair()
|
|
35
44
|
}
|
|
@@ -39,7 +48,11 @@ export async function generateKeyPair (type: KeyType, bits?: number): Promise<un
|
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
if (type === 'RSA') {
|
|
42
|
-
return generateRSAKeyPair(bits
|
|
51
|
+
return generateRSAKeyPair(toBits(bits))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (type === 'ECDSA') {
|
|
55
|
+
return generateECDSAKeyPair(toCurve(bits))
|
|
43
56
|
}
|
|
44
57
|
|
|
45
58
|
throw new UnsupportedKeyTypeError()
|
|
@@ -81,6 +94,8 @@ export function publicKeyFromProtobuf (buf: Uint8Array, digest?: Digest<18, numb
|
|
|
81
94
|
return unmarshalEd25519PublicKey(data)
|
|
82
95
|
case pb.KeyType.secp256k1:
|
|
83
96
|
return unmarshalSecp256k1PublicKey(data)
|
|
97
|
+
case pb.KeyType.ECDSA:
|
|
98
|
+
return unmarshalECDSAPublicKey(data)
|
|
84
99
|
default:
|
|
85
100
|
throw new UnsupportedKeyTypeError()
|
|
86
101
|
}
|
|
@@ -90,13 +105,24 @@ export function publicKeyFromProtobuf (buf: Uint8Array, digest?: Digest<18, numb
|
|
|
90
105
|
* Creates a public key from the raw key bytes
|
|
91
106
|
*/
|
|
92
107
|
export function publicKeyFromRaw (buf: Uint8Array): PublicKey {
|
|
93
|
-
if (buf.byteLength ===
|
|
108
|
+
if (buf.byteLength === ed25519PublicKeyLength) {
|
|
94
109
|
return unmarshalEd25519PublicKey(buf)
|
|
95
|
-
} else if (buf.byteLength ===
|
|
110
|
+
} else if (buf.byteLength === secp256k1PublicKeyLength) {
|
|
96
111
|
return unmarshalSecp256k1PublicKey(buf)
|
|
97
|
-
} else {
|
|
98
|
-
return pkixToRSAPublicKey(buf)
|
|
99
112
|
}
|
|
113
|
+
|
|
114
|
+
const message = decodeDer(buf)
|
|
115
|
+
const ecdsaOid = message[1]?.[0]
|
|
116
|
+
|
|
117
|
+
if (ecdsaOid === ECDSA_P_256_OID || ecdsaOid === ECDSA_P_384_OID || ecdsaOid === ECDSA_P_521_OID) {
|
|
118
|
+
return pkiMessageToECDSAPublicKey(message)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (message[0]?.[0] === RSAES_PKCS1_V1_5_OID) {
|
|
122
|
+
return pkixMessageToRSAPublicKey(message, buf)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
throw new InvalidParametersError('Could not extract public key from raw bytes')
|
|
100
126
|
}
|
|
101
127
|
|
|
102
128
|
/**
|
|
@@ -106,7 +132,7 @@ export function publicKeyFromRaw (buf: Uint8Array): PublicKey {
|
|
|
106
132
|
* RSA keys are not supported as in practice we they are not stored in identity
|
|
107
133
|
* multihash since the hash would be very large.
|
|
108
134
|
*/
|
|
109
|
-
export function publicKeyFromMultihash (digest: MultihashDigest<0x0>): Ed25519PublicKey | Secp256k1PublicKey {
|
|
135
|
+
export function publicKeyFromMultihash (digest: MultihashDigest<0x0>): Ed25519PublicKey | Secp256k1PublicKey | ECDSAPublicKey {
|
|
110
136
|
const { Type, Data } = pb.PublicKey.decode(digest.digest)
|
|
111
137
|
const data = Data ?? new Uint8Array()
|
|
112
138
|
|
|
@@ -115,6 +141,8 @@ export function publicKeyFromMultihash (digest: MultihashDigest<0x0>): Ed25519Pu
|
|
|
115
141
|
return unmarshalEd25519PublicKey(data)
|
|
116
142
|
case pb.KeyType.secp256k1:
|
|
117
143
|
return unmarshalSecp256k1PublicKey(data)
|
|
144
|
+
case pb.KeyType.ECDSA:
|
|
145
|
+
return unmarshalECDSAPublicKey(data)
|
|
118
146
|
default:
|
|
119
147
|
throw new UnsupportedKeyTypeError()
|
|
120
148
|
}
|
|
@@ -133,7 +161,7 @@ export function publicKeyToProtobuf (key: PublicKey): Uint8Array {
|
|
|
133
161
|
/**
|
|
134
162
|
* Converts a protobuf serialized private key into its representative object
|
|
135
163
|
*/
|
|
136
|
-
export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Secp256k1PrivateKey | RSAPrivateKey {
|
|
164
|
+
export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Secp256k1PrivateKey | RSAPrivateKey | ECDSAPrivateKey {
|
|
137
165
|
const decoded = pb.PrivateKey.decode(buf)
|
|
138
166
|
const data = decoded.Data ?? new Uint8Array()
|
|
139
167
|
|
|
@@ -144,6 +172,8 @@ export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Se
|
|
|
144
172
|
return unmarshalEd25519PrivateKey(data)
|
|
145
173
|
case pb.KeyType.secp256k1:
|
|
146
174
|
return unmarshalSecp256k1PrivateKey(data)
|
|
175
|
+
case pb.KeyType.ECDSA:
|
|
176
|
+
return unmarshalECDSAPrivateKey(data)
|
|
147
177
|
default:
|
|
148
178
|
throw new UnsupportedKeyTypeError()
|
|
149
179
|
}
|
|
@@ -155,13 +185,24 @@ export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Se
|
|
|
155
185
|
* differentiate between Ed25519 and secp256k1 keys as they are the same length.
|
|
156
186
|
*/
|
|
157
187
|
export function privateKeyFromRaw (buf: Uint8Array): PrivateKey {
|
|
158
|
-
if (buf.byteLength ===
|
|
188
|
+
if (buf.byteLength === ed25519PrivateKeyLength) {
|
|
159
189
|
return unmarshalEd25519PrivateKey(buf)
|
|
160
|
-
} else if (buf.byteLength ===
|
|
190
|
+
} else if (buf.byteLength === secp256k1PrivateKeyLength) {
|
|
161
191
|
return unmarshalSecp256k1PrivateKey(buf)
|
|
162
|
-
} else {
|
|
163
|
-
return pkcs1ToRSAPrivateKey(buf)
|
|
164
192
|
}
|
|
193
|
+
|
|
194
|
+
const message = decodeDer(buf)
|
|
195
|
+
const ecdsaOid = message[2]?.[0]
|
|
196
|
+
|
|
197
|
+
if (ecdsaOid === ECDSA_P_256_OID || ecdsaOid === ECDSA_P_384_OID || ecdsaOid === ECDSA_P_521_OID) {
|
|
198
|
+
return pkiMessageToECDSAPrivateKey(message)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (message.length > 8) {
|
|
202
|
+
return pkcs1MessageToRSAPrivateKey(message)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
throw new InvalidParametersError('Could not extract private key from raw bytes')
|
|
165
206
|
}
|
|
166
207
|
|
|
167
208
|
/**
|
|
@@ -173,3 +214,79 @@ export function privateKeyToProtobuf (key: PrivateKey): Uint8Array {
|
|
|
173
214
|
Data: key.raw
|
|
174
215
|
})
|
|
175
216
|
}
|
|
217
|
+
|
|
218
|
+
function toBits (bits: any): number {
|
|
219
|
+
if (bits == null) {
|
|
220
|
+
return 2048
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return parseInt(bits, 10)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function toCurve (curve: any): Curve {
|
|
227
|
+
if (curve === 'P-256' || curve == null) {
|
|
228
|
+
return 'P-256'
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (curve === 'P-384') {
|
|
232
|
+
return 'P-384'
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (curve === 'P-521') {
|
|
236
|
+
return 'P-521'
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
throw new InvalidParametersError('Unsupported curve, should be P-256, P-384 or P-521')
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Convert a libp2p RSA or ECDSA private key to a WebCrypto CryptoKeyPair
|
|
244
|
+
*/
|
|
245
|
+
export async function privateKeyToCryptoKeyPair (privateKey: PrivateKey): Promise<CryptoKeyPair> {
|
|
246
|
+
if (privateKey.type === 'RSA') {
|
|
247
|
+
return {
|
|
248
|
+
privateKey: await crypto.subtle.importKey('jwk', privateKey.jwk, {
|
|
249
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
250
|
+
hash: { name: 'SHA-256' }
|
|
251
|
+
}, true, ['sign']),
|
|
252
|
+
publicKey: await crypto.subtle.importKey('jwk', privateKey.publicKey.jwk, {
|
|
253
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
254
|
+
hash: { name: 'SHA-256' }
|
|
255
|
+
}, true, ['verify'])
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (privateKey.type === 'ECDSA') {
|
|
260
|
+
return {
|
|
261
|
+
privateKey: await crypto.subtle.importKey('jwk', privateKey.jwk, {
|
|
262
|
+
name: 'ECDSA',
|
|
263
|
+
namedCurve: privateKey.jwk.crv ?? 'P-256'
|
|
264
|
+
}, true, ['sign']),
|
|
265
|
+
publicKey: await crypto.subtle.importKey('jwk', privateKey.publicKey.jwk, {
|
|
266
|
+
name: 'ECDSA',
|
|
267
|
+
namedCurve: privateKey.publicKey.jwk.crv ?? 'P-256'
|
|
268
|
+
}, true, ['verify'])
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
throw new InvalidParametersError('Only RSA and ECDSA keys are supported')
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Convert a RSA or ECDSA WebCrypto CryptoKeyPair to a libp2p private key
|
|
277
|
+
*/
|
|
278
|
+
export async function privateKeyFromCryptoKeyPair (keyPair: CryptoKeyPair): Promise<PrivateKey> {
|
|
279
|
+
if (keyPair.privateKey.algorithm.name === 'RSASSA-PKCS1-v1_5') {
|
|
280
|
+
const jwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey)
|
|
281
|
+
|
|
282
|
+
return jwkToRSAPrivateKey(jwk)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (keyPair.privateKey.algorithm.name === 'ECDSA') {
|
|
286
|
+
const jwk = await crypto.subtle.exportKey('jwk', keyPair.privateKey)
|
|
287
|
+
|
|
288
|
+
return new ECDSAPrivateKeyClass(jwk)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
throw new InvalidParametersError('Only RSA and ECDSA keys are supported')
|
|
292
|
+
}
|
package/src/keys/keys.proto
CHANGED
package/src/keys/keys.ts
CHANGED
|
@@ -10,13 +10,15 @@ import type { Uint8ArrayList } from 'uint8arraylist'
|
|
|
10
10
|
export enum KeyType {
|
|
11
11
|
RSA = 'RSA',
|
|
12
12
|
Ed25519 = 'Ed25519',
|
|
13
|
-
secp256k1 = 'secp256k1'
|
|
13
|
+
secp256k1 = 'secp256k1',
|
|
14
|
+
ECDSA = 'ECDSA'
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
enum __KeyTypeValues {
|
|
17
18
|
RSA = 0,
|
|
18
19
|
Ed25519 = 1,
|
|
19
|
-
secp256k1 = 2
|
|
20
|
+
secp256k1 = 2,
|
|
21
|
+
ECDSA = 3
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
export namespace KeyType {
|