[WebAuthN] Import a JS CBOR coder
[WebKit-https.git] / LayoutTests / http / wpt / webauthn / resources / util.js
1 const testCredentialIdBase64 = "SMSXHngF7hEOsElA73C3RY+8bR4=";
2 const testES256PrivateKeyBase64 =
3     "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
4     "PH76c0+WFOzZKslPyyFse4goGIW2R7k9VHLPEZl5nfnBgEVFh5zev+/xpHQIvuq6" +
5     "RQ==";
6 const testES256PublicKeyBase64 =
7     "BDj/zxSkzKgaBuS3cdWDF558of8AaIpgFpsjF/Qm1749VBJPgqUIwfhWHJ91nb7U" +
8     "PH76c0+WFOzZKslPyyFse4g=";
9 const testRpId = "localhost";
10 const testUserhandleBase64 = "AAECAwQFBgcICQ==";
11 const testAttestationCertificateBase64 =
12     "MIIB6jCCAZCgAwIBAgIGAWHAxcjvMAoGCCqGSM49BAMCMFMxJzAlBgNVBAMMHkJh" +
13     "c2ljIEF0dGVzdGF0aW9uIFVzZXIgU3ViIENBMTETMBEGA1UECgwKQXBwbGUgSW5j" +
14     "LjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0xODAyMjMwMzM3MjJaFw0xODAyMjQw" +
15     "MzQ3MjJaMGoxIjAgBgNVBAMMGTAwMDA4MDEwLTAwMEE0OUEyMzBBMDIxM0ExGjAY" +
16     "BgNVBAsMEUJBQSBDZXJ0aWZpY2F0aW9uMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMw" +
17     "EQYDVQQIDApDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvCje" +
18     "Pzr6Sg76XMoHuGabPaG6zjpLFL8Zd8/74Hh5PcL2Zq+o+f7ENXX+7nEXXYt0S8Ux" +
19     "5TIRw4hgbfxXQbWLEqM5MDcwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBPAw" +
20     "FwYJKoZIhvdjZAgCBAowCKEGBAR0ZXN0MAoGCCqGSM49BAMCA0gAMEUCIAlK8A8I" +
21     "k43TbvKuYGHZs1DTgpTwmKTBvIUw5bwgZuYnAiEAtuJjDLKbGNJAJFMi5deEBqno" +
22     "pBTCqbfbDJccfyQpjnY=";
23 const testAttestationIssuingCACertificateBase64 =
24     "MIICIzCCAaigAwIBAgIIeNjhG9tnDGgwCgYIKoZIzj0EAwIwUzEnMCUGA1UEAwwe" +
25     "QmFzaWMgQXR0ZXN0YXRpb24gVXNlciBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ" +
26     "bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTE3MDQyMDAwNDIwMFoXDTMyMDMy" +
27     "MjAwMDAwMFowUzEnMCUGA1UEAwweQmFzaWMgQXR0ZXN0YXRpb24gVXNlciBTdWIg" +
28     "Q0ExMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMFkw" +
29     "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoSZ/1t9eBAEVp5a8PrXacmbGb8zNC1X3" +
30     "StLI9YO6Y0CL7blHmSGmjGWTwD4Q+i0J2BY3+bPHTGRyA9jGB3MSbaNmMGQwEgYD" +
31     "VR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBSD5aMhnrB0w/lhkP2XTiMQdqSj" +
32     "8jAdBgNVHQ4EFgQU5mWf1DYLTXUdQ9xmOH/uqeNSD80wDgYDVR0PAQH/BAQDAgEG" +
33     "MAoGCCqGSM49BAMCA2kAMGYCMQC3M360LLtJS60Z9q3vVjJxMgMcFQ1roGTUcKqv" +
34     "W+4hJ4CeJjySXTgq6IEHn/yWab4CMQCm5NnK6SOSK+AqWum9lL87W3E6AA1f2TvJ" +
35     "/hgok/34jr93nhS87tOQNdxDS8zyiqw=";
36
37 const RESOURCES_DIR = "/WebKit/webauthn/resources/";
38
39 function asciiToUint8Array(str)
40 {
41     var chars = [];
42     for (var i = 0; i < str.length; ++i)
43         chars.push(str.charCodeAt(i));
44     return new Uint8Array(chars);
45 }
46
47 // Builds a hex string representation for an array-like input.
48 // "bytes" can be an Array of bytes, an ArrayBuffer, or any TypedArray.
49 // The output looks like this:
50 //    ab034c99
51 function bytesToHexString(bytes)
52 {
53     if (!bytes)
54         return null;
55
56     bytes = new Uint8Array(bytes);
57     var hexBytes = [];
58
59     for (var i = 0; i < bytes.length; ++i) {
60         var byteString = bytes[i].toString(16);
61         if (byteString.length < 2)
62             byteString = "0" + byteString;
63         hexBytes.push(byteString);
64     }
65
66     return hexBytes.join("");
67 }
68
69 function bytesToASCIIString(bytes)
70 {
71     return String.fromCharCode.apply(null, new Uint8Array(bytes));
72 }
73
74 var Base64URL = {
75     stringify: function (a) {
76         var base64string = btoa(String.fromCharCode.apply(0, a));
77         return base64string.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
78     },
79     parse: function (s) {
80         s = s.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, '');
81         return new Uint8Array(Array.prototype.map.call(atob(s), function (c) { return c.charCodeAt(0) }));
82     }
83 };
84
85 function hexStringToUint8Array(hexString)
86 {
87     if (hexString.length % 2 != 0)
88         throw "Invalid hexString";
89     var arrayBuffer = new Uint8Array(hexString.length / 2);
90
91     for (var i = 0; i < hexString.length; i += 2) {
92         var byteValue = parseInt(hexString.substr(i, 2), 16);
93         if (byteValue == NaN)
94             throw "Invalid hexString";
95         arrayBuffer[i/2] = byteValue;
96     }
97
98     return arrayBuffer;
99 }
100
101 function decodeAuthData(authDataUint8Array)
102 {
103     let authDataObject = { };
104     let pos = 0;
105     let size = 0;
106
107     // RP ID Hash
108     size = 32;
109     if (pos + size > authDataUint8Array.byteLength)
110         return { };
111     authDataObject.rpIdHash = authDataUint8Array.slice(pos, pos + size);
112     pos = pos + size;
113
114     // FLAGS
115     size = 1;
116     if (pos + size > authDataUint8Array.byteLength)
117         return { };
118     authDataObject.flags = authDataUint8Array.slice(pos, pos + size)[0];
119     pos = pos + size;
120
121     // Counter
122     size = 4;
123     if (pos + size > authDataUint8Array.byteLength)
124         return { };
125     authDataObject.counter = new Uint32Array(authDataUint8Array.slice(pos, pos + size))[0];
126     pos = pos + size;
127
128     if (pos == authDataUint8Array.byteLength)
129         return authDataObject;
130
131     // AAGUID
132     size = 16;
133     if (pos + size > authDataUint8Array.byteLength)
134         return { };
135     authDataObject.aaguid = authDataUint8Array.slice(pos, pos + size);
136     pos = pos + size;
137
138     // L
139     size = 2;
140     if (pos + size > authDataUint8Array.byteLength)
141         return { };
142     // Little Endian
143     authDataObject.l = new Uint16Array(authDataUint8Array.slice(pos, pos + size))[0];
144     pos = pos + size;
145
146     // Credential ID
147     size = authDataObject.l;
148     if (pos + size > authDataUint8Array.byteLength)
149         return { };
150     authDataObject.credentialID = authDataUint8Array.slice(pos, pos + size);
151     pos = pos + size;
152
153     // Public Key
154     authDataObject.publicKey = CBOR.decode(authDataUint8Array.slice(pos).buffer);
155     if (!authDataObject.publicKey)
156         return { };
157
158     // Assume no extensions.
159     return authDataObject;
160 }
161
162 function concatenateBuffers(buffer1, buffer2)
163 {
164     let tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
165     tmp.set(new Uint8Array(buffer1), 0);
166     tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
167     return tmp.buffer;
168 }
169
170 // Very dirty asn1 decoder. Just works.
171 function extractRawSignature(asn1signature)
172 {
173     const signature = new Uint8Array(asn1signature);
174     let tmp = new Uint8Array(64);
175     const rStart =  signature[3] - 32;
176     tmp.set(new Uint8Array(signature.slice(4 + rStart, 36 + rStart)), 0);
177     const sStart =  signature[37 + rStart] - 32;
178     tmp.set(new Uint8Array(signature.slice(38 + rStart + sStart)), 32);
179     return tmp.buffer;
180 }
181
182 function waitForLoad()
183 {
184     return new Promise((resolve) => {
185         window.addEventListener('message', (message) => {
186             resolve(message);
187         });
188     });
189 }
190
191 function withCrossOriginIframe(resourceFile)
192 {
193     return new Promise((resolve) => {
194         waitForLoad().then((message) => {
195             resolve(message);
196         });
197         const frame = document.createElement("iframe");
198         frame.src = get_host_info().HTTPS_REMOTE_ORIGIN + RESOURCES_DIR + resourceFile;
199         document.body.appendChild(frame);
200     });
201 }
202
203 function promiseRejects(test, expected, promise, description)
204 {
205     return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
206         assert_throws(expected, function() { throw e }, description);
207         assert_equals(e.message, description);
208     });
209 }
210
211 // COSE Key Format: https://www.w3.org/TR/webauthn/#sctn-encoded-credPubKey-examples.
212 function checkPublicKey(publicKey)
213 {
214     if (publicKey['1'] != 2)
215         return false;
216     if (publicKey['3'] != -7)
217         return false;
218     if (publicKey['-1'] != 1)
219         return false;
220     if (publicKey['-2'].byteLength != 32)
221         return false;
222     if (publicKey['-3'].byteLength != 32)
223         return false;
224     return true;
225 }