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