@libp2p/crypto 5.0.15-71267286 → 5.0.15-78cd7d53e
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 +65 -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 +180 -0
- package/dist/src/keys/ecdsa/utils.js.map +1 -0
- package/dist/src/keys/index.d.ts +5 -3
- package/dist/src/keys/index.d.ts.map +1 -1
- package/dist/src/keys/index.js +56 -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/package.json +2 -2
- package/src/keys/ecdsa/ecdsa.ts +84 -0
- package/src/keys/ecdsa/index.ts +50 -0
- package/src/keys/ecdsa/utils.ts +227 -0
- package/src/keys/index.ts +79 -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
|
@@ -0,0 +1,84 @@
|
|
|
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, publicKey: JsonWebKey) {
|
|
61
|
+
this.jwk = jwk
|
|
62
|
+
this.publicKey = new ECDSAPublicKey(publicKey)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get raw (): Uint8Array {
|
|
66
|
+
if (this._raw == null) {
|
|
67
|
+
this._raw = privateKeyToPKIMessage(this.jwk)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return this._raw
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
equals (key?: any): boolean {
|
|
74
|
+
if (key == null || !(key.raw instanceof Uint8Array)) {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return uint8ArrayEquals(this.raw, key.raw)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async sign (message: Uint8Array | Uint8ArrayList): Promise<Uint8Array> {
|
|
82
|
+
return hashAndSign(this.jwk, message)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -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,227 @@
|
|
|
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
|
+
...P_256_KEY_JWK,
|
|
66
|
+
key_ops: ['verify'],
|
|
67
|
+
x,
|
|
68
|
+
y
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (privateKey.byteLength === P_384_KEY_LENGTH) {
|
|
73
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_384_KEY_LENGTH), 'base64url')
|
|
74
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_384_KEY_LENGTH), 'base64url')
|
|
75
|
+
|
|
76
|
+
return new ECDSAPrivateKeyClass({
|
|
77
|
+
...P_384_KEY_JWK,
|
|
78
|
+
key_ops: ['sign'],
|
|
79
|
+
d,
|
|
80
|
+
x,
|
|
81
|
+
y
|
|
82
|
+
}, {
|
|
83
|
+
...P_384_KEY_JWK,
|
|
84
|
+
key_ops: ['verify'],
|
|
85
|
+
x,
|
|
86
|
+
y
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (privateKey.byteLength === P_521_KEY_LENGTH) {
|
|
91
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_521_KEY_LENGTH), 'base64url')
|
|
92
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_521_KEY_LENGTH), 'base64url')
|
|
93
|
+
|
|
94
|
+
return new ECDSAPrivateKeyClass({
|
|
95
|
+
...P_521_KEY_JWK,
|
|
96
|
+
key_ops: ['sign'],
|
|
97
|
+
d,
|
|
98
|
+
x,
|
|
99
|
+
y
|
|
100
|
+
}, {
|
|
101
|
+
...P_521_KEY_JWK,
|
|
102
|
+
key_ops: ['verify'],
|
|
103
|
+
x,
|
|
104
|
+
y
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
throw new InvalidParametersError(`Private key length was wrong length, got ${privateKey.byteLength}, expected 32, 48 or 66`)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function unmarshalECDSAPublicKey (bytes: Uint8Array): ECDSAPublicKey {
|
|
112
|
+
const message = decodeDer(bytes)
|
|
113
|
+
|
|
114
|
+
return pkiMessageToECDSAPublicKey(message)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function pkiMessageToECDSAPublicKey (message: any): ECDSAPublicKey {
|
|
118
|
+
const coordinates = message[1][1][0]
|
|
119
|
+
const offset = 1
|
|
120
|
+
let x: string
|
|
121
|
+
let y: string
|
|
122
|
+
|
|
123
|
+
if (coordinates.byteLength === ((P_256_KEY_LENGTH * 2) + 1)) {
|
|
124
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_256_KEY_LENGTH), 'base64url')
|
|
125
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_256_KEY_LENGTH), 'base64url')
|
|
126
|
+
|
|
127
|
+
return new ECDSAPublicKeyClass({
|
|
128
|
+
...P_256_KEY_JWK,
|
|
129
|
+
key_ops: ['verify'],
|
|
130
|
+
x,
|
|
131
|
+
y
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (coordinates.byteLength === ((P_384_KEY_LENGTH * 2) + 1)) {
|
|
136
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_384_KEY_LENGTH), 'base64url')
|
|
137
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_384_KEY_LENGTH), 'base64url')
|
|
138
|
+
|
|
139
|
+
return new ECDSAPublicKeyClass({
|
|
140
|
+
...P_384_KEY_JWK,
|
|
141
|
+
key_ops: ['verify'],
|
|
142
|
+
x,
|
|
143
|
+
y
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (coordinates.byteLength === ((P_521_KEY_LENGTH * 2) + 1)) {
|
|
148
|
+
x = uint8ArrayToString(coordinates.subarray(offset, offset + P_521_KEY_LENGTH), 'base64url')
|
|
149
|
+
y = uint8ArrayToString(coordinates.subarray(offset + P_521_KEY_LENGTH), 'base64url')
|
|
150
|
+
|
|
151
|
+
return new ECDSAPublicKeyClass({
|
|
152
|
+
...P_521_KEY_JWK,
|
|
153
|
+
key_ops: ['verify'],
|
|
154
|
+
x,
|
|
155
|
+
y
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
throw new InvalidParametersError(`coordinates were wrong length, got ${coordinates.byteLength}, expected 65, 97 or 133`)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function privateKeyToPKIMessage (privateKey: JsonWebKey): Uint8Array {
|
|
163
|
+
return encodeSequence([
|
|
164
|
+
encodeInteger(Uint8Array.from([1])), // header
|
|
165
|
+
encodeOctetString(uint8ArrayFromString(privateKey.d ?? '', 'base64url')), // body
|
|
166
|
+
encodeSequence([ // PKIProtection
|
|
167
|
+
getOID(privateKey.crv)
|
|
168
|
+
], 0xA0),
|
|
169
|
+
encodeSequence([ // extraCerts
|
|
170
|
+
encodeBitString(
|
|
171
|
+
new Uint8ArrayList(
|
|
172
|
+
Uint8Array.from([0x04]),
|
|
173
|
+
uint8ArrayFromString(privateKey.x ?? '', 'base64url'),
|
|
174
|
+
uint8ArrayFromString(privateKey.y ?? '', 'base64url')
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
], 0xA1)
|
|
178
|
+
]).subarray()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function publicKeyToPKIMessage (publicKey: JsonWebKey): Uint8Array {
|
|
182
|
+
return encodeSequence([
|
|
183
|
+
encodeInteger(Uint8Array.from([1])), // header
|
|
184
|
+
encodeSequence([ // PKIProtection
|
|
185
|
+
getOID(publicKey.crv)
|
|
186
|
+
], 0xA0),
|
|
187
|
+
encodeSequence([ // extraCerts
|
|
188
|
+
encodeBitString(
|
|
189
|
+
new Uint8ArrayList(
|
|
190
|
+
Uint8Array.from([0x04]),
|
|
191
|
+
uint8ArrayFromString(publicKey.x ?? '', 'base64url'),
|
|
192
|
+
uint8ArrayFromString(publicKey.y ?? '', 'base64url')
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
], 0xA1)
|
|
196
|
+
]).subarray()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function getOID (curve?: string): Uint8Array {
|
|
200
|
+
if (curve === 'P-256') {
|
|
201
|
+
return OID_256
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (curve === 'P-384') {
|
|
205
|
+
return OID_384
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (curve === 'P-521') {
|
|
209
|
+
return OID_521
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
throw new InvalidParametersError(`Invalid curve ${curve}`)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export async function generateECDSAKeyPair (curve: Curve = 'P-256'): Promise<ECDSAPrivateKey> {
|
|
216
|
+
const key = await generateECDSAKey(curve)
|
|
217
|
+
|
|
218
|
+
return new ECDSAPrivateKeyClass(key.privateKey, key.publicKey)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function ensureECDSAKey (key: Uint8Array, length: number): Uint8Array {
|
|
222
|
+
key = Uint8Array.from(key ?? [])
|
|
223
|
+
if (key.length !== length) {
|
|
224
|
+
throw new InvalidParametersError(`Key must be a Uint8Array of length ${length}, got ${key.length}`)
|
|
225
|
+
}
|
|
226
|
+
return key
|
|
227
|
+
}
|
package/src/keys/index.ts
CHANGED
|
@@ -8,12 +8,19 @@
|
|
|
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 { ECDSA_P_256_OID, ECDSA_P_384_OID, ECDSA_P_521_OID } from './ecdsa/index.js'
|
|
13
|
+
import { generateECDSAKeyPair, pkiMessageToECDSAPrivateKey, pkiMessageToECDSAPublicKey, unmarshalECDSAPrivateKey, unmarshalECDSAPublicKey } from './ecdsa/utils.js'
|
|
14
|
+
import { privateKeyLength as ed25519PrivateKeyLength, publicKeyLength as ed25519PublicKeyLength } from './ed25519/index.js'
|
|
12
15
|
import { generateEd25519KeyPair, generateEd25519KeyPairFromSeed, unmarshalEd25519PrivateKey, unmarshalEd25519PublicKey } from './ed25519/utils.js'
|
|
13
16
|
import * as pb from './keys.js'
|
|
14
|
-
import {
|
|
17
|
+
import { decodeDer } from './rsa/der.js'
|
|
18
|
+
import { RSAES_PKCS1_V1_5_OID } from './rsa/index.js'
|
|
19
|
+
import { pkcs1ToRSAPrivateKey, pkixToRSAPublicKey, generateRSAKeyPair, pkcs1MessageToRSAPrivateKey, pkixMessageToRSAPublicKey } from './rsa/utils.js'
|
|
20
|
+
import { privateKeyLength as secp256k1PrivateKeyLength, publicKeyLength as secp256k1PublicKeyLength } from './secp256k1/index.js'
|
|
15
21
|
import { generateSecp256k1KeyPair, unmarshalSecp256k1PrivateKey, unmarshalSecp256k1PublicKey } from './secp256k1/utils.js'
|
|
16
|
-
import type {
|
|
22
|
+
import type { Curve } from './ecdsa/index.js'
|
|
23
|
+
import type { PrivateKey, PublicKey, KeyType, RSAPrivateKey, Secp256k1PrivateKey, Ed25519PrivateKey, Secp256k1PublicKey, Ed25519PublicKey, ECDSAPrivateKey, ECDSAPublicKey } from '@libp2p/interface'
|
|
17
24
|
import type { MultihashDigest } from 'multiformats'
|
|
18
25
|
import type { Digest } from 'multiformats/hashes/digest'
|
|
19
26
|
|
|
@@ -27,9 +34,10 @@ export { keyStretcher } from './key-stretcher.js'
|
|
|
27
34
|
*/
|
|
28
35
|
export async function generateKeyPair (type: 'Ed25519'): Promise<Ed25519PrivateKey>
|
|
29
36
|
export async function generateKeyPair (type: 'secp256k1'): Promise<Secp256k1PrivateKey>
|
|
37
|
+
export async function generateKeyPair (type: 'ECDSA', curve?: Curve): Promise<ECDSAPrivateKey>
|
|
30
38
|
export async function generateKeyPair (type: 'RSA', bits?: number): Promise<RSAPrivateKey>
|
|
31
39
|
export async function generateKeyPair (type: KeyType, bits?: number): Promise<PrivateKey>
|
|
32
|
-
export async function generateKeyPair (type: KeyType, bits?: number): Promise<unknown> {
|
|
40
|
+
export async function generateKeyPair (type: KeyType, bits?: number | string): Promise<unknown> {
|
|
33
41
|
if (type === 'Ed25519') {
|
|
34
42
|
return generateEd25519KeyPair()
|
|
35
43
|
}
|
|
@@ -39,7 +47,11 @@ export async function generateKeyPair (type: KeyType, bits?: number): Promise<un
|
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
if (type === 'RSA') {
|
|
42
|
-
return generateRSAKeyPair(bits
|
|
50
|
+
return generateRSAKeyPair(toBits(bits))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (type === 'ECDSA') {
|
|
54
|
+
return generateECDSAKeyPair(toCurve(bits))
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
throw new UnsupportedKeyTypeError()
|
|
@@ -81,6 +93,8 @@ export function publicKeyFromProtobuf (buf: Uint8Array, digest?: Digest<18, numb
|
|
|
81
93
|
return unmarshalEd25519PublicKey(data)
|
|
82
94
|
case pb.KeyType.secp256k1:
|
|
83
95
|
return unmarshalSecp256k1PublicKey(data)
|
|
96
|
+
case pb.KeyType.ECDSA:
|
|
97
|
+
return unmarshalECDSAPublicKey(data)
|
|
84
98
|
default:
|
|
85
99
|
throw new UnsupportedKeyTypeError()
|
|
86
100
|
}
|
|
@@ -90,13 +104,24 @@ export function publicKeyFromProtobuf (buf: Uint8Array, digest?: Digest<18, numb
|
|
|
90
104
|
* Creates a public key from the raw key bytes
|
|
91
105
|
*/
|
|
92
106
|
export function publicKeyFromRaw (buf: Uint8Array): PublicKey {
|
|
93
|
-
if (buf.byteLength ===
|
|
107
|
+
if (buf.byteLength === ed25519PublicKeyLength) {
|
|
94
108
|
return unmarshalEd25519PublicKey(buf)
|
|
95
|
-
} else if (buf.byteLength ===
|
|
109
|
+
} else if (buf.byteLength === secp256k1PublicKeyLength) {
|
|
96
110
|
return unmarshalSecp256k1PublicKey(buf)
|
|
97
|
-
} else {
|
|
98
|
-
return pkixToRSAPublicKey(buf)
|
|
99
111
|
}
|
|
112
|
+
|
|
113
|
+
const message = decodeDer(buf)
|
|
114
|
+
const ecdsaOid = message[1]?.[0]
|
|
115
|
+
|
|
116
|
+
if (ecdsaOid === ECDSA_P_256_OID || ecdsaOid === ECDSA_P_384_OID || ecdsaOid === ECDSA_P_521_OID) {
|
|
117
|
+
return pkiMessageToECDSAPublicKey(message)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (message[0]?.[0] === RSAES_PKCS1_V1_5_OID) {
|
|
121
|
+
return pkixMessageToRSAPublicKey(message, buf)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
throw new InvalidParametersError('Could not extract public key from raw bytes')
|
|
100
125
|
}
|
|
101
126
|
|
|
102
127
|
/**
|
|
@@ -106,7 +131,7 @@ export function publicKeyFromRaw (buf: Uint8Array): PublicKey {
|
|
|
106
131
|
* RSA keys are not supported as in practice we they are not stored in identity
|
|
107
132
|
* multihash since the hash would be very large.
|
|
108
133
|
*/
|
|
109
|
-
export function publicKeyFromMultihash (digest: MultihashDigest<0x0>): Ed25519PublicKey | Secp256k1PublicKey {
|
|
134
|
+
export function publicKeyFromMultihash (digest: MultihashDigest<0x0>): Ed25519PublicKey | Secp256k1PublicKey | ECDSAPublicKey {
|
|
110
135
|
const { Type, Data } = pb.PublicKey.decode(digest.digest)
|
|
111
136
|
const data = Data ?? new Uint8Array()
|
|
112
137
|
|
|
@@ -115,6 +140,8 @@ export function publicKeyFromMultihash (digest: MultihashDigest<0x0>): Ed25519Pu
|
|
|
115
140
|
return unmarshalEd25519PublicKey(data)
|
|
116
141
|
case pb.KeyType.secp256k1:
|
|
117
142
|
return unmarshalSecp256k1PublicKey(data)
|
|
143
|
+
case pb.KeyType.ECDSA:
|
|
144
|
+
return unmarshalECDSAPublicKey(data)
|
|
118
145
|
default:
|
|
119
146
|
throw new UnsupportedKeyTypeError()
|
|
120
147
|
}
|
|
@@ -133,7 +160,7 @@ export function publicKeyToProtobuf (key: PublicKey): Uint8Array {
|
|
|
133
160
|
/**
|
|
134
161
|
* Converts a protobuf serialized private key into its representative object
|
|
135
162
|
*/
|
|
136
|
-
export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Secp256k1PrivateKey | RSAPrivateKey {
|
|
163
|
+
export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Secp256k1PrivateKey | RSAPrivateKey | ECDSAPrivateKey {
|
|
137
164
|
const decoded = pb.PrivateKey.decode(buf)
|
|
138
165
|
const data = decoded.Data ?? new Uint8Array()
|
|
139
166
|
|
|
@@ -144,6 +171,8 @@ export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Se
|
|
|
144
171
|
return unmarshalEd25519PrivateKey(data)
|
|
145
172
|
case pb.KeyType.secp256k1:
|
|
146
173
|
return unmarshalSecp256k1PrivateKey(data)
|
|
174
|
+
case pb.KeyType.ECDSA:
|
|
175
|
+
return unmarshalECDSAPrivateKey(data)
|
|
147
176
|
default:
|
|
148
177
|
throw new UnsupportedKeyTypeError()
|
|
149
178
|
}
|
|
@@ -155,13 +184,24 @@ export function privateKeyFromProtobuf (buf: Uint8Array): Ed25519PrivateKey | Se
|
|
|
155
184
|
* differentiate between Ed25519 and secp256k1 keys as they are the same length.
|
|
156
185
|
*/
|
|
157
186
|
export function privateKeyFromRaw (buf: Uint8Array): PrivateKey {
|
|
158
|
-
if (buf.byteLength ===
|
|
187
|
+
if (buf.byteLength === ed25519PrivateKeyLength) {
|
|
159
188
|
return unmarshalEd25519PrivateKey(buf)
|
|
160
|
-
} else if (buf.byteLength ===
|
|
189
|
+
} else if (buf.byteLength === secp256k1PrivateKeyLength) {
|
|
161
190
|
return unmarshalSecp256k1PrivateKey(buf)
|
|
162
|
-
} else {
|
|
163
|
-
return pkcs1ToRSAPrivateKey(buf)
|
|
164
191
|
}
|
|
192
|
+
|
|
193
|
+
const message = decodeDer(buf)
|
|
194
|
+
const ecdsaOid = message[2]?.[0]
|
|
195
|
+
|
|
196
|
+
if (ecdsaOid === ECDSA_P_256_OID || ecdsaOid === ECDSA_P_384_OID || ecdsaOid === ECDSA_P_521_OID) {
|
|
197
|
+
return pkiMessageToECDSAPrivateKey(message)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (message.length > 8) {
|
|
201
|
+
return pkcs1MessageToRSAPrivateKey(message)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
throw new InvalidParametersError('Could not extract private key from raw bytes')
|
|
165
205
|
}
|
|
166
206
|
|
|
167
207
|
/**
|
|
@@ -173,3 +213,27 @@ export function privateKeyToProtobuf (key: PrivateKey): Uint8Array {
|
|
|
173
213
|
Data: key.raw
|
|
174
214
|
})
|
|
175
215
|
}
|
|
216
|
+
|
|
217
|
+
function toBits (bits: any): number {
|
|
218
|
+
if (bits == null) {
|
|
219
|
+
return 2048
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return parseInt(bits, 10)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function toCurve (curve: any): Curve {
|
|
226
|
+
if (curve === 'P-256' || curve == null) {
|
|
227
|
+
return 'P-256'
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (curve === 'P-384') {
|
|
231
|
+
return 'P-384'
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (curve === 'P-521') {
|
|
235
|
+
return 'P-521'
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
throw new InvalidParametersError('Unsupported curve, should be P-256, P-384 or P-521')
|
|
239
|
+
}
|
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 {
|
package/src/keys/rsa/der.ts
CHANGED
|
@@ -13,8 +13,11 @@ interface Decoder {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const decoders: Record<number, Decoder> = {
|
|
16
|
+
0x0: readSequence,
|
|
17
|
+
0x1: readSequence,
|
|
16
18
|
0x2: readInteger,
|
|
17
19
|
0x3: readBitString,
|
|
20
|
+
0x4: readOctetString,
|
|
18
21
|
0x5: readNull,
|
|
19
22
|
0x6: readObjectIdentifier,
|
|
20
23
|
0x10: readSequence,
|
|
@@ -96,13 +99,53 @@ function readInteger (buf: Uint8Array, context: Context): Uint8Array {
|
|
|
96
99
|
return Uint8Array.from(vals)
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
function readObjectIdentifier (buf: Uint8Array, context: Context): string
|
|
102
|
+
function readObjectIdentifier (buf: Uint8Array, context: Context): string {
|
|
100
103
|
const count = readLength(buf, context)
|
|
104
|
+
const finalOffset = context.offset + count
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
context.offset
|
|
106
|
+
const byte = buf[context.offset]
|
|
107
|
+
context.offset++
|
|
108
|
+
|
|
109
|
+
let val1 = 0
|
|
110
|
+
let val2 = 0
|
|
111
|
+
|
|
112
|
+
if (byte < 40) {
|
|
113
|
+
val1 = 0
|
|
114
|
+
val2 = byte
|
|
115
|
+
} else if (byte < 80) {
|
|
116
|
+
val1 = 1
|
|
117
|
+
val2 = byte - 40
|
|
118
|
+
} else {
|
|
119
|
+
val1 = 2
|
|
120
|
+
val2 = byte - 80
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let oid = `${val1}.${val2}`
|
|
124
|
+
let num: number[] = []
|
|
125
|
+
|
|
126
|
+
while (context.offset < finalOffset) {
|
|
127
|
+
const byte = buf[context.offset]
|
|
128
|
+
context.offset++
|
|
129
|
+
|
|
130
|
+
// remove msb
|
|
131
|
+
num.push(byte & 0b01111111)
|
|
132
|
+
|
|
133
|
+
if (byte < 128) {
|
|
134
|
+
num.reverse()
|
|
135
|
+
|
|
136
|
+
// reached the end of the encoding
|
|
137
|
+
let val = 0
|
|
104
138
|
|
|
105
|
-
|
|
139
|
+
for (let i = 0; i < num.length; i++) {
|
|
140
|
+
val += num[i] << (i * 7)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
oid += `.${val}`
|
|
144
|
+
num = []
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return oid
|
|
106
149
|
}
|
|
107
150
|
|
|
108
151
|
function readNull (buf: Uint8Array, context: Context): null {
|
|
@@ -115,7 +158,7 @@ function readBitString (buf: Uint8Array, context: Context): any {
|
|
|
115
158
|
const length = readLength(buf, context)
|
|
116
159
|
const unusedBits = buf[context.offset]
|
|
117
160
|
context.offset++
|
|
118
|
-
const bytes = buf.subarray(context.offset, context.offset + length)
|
|
161
|
+
const bytes = buf.subarray(context.offset, context.offset + length - 1)
|
|
119
162
|
context.offset += length
|
|
120
163
|
|
|
121
164
|
if (unusedBits !== 0) {
|
|
@@ -123,9 +166,15 @@ function readBitString (buf: Uint8Array, context: Context): any {
|
|
|
123
166
|
throw new Error('Unused bits in bit string is unimplemented')
|
|
124
167
|
}
|
|
125
168
|
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
|
|
169
|
+
return bytes
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function readOctetString (buf: Uint8Array, context: Context): any {
|
|
173
|
+
const length = readLength(buf, context)
|
|
174
|
+
const bytes = buf.subarray(context.offset, context.offset + length)
|
|
175
|
+
context.offset += length
|
|
176
|
+
|
|
177
|
+
return bytes
|
|
129
178
|
}
|
|
130
179
|
|
|
131
180
|
function encodeNumber (value: number): Uint8ArrayList {
|
|
@@ -163,7 +212,7 @@ function encodeLength (bytes: { byteLength: number }): Uint8Array | Uint8ArrayLi
|
|
|
163
212
|
export function encodeInteger (value: Uint8Array | Uint8ArrayList): Uint8ArrayList {
|
|
164
213
|
const contents = new Uint8ArrayList()
|
|
165
214
|
|
|
166
|
-
const mask =
|
|
215
|
+
const mask = 0b10000000
|
|
167
216
|
const positive = (value.subarray()[0] & mask) === mask
|
|
168
217
|
|
|
169
218
|
if (positive) {
|
|
@@ -195,7 +244,15 @@ export function encodeBitString (value: Uint8Array | Uint8ArrayList): Uint8Array
|
|
|
195
244
|
)
|
|
196
245
|
}
|
|
197
246
|
|
|
198
|
-
export function
|
|
247
|
+
export function encodeOctetString (value: Uint8Array | Uint8ArrayList): Uint8ArrayList {
|
|
248
|
+
return new Uint8ArrayList(
|
|
249
|
+
Uint8Array.from([0x04]),
|
|
250
|
+
encodeLength(value),
|
|
251
|
+
value
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function encodeSequence (values: Array<Uint8Array | Uint8ArrayList>, tag = 0x30): Uint8ArrayList {
|
|
199
256
|
const output = new Uint8ArrayList()
|
|
200
257
|
|
|
201
258
|
for (const buf of values) {
|
|
@@ -205,7 +262,7 @@ export function encodeSequence (values: Array<Uint8Array | Uint8ArrayList>): Uin
|
|
|
205
262
|
}
|
|
206
263
|
|
|
207
264
|
return new Uint8ArrayList(
|
|
208
|
-
Uint8Array.from([
|
|
265
|
+
Uint8Array.from([tag]),
|
|
209
266
|
encodeLength(output),
|
|
210
267
|
output
|
|
211
268
|
)
|