iso-signatures 0.0.1
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/src/asn1.d.ts +31 -0
- package/dist/src/asn1.d.ts.map +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/spki.d.ts +14 -0
- package/dist/src/spki.d.ts.map +1 -0
- package/dist/src/types.d.ts +63 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/utils.d.ts +12 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/package.json +138 -0
- package/readme.md +59 -0
- package/src/asn1.js +329 -0
- package/src/index.js +1 -0
- package/src/signers/ecdsa.js +147 -0
- package/src/signers/eddsa.js +80 -0
- package/src/signers/rsa-old.js +128 -0
- package/src/signers/rsa.js +133 -0
- package/src/spki.js +39 -0
- package/src/types.ts +75 -0
- package/src/utils.js +35 -0
- package/src/verifiers/ecdsa.js +56 -0
- package/src/verifiers/eddsa.js +12 -0
- package/src/verifiers/resolver.js +63 -0
- package/src/verifiers/rsa-old.js +25 -0
- package/src/verifiers/rsa.js +26 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/* eslint-disable unicorn/numeric-separators-style */
|
|
2
|
+
import { webcrypto } from 'iso-base/crypto'
|
|
3
|
+
import { u8 } from 'iso-base/utils'
|
|
4
|
+
import { DIDKey, keyTypeToAlg } from 'iso-did/key'
|
|
5
|
+
import { createEcdsaParams } from '../utils.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('../types.js').ISigner<CryptoKeyPair>} ISigner
|
|
9
|
+
* @typedef {Extract<import('iso-did/types').SignatureAlgorithm, 'ES256' | 'ES384' | 'ES512'>} ECDSAAlg
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} alg
|
|
14
|
+
*/
|
|
15
|
+
function asECDSAAlg(alg) {
|
|
16
|
+
if (alg.startsWith('ES')) {
|
|
17
|
+
return /** @type {ECDSAAlg} */ (alg)
|
|
18
|
+
}
|
|
19
|
+
throw new TypeError(`Unsupported algorithm ${alg}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ECDSA signer
|
|
24
|
+
*
|
|
25
|
+
* @implements {ISigner}
|
|
26
|
+
*/
|
|
27
|
+
export class ECDSASigner {
|
|
28
|
+
/**
|
|
29
|
+
* @param {ECDSAAlg} alg
|
|
30
|
+
* @param {Uint8Array} publicKey
|
|
31
|
+
* @param {CryptoKeyPair} keypair
|
|
32
|
+
*/
|
|
33
|
+
constructor(alg, publicKey, keypair) {
|
|
34
|
+
this.params = createEcdsaParams(alg)
|
|
35
|
+
this.keypair = keypair
|
|
36
|
+
this.publicKey = publicKey
|
|
37
|
+
this.alg = alg
|
|
38
|
+
this.type = this.params.namedCurve
|
|
39
|
+
this.did = DIDKey.fromPublicKey(this.type, this.publicKey)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate a new signer
|
|
44
|
+
*
|
|
45
|
+
* @param {ECDSAAlg} alg
|
|
46
|
+
*/
|
|
47
|
+
static async generate(alg) {
|
|
48
|
+
const params = createEcdsaParams(alg)
|
|
49
|
+
const cryptoKeyPair = await webcrypto.subtle.generateKey(params, false, [
|
|
50
|
+
'sign',
|
|
51
|
+
'verify',
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
const publicKey = await webcrypto.subtle.exportKey(
|
|
55
|
+
'raw',
|
|
56
|
+
cryptoKeyPair.publicKey
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return new ECDSASigner(alg, u8(publicKey), cryptoKeyPair)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Import a signer from a JWK
|
|
64
|
+
*
|
|
65
|
+
* @param {import('../types.js').PrivateKeyJwk} jwk
|
|
66
|
+
*/
|
|
67
|
+
static async importJwk(jwk) {
|
|
68
|
+
const alg = asECDSAAlg(keyTypeToAlg(jwk.crv))
|
|
69
|
+
const params = createEcdsaParams(alg)
|
|
70
|
+
const privateKey = await webcrypto.subtle.importKey(
|
|
71
|
+
'jwk',
|
|
72
|
+
jwk,
|
|
73
|
+
params,
|
|
74
|
+
false,
|
|
75
|
+
['sign']
|
|
76
|
+
)
|
|
77
|
+
const publicKeyJWK = await webcrypto.subtle.importKey(
|
|
78
|
+
'jwk',
|
|
79
|
+
{ ...jwk, d: undefined },
|
|
80
|
+
params,
|
|
81
|
+
true,
|
|
82
|
+
['verify']
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const publicKey = await webcrypto.subtle.exportKey('raw', publicKeyJWK)
|
|
86
|
+
|
|
87
|
+
return new ECDSASigner(alg, u8(publicKey), {
|
|
88
|
+
privateKey,
|
|
89
|
+
publicKey: publicKeyJWK,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Import a signer from a CryptoKeyPair
|
|
95
|
+
*
|
|
96
|
+
* @param {CryptoKeyPair} key
|
|
97
|
+
*/
|
|
98
|
+
static async import(key) {
|
|
99
|
+
const publicKey = await webcrypto.subtle.exportKey('raw', key.publicKey)
|
|
100
|
+
|
|
101
|
+
/** @type { ECDSAAlg } */
|
|
102
|
+
let alg
|
|
103
|
+
switch (publicKey.byteLength) {
|
|
104
|
+
case 65: {
|
|
105
|
+
alg = 'ES256'
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
case 97: {
|
|
109
|
+
alg = 'ES384'
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
case 133: {
|
|
113
|
+
alg = 'ES512'
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
default: {
|
|
118
|
+
throw new Error('Unsupported key type')
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return new ECDSASigner(alg, u8(publicKey), key)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Sign a message
|
|
126
|
+
*
|
|
127
|
+
* @param {Uint8Array} message
|
|
128
|
+
*/
|
|
129
|
+
async sign(message) {
|
|
130
|
+
const buf = await webcrypto.subtle.sign(
|
|
131
|
+
this.params,
|
|
132
|
+
this.keypair.privateKey,
|
|
133
|
+
message
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return u8(buf)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Export the signer as a CryptoKeyPair
|
|
141
|
+
*
|
|
142
|
+
* @returns {CryptoKeyPair}
|
|
143
|
+
*/
|
|
144
|
+
export() {
|
|
145
|
+
return this.keypair
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* eslint-disable unicorn/numeric-separators-style */
|
|
2
|
+
import { getPublicKeyAsync, signAsync, utils } from '@noble/ed25519'
|
|
3
|
+
import { webcrypto } from 'iso-base/crypto'
|
|
4
|
+
import { base64pad } from 'iso-base/rfc4648'
|
|
5
|
+
import { tag, untag } from 'iso-base/varint'
|
|
6
|
+
import { DIDKey } from 'iso-did/key'
|
|
7
|
+
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
if (!globalThis.crypto) globalThis.crypto = webcrypto
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {import('../types.js').ISigner<string>} ISigner
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* EdDSA signer
|
|
17
|
+
*
|
|
18
|
+
* @implements {ISigner}
|
|
19
|
+
*/
|
|
20
|
+
export class EdDSASigner {
|
|
21
|
+
/** @type {Extract<import('iso-did/types').SignatureAlgorithm, "EdDSA">} */
|
|
22
|
+
static alg = 'EdDSA'
|
|
23
|
+
|
|
24
|
+
/** @type {Extract<import('iso-did/types').KeyType, "Ed25519">} */
|
|
25
|
+
static type = 'Ed25519'
|
|
26
|
+
|
|
27
|
+
static code = 0x1300
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {Uint8Array} publicKey
|
|
31
|
+
* @param {Uint8Array} privateKey
|
|
32
|
+
*/
|
|
33
|
+
constructor(publicKey, privateKey) {
|
|
34
|
+
this.privateKey = privateKey
|
|
35
|
+
this.publicKey = publicKey
|
|
36
|
+
this.alg = EdDSASigner.alg
|
|
37
|
+
this.type = EdDSASigner.type
|
|
38
|
+
this.code = EdDSASigner.code
|
|
39
|
+
this.did = DIDKey.fromPublicKey(EdDSASigner.type, this.publicKey)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Generate a new signer
|
|
44
|
+
*
|
|
45
|
+
* @param {Uint8Array} [bytes]
|
|
46
|
+
*/
|
|
47
|
+
static async generate(bytes) {
|
|
48
|
+
const privateKey = bytes || utils.randomPrivateKey()
|
|
49
|
+
const publicKey = await getPublicKeyAsync(privateKey)
|
|
50
|
+
return new EdDSASigner(publicKey, privateKey)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Import a signer from a encoded string
|
|
55
|
+
*
|
|
56
|
+
* @param {string} encoded
|
|
57
|
+
*/
|
|
58
|
+
static async import(encoded) {
|
|
59
|
+
const privateKey = untag(EdDSASigner.code, base64pad.decode(encoded))
|
|
60
|
+
const publicKey = await getPublicKeyAsync(privateKey)
|
|
61
|
+
|
|
62
|
+
return new EdDSASigner(publicKey, privateKey)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Sign a message
|
|
67
|
+
*
|
|
68
|
+
* @param {Uint8Array} message
|
|
69
|
+
*/
|
|
70
|
+
async sign(message) {
|
|
71
|
+
return signAsync(message, this.privateKey)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Export the signer as a encoded string
|
|
76
|
+
*/
|
|
77
|
+
export() {
|
|
78
|
+
return base64pad.encode(tag(EdDSASigner.code, this.privateKey))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* eslint-disable unicorn/numeric-separators-style */
|
|
2
|
+
import { webcrypto } from 'iso-base/crypto'
|
|
3
|
+
import { u8 } from 'iso-base/utils'
|
|
4
|
+
import { DIDKey } from 'iso-did/key'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('../types.js').ISigner<CryptoKeyPair>} ISigner
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* RSA Old fission format signer
|
|
12
|
+
*
|
|
13
|
+
* @implements {ISigner}
|
|
14
|
+
*/
|
|
15
|
+
export class RSAOldSigner {
|
|
16
|
+
/** @type {Extract<import('iso-did/types').SignatureAlgorithm, "RS256_OLD">} */
|
|
17
|
+
static alg = 'RS256_OLD'
|
|
18
|
+
|
|
19
|
+
/** @type {Extract<import('iso-did/types').KeyType, "RSA_OLD">} */
|
|
20
|
+
static type = 'RSA_OLD'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Uint8Array} publicKey
|
|
24
|
+
* @param {CryptoKeyPair} keypair
|
|
25
|
+
*/
|
|
26
|
+
constructor(publicKey, keypair) {
|
|
27
|
+
this.keypair = keypair
|
|
28
|
+
this.publicKey = publicKey
|
|
29
|
+
this.alg = RSAOldSigner.alg
|
|
30
|
+
this.type = RSAOldSigner.type
|
|
31
|
+
this.did = DIDKey.fromPublicKey(RSAOldSigner.type, this.publicKey)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate a new signer
|
|
36
|
+
*/
|
|
37
|
+
static async generate() {
|
|
38
|
+
const cryptoKeyPair = await webcrypto.subtle.generateKey(
|
|
39
|
+
{
|
|
40
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
41
|
+
modulusLength: 2048,
|
|
42
|
+
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
|
43
|
+
hash: { name: 'SHA-256' },
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
false,
|
|
47
|
+
['sign', 'verify']
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const publicKey = await webcrypto.subtle.exportKey(
|
|
51
|
+
'spki',
|
|
52
|
+
cryptoKeyPair.publicKey
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return new RSAOldSigner(u8(publicKey), cryptoKeyPair)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Import a signer from a JWK
|
|
60
|
+
*
|
|
61
|
+
* @param {import('../types.js').PrivateKeyRSAJwk} jwk
|
|
62
|
+
*/
|
|
63
|
+
static async importJwk(jwk) {
|
|
64
|
+
const privateKey = await webcrypto.subtle.importKey(
|
|
65
|
+
'jwk',
|
|
66
|
+
jwk,
|
|
67
|
+
{
|
|
68
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
69
|
+
hash: 'SHA-256',
|
|
70
|
+
},
|
|
71
|
+
false,
|
|
72
|
+
['sign']
|
|
73
|
+
)
|
|
74
|
+
const publicKeyJWK = await webcrypto.subtle.importKey(
|
|
75
|
+
'jwk',
|
|
76
|
+
{ ...jwk, d: undefined },
|
|
77
|
+
{
|
|
78
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
79
|
+
hash: 'SHA-256',
|
|
80
|
+
},
|
|
81
|
+
true,
|
|
82
|
+
['verify']
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const publicKey = await webcrypto.subtle.exportKey('spki', publicKeyJWK)
|
|
86
|
+
|
|
87
|
+
return new RSAOldSigner(u8(publicKey), {
|
|
88
|
+
privateKey,
|
|
89
|
+
publicKey: publicKeyJWK,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Import a signer from a keypair
|
|
95
|
+
*
|
|
96
|
+
* @param {CryptoKeyPair} keypair
|
|
97
|
+
*/
|
|
98
|
+
static async import(keypair) {
|
|
99
|
+
const publicKey = await webcrypto.subtle.exportKey(
|
|
100
|
+
'spki',
|
|
101
|
+
keypair.publicKey
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return new RSAOldSigner(u8(publicKey), keypair)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Sign a message
|
|
109
|
+
*
|
|
110
|
+
* @param {Uint8Array} message
|
|
111
|
+
*/
|
|
112
|
+
async sign(message) {
|
|
113
|
+
const buf = await webcrypto.subtle.sign(
|
|
114
|
+
{ name: 'RSASSA-PKCS1-v1_5', saltLength: 128 },
|
|
115
|
+
this.keypair.privateKey,
|
|
116
|
+
message
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return u8(buf)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Export the signer as a crypto key pair
|
|
124
|
+
*/
|
|
125
|
+
export() {
|
|
126
|
+
return this.keypair
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/* eslint-disable unicorn/numeric-separators-style */
|
|
2
|
+
import { webcrypto } from 'iso-base/crypto'
|
|
3
|
+
import { u8 } from 'iso-base/utils'
|
|
4
|
+
import { DIDKey } from 'iso-did/key'
|
|
5
|
+
import { spki } from '../spki.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('../types.js').ISigner<CryptoKeyPair>} ISigner
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* RSA signer
|
|
13
|
+
*
|
|
14
|
+
* @implements {ISigner}
|
|
15
|
+
*/
|
|
16
|
+
export class RSASigner {
|
|
17
|
+
/** @type {Extract<import('iso-did/types').SignatureAlgorithm, "RS256">} */
|
|
18
|
+
static alg = 'RS256'
|
|
19
|
+
|
|
20
|
+
/** @type {Extract<import('iso-did/types').KeyType, "RSA">} */
|
|
21
|
+
static type = 'RSA'
|
|
22
|
+
|
|
23
|
+
// multicodec code for RSA private key
|
|
24
|
+
static code = 0x1305
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {Uint8Array} publicKey
|
|
28
|
+
* @param {CryptoKeyPair} keypair
|
|
29
|
+
*/
|
|
30
|
+
constructor(publicKey, keypair) {
|
|
31
|
+
this.keypair = keypair
|
|
32
|
+
this.publicKey = publicKey
|
|
33
|
+
this.alg = RSASigner.alg
|
|
34
|
+
this.type = RSASigner.type
|
|
35
|
+
this.code = RSASigner.code
|
|
36
|
+
this.did = DIDKey.fromPublicKey(RSASigner.type, this.publicKey)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate a new signer
|
|
41
|
+
*/
|
|
42
|
+
static async generate() {
|
|
43
|
+
const cryptoKeyPair = await webcrypto.subtle.generateKey(
|
|
44
|
+
{
|
|
45
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
46
|
+
modulusLength: 2048,
|
|
47
|
+
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
|
48
|
+
hash: { name: 'SHA-256' },
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
false,
|
|
52
|
+
['sign', 'verify']
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const publicKey = await webcrypto.subtle.exportKey(
|
|
56
|
+
'spki',
|
|
57
|
+
cryptoKeyPair.publicKey
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return new RSASigner(spki.decode(u8(publicKey)), cryptoKeyPair)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Import a signer from a JWK
|
|
65
|
+
*
|
|
66
|
+
* @param {import('../types.js').PrivateKeyRSAJwk} jwk
|
|
67
|
+
*/
|
|
68
|
+
static async importJwk(jwk) {
|
|
69
|
+
const privateKey = await webcrypto.subtle.importKey(
|
|
70
|
+
'jwk',
|
|
71
|
+
jwk,
|
|
72
|
+
{
|
|
73
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
74
|
+
hash: 'SHA-256',
|
|
75
|
+
},
|
|
76
|
+
false,
|
|
77
|
+
['sign']
|
|
78
|
+
)
|
|
79
|
+
const publicKeyJWK = await webcrypto.subtle.importKey(
|
|
80
|
+
'jwk',
|
|
81
|
+
{ ...jwk, d: undefined },
|
|
82
|
+
{
|
|
83
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
84
|
+
hash: 'SHA-256',
|
|
85
|
+
},
|
|
86
|
+
true,
|
|
87
|
+
['verify']
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
const publicKey = await webcrypto.subtle.exportKey('spki', publicKeyJWK)
|
|
91
|
+
|
|
92
|
+
return new RSASigner(spki.decode(u8(publicKey)), {
|
|
93
|
+
privateKey,
|
|
94
|
+
publicKey: publicKeyJWK,
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Import a signer from a keypair
|
|
100
|
+
*
|
|
101
|
+
* @param {CryptoKeyPair} keypair
|
|
102
|
+
*/
|
|
103
|
+
static async import(keypair) {
|
|
104
|
+
const publicKey = await webcrypto.subtle.exportKey(
|
|
105
|
+
'spki',
|
|
106
|
+
keypair.publicKey
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return new RSASigner(spki.decode(u8(publicKey)), keypair)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Sign a message
|
|
114
|
+
*
|
|
115
|
+
* @param {Uint8Array} message
|
|
116
|
+
*/
|
|
117
|
+
async sign(message) {
|
|
118
|
+
const buf = await webcrypto.subtle.sign(
|
|
119
|
+
{ name: 'RSASSA-PKCS1-v1_5', saltLength: 128 },
|
|
120
|
+
this.keypair.privateKey,
|
|
121
|
+
message
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return u8(buf)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Export the signer as a crypto key pair
|
|
129
|
+
*/
|
|
130
|
+
export() {
|
|
131
|
+
return this.keypair
|
|
132
|
+
}
|
|
133
|
+
}
|
package/src/spki.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeBitString,
|
|
3
|
+
encodeSequence,
|
|
4
|
+
enterSequence,
|
|
5
|
+
readBitString,
|
|
6
|
+
skipSequence,
|
|
7
|
+
} from './asn1.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* SPKI
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const SPKI_PARAMS_ENCODED = new Uint8Array([
|
|
14
|
+
48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0,
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {Uint8Array} key
|
|
19
|
+
*/
|
|
20
|
+
function encode(key) {
|
|
21
|
+
return encodeSequence([SPKI_PARAMS_ENCODED, encodeBitString(key)])
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Uint8Array} info
|
|
26
|
+
*/
|
|
27
|
+
function decode(info) {
|
|
28
|
+
// go into the top-level SEQUENCE
|
|
29
|
+
const offset = enterSequence(info, 0)
|
|
30
|
+
// skip the header we expect (SKPI_PARAMS_ENCODED)
|
|
31
|
+
const keyOffset = skipSequence(info, offset)
|
|
32
|
+
|
|
33
|
+
// we expect the bitstring next
|
|
34
|
+
return readBitString(info, keyOffset)
|
|
35
|
+
}
|
|
36
|
+
export const spki = {
|
|
37
|
+
encode,
|
|
38
|
+
decode,
|
|
39
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { DIDKey } from 'iso-did/key'
|
|
2
|
+
import type { SignatureAlgorithm, KeyType } from 'iso-did/types'
|
|
3
|
+
|
|
4
|
+
export interface ISigner<Export extends CryptoKeyPair | string> {
|
|
5
|
+
/**
|
|
6
|
+
* JWT signing algorithm
|
|
7
|
+
*/
|
|
8
|
+
alg: SignatureAlgorithm
|
|
9
|
+
/**
|
|
10
|
+
* Keypair type
|
|
11
|
+
*/
|
|
12
|
+
type: KeyType
|
|
13
|
+
/**
|
|
14
|
+
* Multicodec identifier for the private key
|
|
15
|
+
*/
|
|
16
|
+
code?: number
|
|
17
|
+
|
|
18
|
+
did: DIDKey
|
|
19
|
+
/**
|
|
20
|
+
* Sign a message
|
|
21
|
+
*/
|
|
22
|
+
sign: (message: Uint8Array) => Promise<Uint8Array>
|
|
23
|
+
|
|
24
|
+
export: () => Export
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface VerifyInput {
|
|
28
|
+
signature: Uint8Array
|
|
29
|
+
message: Uint8Array
|
|
30
|
+
publicKey: Uint8Array
|
|
31
|
+
}
|
|
32
|
+
export type Verify = (input: VerifyInput) => Promise<boolean>
|
|
33
|
+
|
|
34
|
+
export type Verifier<T extends SignatureAlgorithm> = Record<T, Verify>
|
|
35
|
+
|
|
36
|
+
export type VerifierRegistry<T extends SignatureAlgorithm> = Partial<
|
|
37
|
+
Verifier<T>
|
|
38
|
+
>
|
|
39
|
+
|
|
40
|
+
export interface ResolverVerifyInput extends VerifyInput {
|
|
41
|
+
alg: SignatureAlgorithm
|
|
42
|
+
}
|
|
43
|
+
export type Cache = (parsed: VerifyInput, verify: Verify) => Promise<boolean>
|
|
44
|
+
|
|
45
|
+
export interface IResolver {
|
|
46
|
+
verify: (input: ResolverVerifyInput) => Promise<boolean>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ResolverOptions {
|
|
50
|
+
cache?: Cache | boolean | undefined
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface PublicKeyJwk {
|
|
54
|
+
kty: string
|
|
55
|
+
crv: string
|
|
56
|
+
x: string
|
|
57
|
+
y: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface PublicKeyRSAJwk {
|
|
61
|
+
kty: string
|
|
62
|
+
n: string
|
|
63
|
+
e: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface PrivateKeyRSAJwk extends PublicKeyRSAJwk {
|
|
67
|
+
d: string
|
|
68
|
+
p: string
|
|
69
|
+
q: string
|
|
70
|
+
dp: string
|
|
71
|
+
dq: string
|
|
72
|
+
qi: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type PrivateKeyJwk = PublicKeyJwk & { d: string }
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create web crypto params for ECDSA.
|
|
3
|
+
*
|
|
4
|
+
* @param {import("iso-did/types").SignatureAlgorithm} alg
|
|
5
|
+
* @returns {{name: 'ECDSA', namedCurve: 'P-256' | 'P-384' | 'P-521', hash: 'SHA-256' | 'SHA-384' | 'SHA-512'}}
|
|
6
|
+
*/
|
|
7
|
+
export function createEcdsaParams(alg) {
|
|
8
|
+
switch (alg) {
|
|
9
|
+
case 'ES256': {
|
|
10
|
+
return {
|
|
11
|
+
name: 'ECDSA',
|
|
12
|
+
namedCurve: 'P-256',
|
|
13
|
+
hash: 'SHA-256',
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
case 'ES384': {
|
|
17
|
+
return {
|
|
18
|
+
name: 'ECDSA',
|
|
19
|
+
namedCurve: 'P-384',
|
|
20
|
+
hash: 'SHA-384',
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
case 'ES512': {
|
|
24
|
+
return {
|
|
25
|
+
name: 'ECDSA',
|
|
26
|
+
namedCurve: 'P-521',
|
|
27
|
+
hash: 'SHA-512',
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
default: {
|
|
32
|
+
throw new TypeError(`Unsupported algorithm ${alg}`)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* eslint-disable unicorn/numeric-separators-style */
|
|
2
|
+
import { webcrypto } from 'iso-base/crypto'
|
|
3
|
+
import { decompress, isCompressed } from 'iso-base/ec-compression'
|
|
4
|
+
import { createEcdsaParams } from '../utils.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {import('iso-did/types').SignatureAlgorithm} type
|
|
9
|
+
*/
|
|
10
|
+
function createVerifier(type) {
|
|
11
|
+
const params = createEcdsaParams(type)
|
|
12
|
+
|
|
13
|
+
/** @type {import('../types.js').Verify} */
|
|
14
|
+
async function fn({ signature, message, publicKey }) {
|
|
15
|
+
if (isCompressed(publicKey)) {
|
|
16
|
+
publicKey = decompress(publicKey, params.namedCurve)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const key = await webcrypto.subtle.importKey(
|
|
20
|
+
'raw',
|
|
21
|
+
publicKey,
|
|
22
|
+
{ name: params.name, namedCurve: params.namedCurve },
|
|
23
|
+
true,
|
|
24
|
+
['verify']
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return await webcrypto.subtle.verify(
|
|
28
|
+
{
|
|
29
|
+
name: params.name,
|
|
30
|
+
hash: { name: params.hash },
|
|
31
|
+
},
|
|
32
|
+
key,
|
|
33
|
+
signature,
|
|
34
|
+
message
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return fn
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @type {import('../types.js').Verifier<'ES256' | 'ES384' | 'ES512'>} */
|
|
42
|
+
export const verifier = {
|
|
43
|
+
ES256: createVerifier('ES256'),
|
|
44
|
+
ES384: createVerifier('ES384'),
|
|
45
|
+
ES512: createVerifier('ES512'),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {'ES256' | 'ES384' | 'ES512'} type
|
|
51
|
+
* @param {import('../types.js').VerifyInput} param1
|
|
52
|
+
* @returns
|
|
53
|
+
*/
|
|
54
|
+
export async function verify(type, { signature, message, publicKey }) {
|
|
55
|
+
return await verifier[type]({ signature, message, publicKey })
|
|
56
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* eslint-disable unicorn/numeric-separators-style */
|
|
2
|
+
import { verifyAsync } from '@noble/ed25519'
|
|
3
|
+
|
|
4
|
+
/** @type {import('../types').Verify} */
|
|
5
|
+
export function verify({ signature, message, publicKey }) {
|
|
6
|
+
return verifyAsync(signature, message, publicKey)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** @type {import('../types').Verifier<'EdDSA'>} */
|
|
10
|
+
export const verifier = {
|
|
11
|
+
EdDSA: verify,
|
|
12
|
+
}
|