61d8f725ff2b5178aea18388a7550e3a256369cd
[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 testRpId = "localhost";
7 const testUserhandleBase64 = "AAECAwQFBgcICQ==";
8 const testAttestationCertificateBase64 =
9     "MIIB6jCCAZCgAwIBAgIGAWHAxcjvMAoGCCqGSM49BAMCMFMxJzAlBgNVBAMMHkJh" +
10     "c2ljIEF0dGVzdGF0aW9uIFVzZXIgU3ViIENBMTETMBEGA1UECgwKQXBwbGUgSW5j" +
11     "LjETMBEGA1UECAwKQ2FsaWZvcm5pYTAeFw0xODAyMjMwMzM3MjJaFw0xODAyMjQw" +
12     "MzQ3MjJaMGoxIjAgBgNVBAMMGTAwMDA4MDEwLTAwMEE0OUEyMzBBMDIxM0ExGjAY" +
13     "BgNVBAsMEUJBQSBDZXJ0aWZpY2F0aW9uMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMw" +
14     "EQYDVQQIDApDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvCje" +
15     "Pzr6Sg76XMoHuGabPaG6zjpLFL8Zd8/74Hh5PcL2Zq+o+f7ENXX+7nEXXYt0S8Ux" +
16     "5TIRw4hgbfxXQbWLEqM5MDcwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCBPAw" +
17     "FwYJKoZIhvdjZAgCBAowCKEGBAR0ZXN0MAoGCCqGSM49BAMCA0gAMEUCIAlK8A8I" +
18     "k43TbvKuYGHZs1DTgpTwmKTBvIUw5bwgZuYnAiEAtuJjDLKbGNJAJFMi5deEBqno" +
19     "pBTCqbfbDJccfyQpjnY=";
20 const testAttestationIssuingCACertificateBase64 =
21     "MIICIzCCAaigAwIBAgIIeNjhG9tnDGgwCgYIKoZIzj0EAwIwUzEnMCUGA1UEAwwe" +
22     "QmFzaWMgQXR0ZXN0YXRpb24gVXNlciBSb290IENBMRMwEQYDVQQKDApBcHBsZSBJ" +
23     "bmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMB4XDTE3MDQyMDAwNDIwMFoXDTMyMDMy" +
24     "MjAwMDAwMFowUzEnMCUGA1UEAwweQmFzaWMgQXR0ZXN0YXRpb24gVXNlciBTdWIg" +
25     "Q0ExMRMwEQYDVQQKDApBcHBsZSBJbmMuMRMwEQYDVQQIDApDYWxpZm9ybmlhMFkw" +
26     "EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoSZ/1t9eBAEVp5a8PrXacmbGb8zNC1X3" +
27     "StLI9YO6Y0CL7blHmSGmjGWTwD4Q+i0J2BY3+bPHTGRyA9jGB3MSbaNmMGQwEgYD" +
28     "VR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBSD5aMhnrB0w/lhkP2XTiMQdqSj" +
29     "8jAdBgNVHQ4EFgQU5mWf1DYLTXUdQ9xmOH/uqeNSD80wDgYDVR0PAQH/BAQDAgEG" +
30     "MAoGCCqGSM49BAMCA2kAMGYCMQC3M360LLtJS60Z9q3vVjJxMgMcFQ1roGTUcKqv" +
31     "W+4hJ4CeJjySXTgq6IEHn/yWab4CMQCm5NnK6SOSK+AqWum9lL87W3E6AA1f2TvJ" +
32     "/hgok/34jr93nhS87tOQNdxDS8zyiqw=";
33 const testDummyMessagePayloadBase64 =
34     "/////wYAEQABAgMEBQYHAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
35     "AAAAAAAAAAAAAAAAAAAAAAAAAAEQADoAAQIDBAUGBwECAwQAAAAAAAAAAAAAAAAA" +
36     "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgMEAAAAAAAAAAAAAAAA" +
37     "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
38 const testCreationMessageBase64 =
39     "AKMBZnBhY2tlZAJYxEbMf7lnnVWy25CS4cjZ5eHQK3WA8LSBLHcJYuHkj1rYQQAA" +
40     "AE74oBHzjApNFYAGFxEfntx9AEAoCK3O6P5OyXN6V/f+9nAga0NA2Cgp4V3mgSJ5" +
41     "jOHLMDrmxp/S0rbD+aihru1C0aAN3BkiM6GNy5nSlDVqOgTgpQECAyYgASFYIEFb" +
42     "he3RkNud6sgyraBGjlh1pzTlCZehQlL/b18HZ6WGIlggJgfUd/en9p5AIqMQbUni" +
43     "nEeXdFLkvW0/zV5BpEjjNxADo2NhbGcmY3NpZ1hHMEUCIQDKg+ZBmEBtf0lWq4Re" +
44     "dH4/i/LOYqOR4uR2NAj2zQmw9QIgbTXb4hvFbj4T27bv/rGrc+y+0puoYOBkBk9P" +
45     "mCewWlNjeDVjgVkCwjCCAr4wggGmoAMCAQICBHSG/cIwDQYJKoZIhvcNAQELBQAw" +
46     "LjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEw" +
47     "IBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG8xCzAJBgNVBAYTAlNF" +
48     "MRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0" +
49     "ZXN0YXRpb24xKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NTUwMDM4" +
50     "NDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASVXfOt9yR9MXXv/ZzE8xpOh466" +
51     "4YEJVmFQ+ziLLl9lJ79XQJqlgaUNCsUvGERcChNUihNTyKTlmnBOUjvATevto2ww" +
52     "ajAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMTATBgsrBgEEAYLl" +
53     "HAIBAQQEAwIFIDAhBgsrBgEEAYLlHAEBBAQSBBD4oBHzjApNFYAGFxEfntx9MAwG" +
54     "A1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBADFcSIDmmlJ+OGaJvWn9Cqhv" +
55     "SeueToVFQVVvqtALOgCKHdwB+Wx29mg2GpHiMsgQp5xjB0ybbnpG6x212FxESJ+G" +
56     "inZD0ipchi7APwPlhIvjgH16zVX44a4e4hOsc6tLIOP71SaMsHuHgCcdH0vg5d2s" +
57     "c006WJe9TXO6fzV+ogjJnYpNKQLmCXoAXE3JBNwKGBIOCvfQDPyWmiiG5bGxYfPt" +
58     "y8Z3pnjX+1MDnM2hhr40ulMxlSNDnX/ZSnDyMGIbk8TOQmjTF02UO8auP8k3wt5D" +
59     "1rROIRU9+FCSX5WQYi68RuDrGMZB8P5+byoJqbKQdxn2LmE1oZAyohPAmLcoPO4=";
60 const testHidCredentialIdBase64 =
61     "KAitzuj-Tslzelf3_vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2w_mooa7tQtGg" +
62     "DdwZIjOhjcuZ0pQ1ajoE4A";
63 const testAssertionMessageBase64 =
64     "AKMBomJpZFhAKAitzuj+Tslzelf3/vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2" +
65     "w/mooa7tQtGgDdwZIjOhjcuZ0pQ1ajoE4GR0eXBlanB1YmxpYy1rZXkCWCVGzH+5" +
66     "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
67     "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
68     "QoJ1L7Fe64G9uBc=";
69 const testAssertionMessageLongBase64 =
70     "AKUBomJpZFhAKAitzuj+Tslzelf3/vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2" +
71     "w/mooa7tQtGgDdwZIjOhjcuZ0pQ1ajoE4GR0eXBlanB1YmxpYy1rZXkCWCVGzH+5" +
72     "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
73     "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
74     "QoJ1L7Fe64G9uBcEpGJpZFggMIIBkzCCATigAwIBAjCCAZMwggE4oAMCAQIwggGT" +
75     "MIJkaWNvbngoaHR0cHM6Ly9waWNzLmFjbWUuY29tLzAwL3AvYUJqampwcVBiLnBu" +
76     "Z2RuYW1ldmpvaG5wc21pdGhAZXhhbXBsZS5jb21rZGlzcGxheU5hbWVtSm9obiBQ" +
77     "LiBTbWl0aAUC";
78 const testU2fApduNoErrorOnlyResponseBase64 = "kAA=";
79 const testU2fApduInsNotSupportedOnlyResponseBase64 = "bQA=";
80 const testU2fApduWrongDataOnlyResponseBase64 = "aoA=";
81 const testU2fApduConditionsNotSatisfiedOnlyResponseBase64 = "aYU=";
82 const testU2fRegisterResponse =
83     "BQTodiWJbuTkbcAydm6Ah5YvNt+d/otWfzdjAVsZkKYOFCfeYS1mQYvaGVBYHrxc" +
84     "jB2tcQyxTCL4yXBF9GEvsgyRQD69ib937FCXVe6cJjXvqqx7K5xc7xc2w3F9pIU0" +
85     "yMa2VNf/lF9QtcxOeAVb3TlrZPeNosX5YgDM1BXNCP5CADgwggJKMIIBMqADAgEC" +
86     "AgQEbIgiMA0GCSqGSIb3DQEBCwUAMC4xLDAqBgNVBAMTI1l1YmljbyBVMkYgUm9v" +
87     "dCBDQSBTZXJpYWwgNDU3MjAwNjMxMCAXDTE0MDgwMTAwMDAwMFoYDzIwNTAwOTA0" +
88     "MDAwMDAwWjAsMSowKAYDVQQDDCFZdWJpY28gVTJGIEVFIFNlcmlhbCAyNDkxODIz" +
89     "MjQ3NzAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ8yrksy5cofujmOUN+IfzW" +
90     "tvFlstWj89sTHTHBa3QrtHbY0emQgOtUbJu99VbmIQ/UJ4WJnnjMWJ6+MQ9s25/0" +
91     "ozswOTAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMjATBgsrBgEE" +
92     "AYLlHAIBAQQEAwIEMDANBgkqhkiG9w0BAQsFAAOCAQEAn5sFIki8TPQsxZkfyqus" +
93     "m2Ubvlvc3I7wrSwcH/s20YcV1C54skkiT5LH5uegXEnw5+TIgb8ulPReSiGDPXRW" +
94     "hR0PbBRaKVQMh08wksk0tD0iK4liwPQQzvHbdYkq8Ra0Spb101reo4IvxxRvYAQ4" +
95     "W8tptlyZ5+tpGXhnA8DYzUHo91zKRKqKtyWtjnmf86hpam8bJlbmMbHkAYPAj9pT" +
96     "+kqPhaBWk5RK4XmhM50ALRXKvYEAkOxyLvXe+ZZaNx1BXWJLaKJwfK2XvN0Xha+X" +
97     "4ljzPfVqAxqgNW2OjV68rcdOBxY2xrEQrOXMm5Df6srmQP8bsPH+XbTv96lfBgcz" +
98     "9TBFAiAyR3nGjzOAKIoRl7YJX3puubGxwSf2auEqmf6FMuwjuQIhAOOVFqxNYe5k" +
99     "BE1QtBWmpNTYS6bYlctat6GqfQgd40H6kAA=";
100 const testU2fCredentialIdBase64 =
101     "Pr2Jv3fsUJdV7pwmNe-qrHsrnFzvFzbDcX2khTTIxrZU1_-UX1C1zE54BVvdOWtk" +
102     "942ixfliAMzUFc0I_kIAOA";
103 const testU2fSignResponse =
104     "AQAAADswRAIge94KUqwfTIsn4AOjcM1mpMcRjdItVEeDX0W5nGhCP/cCIDxRe0eH" +
105     "f4V4LeEAhqeD0effTjY553H19q+jWq1Tc4WOkAA=";
106 const testCtapErrCredentialExcludedOnlyResponseBase64 = "GQ==";
107 const testCtapErrInvalidCredentialResponseBase64 = "Ig==";
108 const testCtapErrNotAllowedResponseBase64 = "MA==";
109 const testNfcU2fVersionBase64 = "VTJGX1YykAA=";
110 const testNfcCtapVersionBase64 = "RklET18yXzCQAA==";
111 const testGetInfoResponseApduBase64 =
112     "AKYBgmZVMkZfVjJoRklET18yXzACgWtobWFjLXNlY3JldANQbUS6m/bsLkm5MAyP" +
113     "6SDLcwSkYnJr9WJ1cPVkcGxhdPRpY2xpZW50UGlu9AUZBLAGgQGQAA==";
114 const testCreationMessageApduBase64 =
115     "AKMBZnBhY2tlZAJYxEbMf7lnnVWy25CS4cjZ5eHQK3WA8LSBLHcJYuHkj1rYQQAA" +
116     "AE74oBHzjApNFYAGFxEfntx9AEAoCK3O6P5OyXN6V/f+9nAga0NA2Cgp4V3mgSJ5" +
117     "jOHLMDrmxp/S0rbD+aihru1C0aAN3BkiM6GNy5nSlDVqOgTgpQECAyYgASFYIEFb" +
118     "he3RkNud6sgyraBGjlh1pzTlCZehQlL/b18HZ6WGIlggJgfUd/en9p5AIqMQbUni" +
119     "nEeXdFLkvW0/zV5BpEjjNxADo2NhbGcmY3NpZ1hHMEUCIQDKg+ZBmEBtf0lWq4Re" +
120     "dH4/i/LOYqOR4uR2NAj2zQmw9QIgbTXb4hvFbj4T27bv/rGrc+y+0puoYOBkBk9P" +
121     "mCewWlNjeDVjgVkCwjCCAr4wggGmoAMCAQICBHSG/cIwDQYJKoZIhvcNAQELBQAw" +
122     "LjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEw" +
123     "IBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG8xCzAJBgNVBAYTAlNF" +
124     "MRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0" +
125     "ZXN0YXRpb24xKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NTUwMDM4" +
126     "NDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASVXfOt9yR9MXXv/ZzE8xpOh466" +
127     "4YEJVmFQ+ziLLl9lJ79XQJqlgaUNCsUvGERcChNUihNTyKTlmnBOUjvATevto2ww" +
128     "ajAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMTATBgsrBgEEAYLl" +
129     "HAIBAQQEAwIFIDAhBgsrBgEEAYLlHAEBBAQSBBD4oBHzjApNFYAGFxEfntx9MAwG" +
130     "A1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBADFcSIDmmlJ+OGaJvWn9Cqhv" +
131     "SeueToVFQVVvqtALOgCKHdwB+Wx29mg2GpHiMsgQp5xjB0ybbnpG6x212FxESJ+G" +
132     "inZD0ipchi7APwPlhIvjgH16zVX44a4e4hOsc6tLIOP71SaMsHuHgCcdH0vg5d2s" +
133     "c006WJe9TXO6fzV+ogjJnYpNKQLmCXoAXE3JBNwKGBIOCvfQDPyWmiiG5bGxYfPt" +
134     "y8Z3pnjX+1MDnM2hhr40ulMxlSNDnX/ZSnDyMGIbk8TOQmjTF02UO8auP8k3wt5D" +
135     "1rROIRU9+FCSX5WQYi68RuDrGMZB8P5+byoJqbKQdxn2LmE1oZAyohPAmLcoPO6Q" +
136     "AA==";
137 const testAssertionMessageApduBase64 =
138     "AKMBomJpZFhAKAitzuj+Tslzelf3/vZwIGtDQNgoKeFd5oEieYzhyzA65saf0tK2" +
139     "w/mooa7tQtGgDdwZIjOhjcuZ0pQ1ajoE4GR0eXBlanB1YmxpYy1rZXkCWCVGzH+5" +
140     "Z51VstuQkuHI2eXh0Ct1gPC0gSx3CWLh5I9a2AEAAABQA1hHMEUCIQCSFTuuBWgB" +
141     "4/F0VB7DlUVM09IHPmxe1MzHUwRoCRZbCAIgGKov6xoAx2MEf6/6qNs8OutzhP2C" +
142     "QoJ1L7Fe64G9uBeQAA==";
143
144 const RESOURCES_DIR = "/WebKit/webauthn/resources/";
145
146 function asciiToUint8Array(str)
147 {
148     var chars = [];
149     for (var i = 0; i < str.length; ++i)
150         chars.push(str.charCodeAt(i));
151     return new Uint8Array(chars);
152 }
153
154 // Builds a hex string representation for an array-like input.
155 // "bytes" can be an Array of bytes, an ArrayBuffer, or any TypedArray.
156 // The output looks like this:
157 //    ab034c99
158 function bytesToHexString(bytes)
159 {
160     if (!bytes)
161         return null;
162
163     bytes = new Uint8Array(bytes);
164     var hexBytes = [];
165
166     for (var i = 0; i < bytes.length; ++i) {
167         var byteString = bytes[i].toString(16);
168         if (byteString.length < 2)
169             byteString = "0" + byteString;
170         hexBytes.push(byteString);
171     }
172
173     return hexBytes.join("");
174 }
175
176 function bytesToASCIIString(bytes)
177 {
178     return String.fromCharCode.apply(null, new Uint8Array(bytes));
179 }
180
181 var Base64URL = {
182     stringify: function (a) {
183         var base64string = btoa(String.fromCharCode.apply(0, a));
184         return base64string.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
185     },
186     parse: function (s) {
187         s = s.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, '');
188         return new Uint8Array(Array.prototype.map.call(atob(s), function (c) { return c.charCodeAt(0) }));
189     }
190 };
191
192 function hexStringToUint8Array(hexString)
193 {
194     if (hexString.length % 2 != 0)
195         throw "Invalid hexString";
196     var arrayBuffer = new Uint8Array(hexString.length / 2);
197
198     for (var i = 0; i < hexString.length; i += 2) {
199         var byteValue = parseInt(hexString.substr(i, 2), 16);
200         if (byteValue == NaN)
201             throw "Invalid hexString";
202         arrayBuffer[i/2] = byteValue;
203     }
204
205     return arrayBuffer;
206 }
207
208 function decodeAuthData(authDataUint8Array)
209 {
210     let authDataObject = { };
211     let pos = 0;
212     let size = 0;
213
214     // RP ID Hash
215     size = 32;
216     if (pos + size > authDataUint8Array.byteLength)
217         return { };
218     authDataObject.rpIdHash = authDataUint8Array.slice(pos, pos + size);
219     pos = pos + size;
220
221     // FLAGS
222     size = 1;
223     if (pos + size > authDataUint8Array.byteLength)
224         return { };
225     authDataObject.flags = authDataUint8Array.slice(pos, pos + size)[0];
226     pos = pos + size;
227
228     // Counter
229     size = 4;
230     if (pos + size > authDataUint8Array.byteLength)
231         return { };
232     authDataObject.counter = (authDataUint8Array[pos] << 24) + (authDataUint8Array[pos + 1] << 16) + (authDataUint8Array[pos + 2] << 8) + authDataUint8Array[pos + 3];
233     pos = pos + size;
234
235     if (pos == authDataUint8Array.byteLength)
236         return authDataObject;
237
238     // AAGUID
239     size = 16;
240     if (pos + size > authDataUint8Array.byteLength)
241         return { };
242     authDataObject.aaguid = authDataUint8Array.slice(pos, pos + size);
243     pos = pos + size;
244
245     // L
246     size = 2;
247     if (pos + size > authDataUint8Array.byteLength)
248         return { };
249     authDataObject.l = (authDataUint8Array[pos] << 8) + authDataUint8Array[pos + 1];
250     pos = pos + size;
251
252     // Credential ID
253     size = authDataObject.l;
254     if (pos + size > authDataUint8Array.byteLength)
255         return { };
256     authDataObject.credentialID = authDataUint8Array.slice(pos, pos + size);
257     pos = pos + size;
258
259     // Public Key
260     authDataObject.publicKey = CBOR.decode(authDataUint8Array.slice(pos).buffer);
261     if (!authDataObject.publicKey)
262         return { };
263
264     // Assume no extensions.
265     return authDataObject;
266 }
267
268 function concatenateBuffers(buffer1, buffer2)
269 {
270     let tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
271     tmp.set(new Uint8Array(buffer1), 0);
272     tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
273     return tmp.buffer;
274 }
275
276 // Very dirty asn1 decoder. Just works.
277 function extractRawSignature(asn1signature)
278 {
279     const signature = new Uint8Array(asn1signature);
280     let tmp = new Uint8Array(64);
281
282     const rStart =  signature[3] - 32;
283     if (rStart >= 0)
284         tmp.set(new Uint8Array(signature.slice(4 + rStart, 36 + rStart)), 0);
285     else
286         tmp.set(new Uint8Array(signature.slice(4, 36 + rStart)), -rStart);
287
288     const sStart =  signature[37 + rStart] - 32;
289     if (sStart >= 0)
290         tmp.set(new Uint8Array(signature.slice(38 + rStart + sStart)), 32);
291     else
292         tmp.set(new Uint8Array(signature.slice(38 + rStart)), 32 - sStart);
293
294     return tmp.buffer;
295 }
296
297 function waitForLoad()
298 {
299     return new Promise((resolve) => {
300         window.addEventListener('message', (message) => {
301             resolve(message);
302         });
303     });
304 }
305
306 function withCrossOriginIframe(resourceFile)
307 {
308     return new Promise((resolve) => {
309         waitForLoad().then((message) => {
310             resolve(message);
311         });
312         const frame = document.createElement("iframe");
313         frame.src = get_host_info().HTTPS_REMOTE_ORIGIN + RESOURCES_DIR + resourceFile;
314         document.body.appendChild(frame);
315     });
316 }
317
318 function promiseRejects(test, expected, promise, description)
319 {
320     return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
321         assert_throws(expected, function() { throw e }, description);
322         assert_equals(e.message, description);
323     });
324 }
325
326 // COSE Key Format: https://www.w3.org/TR/webauthn/#sctn-encoded-credPubKey-examples.
327 function checkPublicKey(publicKey)
328 {
329     if (publicKey['1'] != 2)
330         return false;
331     if (publicKey['3'] != -7)
332         return false;
333     if (publicKey['-1'] != 1)
334         return false;
335     if (publicKey['-2'].byteLength != 32)
336         return false;
337     if (publicKey['-3'].byteLength != 32)
338         return false;
339     return true;
340 }
341
342 function checkCtapMakeCredentialResult(credential, isNoneAttestation = true)
343 {
344     // Check response
345     assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testHidCredentialIdBase64));
346     assert_equals(credential.type, 'public-key');
347     assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testHidCredentialIdBase64));
348     assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
349     assert_not_own_property(credential.getClientExtensionResults(), "appid");
350
351     // Check attestation
352     const attestationObject = CBOR.decode(credential.response.attestationObject);
353     if (isNoneAttestation)
354         assert_equals(attestationObject.fmt, "none");
355     else
356         assert_equals(attestationObject.fmt, "packed");
357     // Check authData
358     const authData = decodeAuthData(attestationObject.authData);
359     assert_equals(bytesToHexString(authData.rpIdHash), "46cc7fb9679d55b2db9092e1c8d9e5e1d02b7580f0b4812c770962e1e48f5ad8");
360     assert_equals(authData.flags, 65);
361     assert_equals(authData.counter, 78);
362     if (isNoneAttestation)
363         assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000");
364     else
365         assert_equals(bytesToHexString(authData.aaguid), "f8a011f38c0a4d15800617111f9edc7d");
366     assert_array_equals(authData.credentialID, Base64URL.parse(testHidCredentialIdBase64));
367     // Check packed attestation
368     assert_true(checkPublicKey(authData.publicKey));
369     if (isNoneAttestation)
370         assert_object_equals(attestationObject.attStmt, { });
371     else {
372         assert_equals(attestationObject.attStmt.alg, -7);
373         assert_equals(attestationObject.attStmt.x5c.length, 1);
374     }
375 }
376
377 function checkU2fMakeCredentialResult(credential, isNoneAttestation = true)
378 {
379     // Check response
380     assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testU2fCredentialIdBase64));
381     assert_equals(credential.type, 'public-key');
382     assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testU2fCredentialIdBase64));
383     assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.create","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
384     assert_not_own_property(credential.getClientExtensionResults(), "appid");
385
386     // Check attestation
387     const attestationObject = CBOR.decode(credential.response.attestationObject);
388     if (isNoneAttestation)
389         assert_equals(attestationObject.fmt, "none");
390     else
391         assert_equals(attestationObject.fmt, "fido-u2f");
392     // Check authData
393     const authData = decodeAuthData(attestationObject.authData);
394     assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
395     assert_equals(authData.flags, 65);
396     assert_equals(authData.counter, 0);
397     assert_equals(bytesToHexString(authData.aaguid), "00000000000000000000000000000000");
398     assert_array_equals(authData.credentialID, Base64URL.parse(testU2fCredentialIdBase64));
399     // Check fido-u2f attestation
400     assert_true(checkPublicKey(authData.publicKey));
401     if (isNoneAttestation)
402         assert_object_equals(attestationObject.attStmt, { });
403     else
404         assert_equals(attestationObject.attStmt.x5c.length, 1);
405 }
406
407 function checkCtapGetAssertionResult(credential, userHandleBase64 = null)
408 {
409     // Check respond
410     assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testHidCredentialIdBase64));
411     assert_equals(credential.type, 'public-key');
412     assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testHidCredentialIdBase64));
413     assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
414     if (userHandleBase64 == null)
415         assert_equals(credential.response.userHandle, null);
416     else
417         assert_array_equals(new Uint8Array(credential.response.userHandle), Base64URL.parse(userHandleBase64));
418     assert_not_own_property(credential.getClientExtensionResults(), "appid");
419
420     // Check authData
421     const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));
422     assert_equals(bytesToHexString(authData.rpIdHash), "46cc7fb9679d55b2db9092e1c8d9e5e1d02b7580f0b4812c770962e1e48f5ad8");
423     assert_equals(authData.flags, 1);
424     assert_equals(authData.counter, 80);
425 }
426
427 function checkU2fGetAssertionResult(credential, isAppID = false, appIDHash = "c2671b6eb9233197d5f2b1288a55ba4f0860f96f7199bba32fe6da7c3f0f31e5")
428 {
429     // Check respond
430     assert_array_equals(Base64URL.parse(credential.id), Base64URL.parse(testU2fCredentialIdBase64));
431     assert_equals(credential.type, 'public-key');
432     assert_array_equals(new Uint8Array(credential.rawId), Base64URL.parse(testU2fCredentialIdBase64));
433     assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443"}');
434     assert_equals(credential.response.userHandle, null);
435     if (!isAppID)
436         assert_not_own_property(credential.getClientExtensionResults(), "appid");
437     else
438         assert_true(credential.getClientExtensionResults().appid);
439
440     // Check authData
441     const authData = decodeAuthData(new Uint8Array(credential.response.authenticatorData));
442     if (!isAppID)
443         assert_equals(bytesToHexString(authData.rpIdHash), "49960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d9763");
444     else
445         assert_equals(bytesToHexString(authData.rpIdHash), appIDHash);
446     assert_equals(authData.flags, 1);
447     assert_equals(authData.counter, 59);
448 }
449
450 function generateUserhandleBase64()
451 {
452     let buffer = new Uint8Array(16);
453     crypto.getRandomValues(buffer);
454     return btoa(String.fromCharCode.apply(0, buffer));
455 }
456
457 async function generatePrivateKeyBase64()
458 {
459     const algorithmKeyGen = {
460         name: "ECDSA",
461         namedCurve: "P-256"
462     };
463     const extractable = true;
464
465     const keyPair = await crypto.subtle.generateKey(algorithmKeyGen, extractable, ["sign", "verify"]);
466     const jwkPrivateKey = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
467
468     const x = Base64URL.parse(jwkPrivateKey.x);
469     const y = Base64URL.parse(jwkPrivateKey.y);
470     const d = Base64URL.parse(jwkPrivateKey.d);
471
472     let buffer = new Uint8Array(x.length + y.length + d.length + 1);
473     buffer[0] = 0x04;
474     let pos = 1;
475     buffer.set(x, pos);
476     pos += x.length;
477     buffer.set(y, pos);
478     pos += y.length;
479     buffer.set(d, pos);
480
481     return btoa(String.fromCharCode.apply(0, buffer));
482 }
483
484 async function calculateCredentialID(privateKeyBase64) {
485     const privateKey = Base64URL.parse(privateKeyBase64);
486     const publicKey = privateKey.slice(0, 65);
487     return new Uint8Array(await crypto.subtle.digest("sha-1", publicKey));
488 }