-
Notifications
You must be signed in to change notification settings - Fork 2
/
signer.js
119 lines (112 loc) · 3.18 KB
/
signer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
const {toCryptoKeyFromJwk, toJwkFromPkcs1} = require('./lib/keyutil');
/** @description The signer based on Web Crypto API */
class WebCryptoSigner {
/**
* @param {String|Object} key key can be a CryptoKey or PKCS#1 PEM
*/
constructor(key) {
if (typeof key == 'string') {
this.pkcs1 = key;
} else if (typeof key === 'object') {
this.key = key;
} else {
throw new Error(
'key type should be either String (PEM) or Object (CryptoKey)',
);
}
}
/**
* @param {Uint8Array} content
* @return {Uint8Array}
*/
async sign(content) {
let key;
if (this.key) {
key = this.key;
} else {
if (!this.jwk) {
this.jwk = await toJwkFromPkcs1(this.pkcs1);
}
try {
key = await toCryptoKeyFromJwk(this.jwk);
} catch (_) {
throw new Error('Failed load the PEM file');
}
}
const algorithm = {
// EcdsaParams
name: 'ECDSA',
hash: 'SHA-256',
};
const data = content;
try {
const signature = await window.crypto.subtle.sign(algorithm, key, data);
return this._P1363ToDer(new Uint8Array(signature));
} catch (_) {
throw new Error(`Failed to sign the request`);
}
}
/**
* @param {Uint8Array} sig - P1363 signature
* @return {Uint8Array} DER signature
*
* This function is taken from the SDK of token.io (https://github.com/tokenio/sdk-js)
*
* Copyright (c) 2020, Token, Inc. (https://token.io)
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* Converts an ECDSA signature from P1363 to DER format
*
* IEEE P1363: bytes array of [
* r,
* s
* ]
*
* ASN.1 DER: bytes array of [
* 0x30 (DER sequence tag),
* (length of the bytes after this byte),
* 0x02 (DER integer tag),
* (length of the bytes of r),
* (padding, if necessary)
* r,
* 0x02 (DER integer tag),
* (length of the bytes of s),
* (padding, if necessary)
* s
* ]
*/
_P1363ToDer(sig) {
const signature = Array.from(sig, (x) =>
('00' + x.toString(16)).slice(-2),
).join('');
let r = signature.substr(0, signature.length / 2);
let s = signature.substr(signature.length / 2);
r = r.replace(/^(00)+/, '');
s = s.replace(/^(00)+/, '');
if ((parseInt(r, 16) & '0x80') > 0) r = `00${r}`;
if ((parseInt(s, 16) & '0x80') > 0) s = `00${s}`;
const rString = `02${(r.length / 2).toString(16).padStart(2, '0')}${r}`;
const sString = `02${(s.length / 2).toString(16).padStart(2, '0')}${s}`;
const derSig = `30${((rString.length + sString.length) / 2)
.toString(16)
.padStart(2, '0')}${rString}${sString}`;
return new Uint8Array(
derSig.match(/[\da-f]{2}/gi).map((h) => parseInt(h, 16)),
);
}
}
/** @description A factory to create EllipticSigner by given PEM */
class SignerFactory {
/**
* @param {String|Object} key
* @return {WebCryptoSigner}
*/
create(key) {
return new WebCryptoSigner(key);
}
}
module.exports = {
SignerFactory,
};