test_webauthn_make_credential.html (18013B)
1 <!DOCTYPE html> 2 <meta charset=utf-8> 3 <head> 4 <title>Test for MakeCredential for W3C Web Authentication</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <script type="text/javascript" src="u2futil.js"></script> 7 <script type="text/javascript" src="pkijs/common.js"></script> 8 <script type="text/javascript" src="pkijs/asn1.js"></script> 9 <script type="text/javascript" src="pkijs/x509_schema.js"></script> 10 <script type="text/javascript" src="pkijs/x509_simpl.js"></script> 11 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 12 </head> 13 <body> 14 15 <h1>Test for MakeCredential for W3C Web Authentication</h1> 16 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a> 17 18 <script class="testbody" type="text/javascript"> 19 "use strict"; 20 21 add_task(async () => { 22 await addVirtualAuthenticator(); 23 }); 24 25 is(navigator.authentication, undefined, "navigator.authentication does not exist any longer"); 26 isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist"); 27 isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist"); 28 isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist"); 29 30 let credm; 31 let gCredentialChallenge; 32 let rp; 33 let user; 34 let param; 35 let unsupportedParam; 36 let unknownParam; 37 38 // Setup test env 39 add_task(() => { 40 gCredentialChallenge = new Uint8Array(16); 41 window.crypto.getRandomValues(gCredentialChallenge); 42 43 rp = {id: document.domain, name: "none"}; 44 user = {id: new Uint8Array(64), name: "none", displayName: "none"}; 45 param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256}; 46 unsupportedParam = {type: "public-key", alg: cose_alg_ECDSA_w_SHA512}; 47 unknownParam = {type: "SimplePassword", alg: "MaxLength=2"}; 48 credm = navigator.credentials; 49 }); 50 // Add tests 51 add_task(test_good_call); 52 add_task(test_empty_account); 53 add_task(test_without_rp_name); 54 add_task(test_without_user_id); 55 add_task(test_without_user_name); 56 add_task(test_without_user_displayname); 57 add_task(test_user_too_large); 58 add_task(test_empty_parameters); 59 add_task(test_without_parameters); 60 add_task(test_unsupported_parameter); 61 add_task(test_unsupported_but_one_param); 62 add_task(test_one_unknown_parameter); 63 add_task(test_one_unknown_one_unsupported_param); 64 add_task(test_one_of_each_parameters); 65 add_task(test_without_challenge); 66 add_task(test_invalid_challenge); 67 add_task(test_duplicate_pub_key); 68 add_task(test_invalid_rp_id); 69 add_task(test_invalid_rp_id_2); 70 add_task(test_missing_rp); 71 add_task(test_incorrect_user_id_type); 72 add_task(test_missing_user); 73 add_task(test_complete_account); 74 add_task(test_too_large_user_id); 75 add_task(test_excluding_unknown_transports); 76 add_task(test_unknown_attestation_type); 77 add_task(test_unknown_selection_criteria); 78 add_task(test_no_unexpected_extensions); 79 add_task(test_cred_props_with_rk_required); 80 add_task(test_cred_props_with_rk_discouraged); 81 82 function arrivingHereIsGood(aResult) { 83 ok(true, "Good result! Received a: " + aResult); 84 return Promise.resolve(); 85 } 86 87 function arrivingHereIsBad(aResult) { 88 ok(false, "Bad result! Received a: " + aResult); 89 return Promise.resolve(); 90 } 91 92 function expectNotAllowedError(aResult) { 93 ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError"); 94 return Promise.resolve(); 95 } 96 97 function expectTypeError(aResult) { 98 ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError"); 99 return Promise.resolve(); 100 } 101 102 function expectNotSupportedError(aResult) { 103 ok(aResult.toString().startsWith("NotSupportedError"), "Expecting a NotSupportedError"); 104 return Promise.resolve(); 105 } 106 107 // Test basic good call 108 async function test_good_call() { 109 let makeCredentialOptions = { 110 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param] 111 }; 112 return credm.create({publicKey: makeCredentialOptions}) 113 .then(arrivingHereIsGood) 114 .catch(arrivingHereIsBad); 115 } 116 117 // Test empty account 118 async function test_empty_account() { 119 let makeCredentialOptions = { 120 challenge: gCredentialChallenge, pubKeyCredParams: [param] 121 }; 122 return credm.create({publicKey: makeCredentialOptions}) 123 .then(arrivingHereIsBad) 124 .catch(expectTypeError); 125 } 126 127 // Test without rp.name 128 async function test_without_rp_name() { 129 let rp1 = {id: document.domain}; 130 let makeCredentialOptions = { 131 rp: rp1, user, challenge: gCredentialChallenge, pubKeyCredParams: [param] 132 }; 133 return credm.create({publicKey: makeCredentialOptions}) 134 .then(arrivingHereIsBad) 135 .catch(expectTypeError); 136 } 137 138 // Test without user.id 139 async function test_without_user_id() { 140 let user1 = {name: "none", displayName: "none"}; 141 let makeCredentialOptions = { 142 rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param] 143 }; 144 return credm.create({publicKey: makeCredentialOptions}) 145 .then(arrivingHereIsBad) 146 .catch(expectTypeError); 147 } 148 149 // Test without user.name 150 async function test_without_user_name() { 151 let user1 = {id: new Uint8Array(64), displayName: "none"}; 152 let makeCredentialOptions = { 153 rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param] 154 }; 155 return credm.create({publicKey: makeCredentialOptions}) 156 .then(arrivingHereIsBad) 157 .catch(expectTypeError); 158 } 159 160 // Test without user.displayName 161 async function test_without_user_displayname() { 162 let user1 = {id: new Uint8Array(64), name: "none"}; 163 let makeCredentialOptions = { 164 rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param] 165 }; 166 return credm.create({publicKey: makeCredentialOptions}) 167 .then(arrivingHereIsBad) 168 .catch(expectTypeError); 169 } 170 171 // Test with a user handle that exceeds the max length 172 async function test_user_too_large() { 173 let user1 = {id: new Uint8Array(65), name: "none", displayName: "none"}; 174 let makeCredentialOptions = { 175 rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param] 176 }; 177 return credm.create({publicKey: makeCredentialOptions}) 178 .then(arrivingHereIsBad) 179 .catch(expectTypeError); 180 } 181 182 // Test without any parameters; this is acceptable meaning the RP ID is 183 // happy to accept either ECDSA-SHA256 or RSA-SHA256 184 async function test_empty_parameters() { 185 let makeCredentialOptions = { 186 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [] 187 }; 188 return credm.create({publicKey: makeCredentialOptions}) 189 .then(arrivingHereIsGood) 190 .catch(arrivingHereIsBad); 191 } 192 193 // Test without a parameter array at all 194 async function test_without_parameters() { 195 let makeCredentialOptions = { 196 rp, user, challenge: gCredentialChallenge 197 }; 198 return credm.create({publicKey: makeCredentialOptions}) 199 .then(arrivingHereIsBad) 200 .catch(expectTypeError); 201 } 202 203 // Test with an unsupported parameter 204 // The result depends on the tokens that are available, so we 205 // expect a NotAllowedError so as not to leak this. 206 async function test_unsupported_parameter() { 207 let makeCredentialOptions = { 208 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [unsupportedParam] 209 }; 210 return credm.create({publicKey: makeCredentialOptions}) 211 .then(arrivingHereIsBad) 212 .catch(expectNotAllowedError); 213 } 214 215 // Test with an unsupported parameter and a good one 216 async function test_unsupported_but_one_param() { 217 let makeCredentialOptions = { 218 rp, user, challenge: gCredentialChallenge, 219 pubKeyCredParams: [param, unsupportedParam] 220 }; 221 return credm.create({publicKey: makeCredentialOptions}) 222 .then(arrivingHereIsGood) 223 .catch(arrivingHereIsBad); 224 } 225 226 // Test with one unknown (not "public-key") parameter. 227 async function test_one_unknown_parameter() { 228 let makeCredentialOptions = { 229 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [unknownParam] 230 }; 231 return credm.create({publicKey: makeCredentialOptions}) 232 .then(arrivingHereIsBad) 233 .catch(expectNotSupportedError); 234 } 235 236 // Test with an unsupported parameter, and an unknown one 237 // The result depends on the tokens that are available, so we 238 // expect a NotAllowedError so as not to leak this. 239 async function test_one_unknown_one_unsupported_param() { 240 let makeCredentialOptions = { 241 rp, user, challenge: gCredentialChallenge, 242 pubKeyCredParams: [unsupportedParam, unknownParam] 243 }; 244 return credm.create({publicKey: makeCredentialOptions}) 245 .then(arrivingHereIsBad) 246 .catch(expectNotAllowedError); 247 } 248 249 // Test with an unsupported parameter, an unknown one, and a good one. This 250 // should succeed, as the unsupported and unknown should be ignored. 251 async function test_one_of_each_parameters() { 252 let makeCredentialOptions = { 253 rp, user, challenge: gCredentialChallenge, 254 pubKeyCredParams: [param, unsupportedParam, unknownParam] 255 }; 256 return credm.create({publicKey: makeCredentialOptions}) 257 .then(arrivingHereIsGood) 258 .catch(arrivingHereIsBad); 259 } 260 261 // Test without a challenge 262 async function test_without_challenge() { 263 let makeCredentialOptions = { 264 rp, user, pubKeyCredParams: [param] 265 }; 266 return credm.create({publicKey: makeCredentialOptions}) 267 .then(arrivingHereIsBad) 268 .catch(expectTypeError); 269 } 270 271 // Test with an invalid challenge 272 async function test_invalid_challenge() { 273 let makeCredentialOptions = { 274 rp, user, challenge: "begone, thou ill-fitting moist glove!", 275 pubKeyCredParams: [unsupportedParam] 276 }; 277 return credm.create({publicKey: makeCredentialOptions}) 278 .then(arrivingHereIsBad) 279 .catch(expectTypeError); 280 } 281 282 // Test with duplicate pubKeyCredParams 283 async function test_duplicate_pub_key() { 284 let makeCredentialOptions = { 285 rp, user, challenge: gCredentialChallenge, 286 pubKeyCredParams: [param, param, param] 287 }; 288 return credm.create({publicKey: makeCredentialOptions}) 289 .then(arrivingHereIsGood) 290 .catch(arrivingHereIsBad); 291 } 292 293 // Test with an RP ID that is not a valid domain string 294 async function test_invalid_rp_id() { 295 let rp1 = { id: document.domain + ":somejunk", name: "none"}; 296 let makeCredentialOptions = { 297 rp: rp1, user, challenge: gCredentialChallenge, pubKeyCredParams: [param] 298 }; 299 return credm.create({publicKey: makeCredentialOptions}) 300 .then(arrivingHereIsBad) 301 .catch(arrivingHereIsGood); 302 } 303 304 // Test with another RP ID that is not a valid domain string 305 async function test_invalid_rp_id_2() { 306 let rp1 = { id: document.domain + ":8888", name: "none"}; 307 let makeCredentialOptions = { 308 rp: rp1, user, challenge: gCredentialChallenge, pubKeyCredParams: [param] 309 }; 310 return credm.create({publicKey: makeCredentialOptions}) 311 .then(arrivingHereIsBad) 312 .catch(arrivingHereIsGood); 313 } 314 315 // Test with missing rp 316 async function test_missing_rp() { 317 let makeCredentialOptions = { 318 user, challenge: gCredentialChallenge, pubKeyCredParams: [param] 319 }; 320 return credm.create({publicKey: makeCredentialOptions}) 321 .then(arrivingHereIsBad) 322 .catch(expectTypeError); 323 } 324 325 // Test with incorrect user ID type 326 async function test_incorrect_user_id_type() { 327 let invalidType = {id: "a string, which is not a buffer", name: "none", displayName: "none"}; 328 let makeCredentialOptions = { 329 user: invalidType, challenge: gCredentialChallenge, pubKeyCredParams: [param] 330 }; 331 return credm.create({publicKey: makeCredentialOptions}) 332 .then(arrivingHereIsBad) 333 .catch(expectTypeError); 334 } 335 336 // Test with missing user 337 async function test_missing_user() { 338 let makeCredentialOptions = { 339 rp, challenge: gCredentialChallenge, pubKeyCredParams: [param] 340 }; 341 return credm.create({publicKey: makeCredentialOptions}) 342 .then(arrivingHereIsBad) 343 .catch(expectTypeError); 344 } 345 346 // Test a complete account 347 async function test_complete_account() { 348 // the icon fields are deprecated, but including them should not cause an error 349 let completeRP = {id: document.domain, name: "Foxxy Name", 350 icon: "https://example.com/fox.svg"}; 351 let completeUser = {id: string2buffer("foxes_are_the_best@example.com"), 352 name: "Fox F. Foxington", 353 icon: "https://example.com/fox.svg", 354 displayName: "Foxxy V"}; 355 let makeCredentialOptions = { 356 rp: completeRP, user: completeUser, challenge: gCredentialChallenge, 357 pubKeyCredParams: [param] 358 }; 359 return credm.create({publicKey: makeCredentialOptions}) 360 .then(arrivingHereIsGood) 361 .catch(arrivingHereIsBad); 362 } 363 364 // Test with too-large user ID buffer 365 async function test_too_large_user_id() { 366 let hugeUser = {id: new Uint8Array(65), 367 name: "Fox F. Foxington", 368 displayName: "Foxxy V"}; 369 let makeCredentialOptions = { 370 rp, user: hugeUser, challenge: gCredentialChallenge, 371 pubKeyCredParams: [param] 372 }; 373 return credm.create({publicKey: makeCredentialOptions}) 374 .then(arrivingHereIsBad) 375 .catch(expectTypeError); 376 } 377 378 // Test with excluding unknown transports 379 async function test_excluding_unknown_transports() { 380 let completeRP = {id: document.domain, name: "Foxxy Name"}; 381 let completeUser = {id: string2buffer("foxes_are_the_best@example.com"), 382 name: "Fox F. Foxington", 383 displayName: "Foxxy V"}; 384 let excludedUnknownTransport = {type: "public-key", 385 id: string2buffer("123"), 386 transports: ["unknown", "usb"]}; 387 let makeCredentialOptions = { 388 rp: completeRP, user: completeUser, challenge: gCredentialChallenge, 389 pubKeyCredParams: [param], excludeCredentials: [excludedUnknownTransport] 390 }; 391 return credm.create({publicKey: makeCredentialOptions}) 392 .then(arrivingHereIsGood) 393 .catch(arrivingHereIsBad); 394 } 395 396 async function test_unknown_attestation_type() { 397 let makeCredentialOptions = { 398 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param], 399 attestation: "unknown" 400 }; 401 return credm.create({publicKey: makeCredentialOptions }) 402 .then(arrivingHereIsGood) 403 .catch(arrivingHereIsBad); 404 } 405 406 async function test_unknown_selection_criteria() { 407 let makeCredentialOptions = { 408 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param], 409 authenticatorSelection: { 410 userVerificationRequirement: "unknown UV requirement", 411 authenticatorAttachment: "unknown authenticator attachment type" 412 }, 413 }; 414 return credm.create({publicKey: makeCredentialOptions }) 415 .then(arrivingHereIsGood) 416 .catch(arrivingHereIsBad); 417 } 418 419 async function test_no_unexpected_extensions() { 420 let makeCredentialOptions = { 421 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param], 422 }; 423 let cred = await credm.create({publicKey: makeCredentialOptions}).catch(arrivingHereIsBad); 424 let extensionResults = cred.getClientExtensionResults(); 425 is(extensionResults.credProps, undefined, "no credProps output"); 426 } 427 428 async function test_cred_props_with_rk_required() { 429 let makeCredentialOptions = { 430 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param], 431 authenticatorSelection: { 432 authenticatorAttachment: "cross-platform", 433 residentKey: "required", 434 }, 435 extensions: { credProps: true } 436 }; 437 let cred = await credm.create({publicKey: makeCredentialOptions}).catch(arrivingHereIsBad); 438 let extensionResults = cred.getClientExtensionResults(); 439 is(extensionResults.credProps?.rk, true, "rk is true"); 440 } 441 442 async function test_cred_props_with_rk_discouraged() { 443 let makeCredentialOptions = { 444 rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param], 445 authenticatorSelection: { 446 authenticatorAttachment: "cross-platform", 447 residentKey: "discouraged", 448 }, 449 extensions: { credProps: true } 450 }; 451 let cred = await credm.create({publicKey: makeCredentialOptions}).catch(arrivingHereIsBad); 452 let extensionResults = cred.getClientExtensionResults(); 453 is(extensionResults.credProps?.rk, false, "rk is false"); 454 } 455 456 </script> 457 458 </body> 459 </html>