-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathder_lite.js
170 lines (156 loc) · 6.54 KB
/
der_lite.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
'use strict';
class DERLite{
constructor(mzcc) {
if (mzcc === undefined) {
mzcc = new MozCommon();
}
this.mzcc = mzcc
}
/* Simplified DER export and import is provided because a large number of
* libraries and languages understand DER as a key exchange and storage
* format. DER is NOT required for VAPID, however, the key you may
* generate here (or in a different library) may be in this format.
*
* A fully featured DER library is available at
* https://github.com/indutny/asn1.js
*/
export_private_der(key) {
/* Generate a DER sequence.
*
* This can be read in via something like
* python's
* ecdsa.keys.SigningKey
* .from_der(base64.urlsafe_b64decode("MHc..."))
*
* :param key: CryptoKey containing private key info
*/
return webCrypto.exportKey("jwk", key)
.then(k => {
// verifying key
let xv = this.mzcc.fromUrlBase64(k.x);
let yv = this.mzcc.fromUrlBase64(k.y);
// private key
let dv = this.mzcc.fromUrlBase64(k.d);
// verifying key (public)
let vk = '\x00\x04' + xv + yv;
// \x02 is integer
let int1 = '\x02\x01\x01'; // integer 1
// \x04 is octet string
let dvstr = '\x04' + this.mzcc.chr(dv.length) + dv;
let curve_oid = "\x06\x08" +
"\x2a\x86\x48\xce\x3d\x03\x01\x07";
// \xaX is a construct, low byte is order.
let curve_oid_const = '\xa0' + this.mzcc.chr(curve_oid.length) +
curve_oid;
// \x03 is a bitstring
let vk_enc = '\x03' + this.mzcc.chr(vk.length) + vk;
let vk_const = '\xa1' + this.mzcc.chr(vk_enc.length) + vk_enc;
// \x30 is a sequence start.
let seq = int1 + dvstr + curve_oid_const + vk_const;
let rder = "\x30" + this.mzcc.chr(seq.length) + seq;
return this.mzcc.toUrlBase64(rder);
})
.catch(err => console.error(err))
}
import_private_der(der_str) {
/* Import a Private Key stored in DER format. This allows a key
* to be generated outside of this script.
*
* :param der_str: URL safe base64 formatted DER string.
* :returns: Promise containing the imported private key
*/
let der = this.mzcc.strToArray(this.mzcc.fromUrlBase64(der_str));
// quick guantlet to see if this is a valid DER
let cmp = new Uint8Array([2,1,1,4]);
if (der[0] != 48 ||
! der.slice(2, 6).every(function(v, i){return cmp[i] == v})){
throw new Error("Invalid import key")
}
let dv = der.slice(7, 7+der[6]);
// HUGE cheat to get the x y values
let xv = der.slice(-64, -32);
let yv = der.slice(-32);
let key_ops = ['sign'];
let jwk = {
crv: "P-256",
ext: true,
key_ops: key_ops,
kty: "EC",
x: this.mzcc.toUrlBase64(String.fromCharCode.apply(null, xv)),
y: this.mzcc.toUrlBase64(String.fromCharCode.apply(null, yv)),
d: this.mzcc.toUrlBase64(String.fromCharCode.apply(null, dv)),
};
console.debug(JSON.stringify(jwk));
return webCrypto.importKey('jwk', jwk, 'ECDSA', true, key_ops);
}
export_public_der(key) {
/* Generate a DER sequence containing just the public key info.
*
* :param key: CryptoKey containing public key information
* :returns: a URL safe base64 encoded string containing the
* public key
*/
return webCrypto.exportKey("jwk", key)
.then(k => {
// raw keys always begin with a 4
let xv = this.mzcc.strToArray(this.mzcc.fromUrlBase64(k.x));
let yv = this.mzcc.strToArray(this.mzcc.fromUrlBase64(k.y));
let point = "\x00\x04" +
String.fromCharCode.apply(null, xv) +
String.fromCharCode.apply(null, yv);
window.Kpoint = point;
// a combination of the oid_ecPublicKey + p256 encoded oid
let prefix = "\x30\x13" + // sequence + length
"\x06\x07" + "\x2a\x86\x48\xce\x3d\x02\x01" +
"\x06\x08" + "\x2a\x86\x48\xce\x3d\x03\x01\x07"
let encPoint = "\x03" + this.mzcc.chr(point.length) + point
let rder = "\x30" + this.mzcc.chr(prefix.length + encPoint.length) +
prefix + encPoint;
let der = this.mzcc.toUrlBase64(rder);
return der;
});
}
import_public_der(derArray) {
/* Import a DER formatted public key string.
*
* The Crypto-Key p256ecdsa=... key is such a thing.
* Returns a promise containing the public key.
*
* :param derArray: the DER array containing the public key.
* NOTE: This may also be a URL safe base64 encoded version
* of the DER array.
* :returns: A promise containing the imported public key.
*
*/
if (typeof(derArray) == "string") {
derArray = this.mzcc.strToArray(this.mzcc.fromUrlBase64(derArray));
}
/* Super light weight public key import function */
let err = new Error(this.lang.errs.ERR_PUB_D_KEY);
// Does the record begin with "\x30"
if (derArray[0] != 48) { throw err}
// is this an ECDSA record? (looking for \x2a and \x86
if (derArray[6] != 42 && derArray[7] != 134) { throw err}
if (derArray[15] != 42 && derArray[16] != 134) { throw err}
// Public Key Record usually beings @ offset 23.
if (derArray[23] != 3 && derArray[24] != 40 &&
derArray[25] != 0 && derArray[26] != 4) {
throw err;
}
// pubkey offset starts at byte 25
let x = this.mzcc.toUrlBase64(String.fromCharCode
.apply(null, derArray.slice(27, 27+32)));
let y = this.mzcc.toUrlBase64(String.fromCharCode
.apply(null, derArray.slice(27+32, 27+64)));
// Convert to a JWK and import it.
let jwk = {
crv: "P-256",
ext: true,
key_ops: ["verify"],
kty: "EC",
x: x,
y, y
};
return webCrypto.importKey('jwk', jwk, 'ECDSA', true, ["verify"])
}
}