@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.
Files changed (63) hide show
  1. package/dist/index.min.js +1 -1
  2. package/dist/src/keys/ecdsa/ecdsa.d.ts +27 -0
  3. package/dist/src/keys/ecdsa/ecdsa.d.ts.map +1 -0
  4. package/dist/src/keys/ecdsa/ecdsa.js +65 -0
  5. package/dist/src/keys/ecdsa/ecdsa.js.map +1 -0
  6. package/dist/src/keys/ecdsa/index.d.ts +10 -0
  7. package/dist/src/keys/ecdsa/index.d.ts.map +1 -0
  8. package/dist/src/keys/ecdsa/index.js +39 -0
  9. package/dist/src/keys/ecdsa/index.js.map +1 -0
  10. package/dist/src/keys/ecdsa/utils.d.ts +11 -0
  11. package/dist/src/keys/ecdsa/utils.d.ts.map +1 -0
  12. package/dist/src/keys/ecdsa/utils.js +180 -0
  13. package/dist/src/keys/ecdsa/utils.js.map +1 -0
  14. package/dist/src/keys/index.d.ts +5 -3
  15. package/dist/src/keys/index.d.ts.map +1 -1
  16. package/dist/src/keys/index.js +56 -11
  17. package/dist/src/keys/index.js.map +1 -1
  18. package/dist/src/keys/keys.d.ts +2 -1
  19. package/dist/src/keys/keys.d.ts.map +1 -1
  20. package/dist/src/keys/keys.js +2 -0
  21. package/dist/src/keys/keys.js.map +1 -1
  22. package/dist/src/keys/rsa/der.d.ts +2 -1
  23. package/dist/src/keys/rsa/der.d.ts.map +1 -1
  24. package/dist/src/keys/rsa/der.js +53 -10
  25. package/dist/src/keys/rsa/der.js.map +1 -1
  26. package/dist/src/keys/rsa/index.browser.d.ts +1 -0
  27. package/dist/src/keys/rsa/index.browser.d.ts.map +1 -1
  28. package/dist/src/keys/rsa/index.browser.js +1 -0
  29. package/dist/src/keys/rsa/index.browser.js.map +1 -1
  30. package/dist/src/keys/rsa/index.d.ts +1 -0
  31. package/dist/src/keys/rsa/index.d.ts.map +1 -1
  32. package/dist/src/keys/rsa/index.js +1 -0
  33. package/dist/src/keys/rsa/index.js.map +1 -1
  34. package/dist/src/keys/rsa/rsa.d.ts +4 -4
  35. package/dist/src/keys/rsa/rsa.d.ts.map +1 -1
  36. package/dist/src/keys/rsa/rsa.js +10 -10
  37. package/dist/src/keys/rsa/rsa.js.map +1 -1
  38. package/dist/src/keys/rsa/utils.d.ts +12 -2
  39. package/dist/src/keys/rsa/utils.d.ts.map +1 -1
  40. package/dist/src/keys/rsa/utils.js +41 -16
  41. package/dist/src/keys/rsa/utils.js.map +1 -1
  42. package/dist/src/keys/secp256k1/index.browser.d.ts +4 -0
  43. package/dist/src/keys/secp256k1/index.browser.d.ts.map +1 -1
  44. package/dist/src/keys/secp256k1/index.browser.js +4 -0
  45. package/dist/src/keys/secp256k1/index.browser.js.map +1 -1
  46. package/dist/src/keys/secp256k1/index.d.ts +4 -0
  47. package/dist/src/keys/secp256k1/index.d.ts.map +1 -1
  48. package/dist/src/keys/secp256k1/index.js +4 -0
  49. package/dist/src/keys/secp256k1/index.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/keys/ecdsa/ecdsa.ts +84 -0
  52. package/src/keys/ecdsa/index.ts +50 -0
  53. package/src/keys/ecdsa/utils.ts +227 -0
  54. package/src/keys/index.ts +79 -15
  55. package/src/keys/keys.proto +1 -0
  56. package/src/keys/keys.ts +4 -2
  57. package/src/keys/rsa/der.ts +68 -11
  58. package/src/keys/rsa/index.browser.ts +1 -0
  59. package/src/keys/rsa/index.ts +2 -0
  60. package/src/keys/rsa/rsa.ts +10 -10
  61. package/src/keys/rsa/utils.ts +48 -16
  62. package/src/keys/secp256k1/index.browser.ts +6 -0
  63. 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 { pkcs1ToRSAPrivateKey, pkixToRSAPublicKey, generateRSAKeyPair } from './rsa/utils.js'
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 { PrivateKey, PublicKey, KeyType, RSAPrivateKey, Secp256k1PrivateKey, Ed25519PrivateKey, Secp256k1PublicKey, Ed25519PublicKey } from '@libp2p/interface'
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 ?? 2048)
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 === 32) {
107
+ if (buf.byteLength === ed25519PublicKeyLength) {
94
108
  return unmarshalEd25519PublicKey(buf)
95
- } else if (buf.byteLength === 33) {
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 === 64) {
187
+ if (buf.byteLength === ed25519PrivateKeyLength) {
159
188
  return unmarshalEd25519PrivateKey(buf)
160
- } else if (buf.byteLength === 32) {
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
+ }
@@ -4,6 +4,7 @@ enum KeyType {
4
4
  RSA = 0;
5
5
  Ed25519 = 1;
6
6
  secp256k1 = 2;
7
+ ECDSA = 3;
7
8
  }
8
9
  message PublicKey {
9
10
  // the proto2 version of this field is "required" which means it will have
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 {
@@ -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
- // skip OID
103
- context.offset += count
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
- return ['oid-unimplemented']
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 decodeDer(bytes, {
127
- offset: 0
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 = parseInt('10000000', 2)
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 encodeSequence (values: Array<Uint8Array | Uint8ArrayList>): Uint8ArrayList {
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([0x30]),
265
+ Uint8Array.from([tag]),
209
266
  encodeLength(output),
210
267
  output
211
268
  )