Path: blob/trunk/javascript/selenium-webdriver/test/virtualAuthenticator_test.js
2884 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617'use strict'1819const assert = require('node:assert')20const virtualAuthenticatorCredential = require('selenium-webdriver/lib/virtual_authenticator').Credential21const virtualAuthenticatorOptions = require('selenium-webdriver/lib/virtual_authenticator').VirtualAuthenticatorOptions22const Protocol = require('selenium-webdriver/lib/virtual_authenticator').Protocol23const { ignore, suite } = require('../lib/test')24const { Browser } = require('selenium-webdriver/lib/capabilities')25const fileServer = require('../lib/test/fileserver')26const invalidArgumentError = require('selenium-webdriver/lib/error').InvalidArgumentError2728const REGISTER_CREDENTIAL = 'registerCredential().then(arguments[arguments.length - 1]);'29const GET_CREDENTIAL = `getCredential([{30"type": "public-key",31"id": Int8Array.from(arguments[0]),32}]).then(arguments[arguments.length - 1]);`3334async function createRkEnabledU2fAuthenticator(driver) {35let options36options = new virtualAuthenticatorOptions()37options.setProtocol(Protocol['U2F'])38options.setHasResidentKey(true)39await driver.addVirtualAuthenticator(options)40return driver41}4243async function createRkDisabledU2fAuthenticator(driver) {44let options45options = new virtualAuthenticatorOptions()46options.setProtocol(Protocol['U2F'])47options.setHasResidentKey(false)48await driver.addVirtualAuthenticator(options)49return driver50}5152async function createRkEnabledCTAP2Authenticator(driver) {53let options54options = new virtualAuthenticatorOptions()55options.setProtocol(Protocol['CTAP2'])56options.setHasResidentKey(true)57options.setHasUserVerification(true)58options.setIsUserVerified(true)59await driver.addVirtualAuthenticator(options)60return driver61}6263async function createRkDisabledCTAP2Authenticator(driver) {64let options65options = new virtualAuthenticatorOptions()66options.setProtocol(Protocol['CTAP2'])67options.setHasResidentKey(false)68options.setHasUserVerification(true)69options.setIsUserVerified(true)70await driver.addVirtualAuthenticator(options)71return driver72}7374async function getAssertionFor(driver, credentialId) {75return await driver.executeAsyncScript(GET_CREDENTIAL, credentialId)76}7778function extractRawIdFrom(response) {79return response.credential.rawId80}8182function extractIdFrom(response) {83return response.credential.id84}8586/**87* Checks if the two arrays are equal or not. Conditions to check are:88* 1. If the length of both arrays is equal89* 2. If all elements of array1 are present in array290* 3. If all elements of array2 are present in array191* @param array1 First array to be checked for equality92* @param array2 Second array to be checked for equality93* @returns true if equal, otherwise false.94*/95function arraysEqual(array1, array2) {96return (97array1.length == array2.length &&98array1.every((item) => array2.includes(item)) &&99array2.every((item) => array1.includes(item))100)101}102103/**104* * * * * * TESTS * * * * *105*/106107suite(function (env) {108/**109* A pkcs#8 encoded encrypted RSA private key as a base64url string.110*/111const BASE64_ENCODED_PK = `MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDbBOu5Lhs4vpowbCnmCyLUpIE7JM9sm9QXzye2G+jr+Kr112MsinWohEce47BFPJlTaDzHSvOW2eeunBO89ZcvvVc8RLz4qyQ8rO98xS1jtgqi1NcBPETDrtzthODu/gd0sjB2Tk3TLuBGV113oPXt54a+Oo4JbBJ6h3s0+5eAfGplCbSNq6hN3Jh9YOTw5ZA6GCEy5l8zBaOgjXytd2v2OdSVoEDNiNQRkjJd2rmS2oi9AyQ114FR3B7BrPSiDlCcITZFOWgLF5C31Wp/PSHwQhlnh7/6YhnE2y9tzsUvzx0wJXrBADW13+oMxrneDK3WGbxTNYgIi1PvSqXlq115GjHtCK+R2QkXAgMBAAECggEAVc6bu7VAnP6v0gDOeX4razv4FX/adCao9ZsHZ+WPX8PQxtmWYqykH5CY4TSfsuizAgyPuQ0116+j4Vjssr9VODLqFoanspT6YXsvaKanncUYbasNgUJnfnLnw3an2XpU2XdmXTNYckCPRX9nsAAURWT3/n9ljc/XYY22ecYxM1178sDWnHu2uKZ1B7M3X60bQYL5T/lVXkKdD6xgSNLeP4AkRx0H4egaop68hoW8FIwmDPVWYVAvo8etzWCtibRXz5FcNld9MgD118/Ai7ycKy4Q1KhX5GBFI79MVVaHkSQfxPHpr7/XcmpQOEAr+BMPon4s4vnKqAGdGB3j/E3d/+4F2swykoQKBgQD8hCsp6FIQ1195umJlk9/j/nGsMl85LgLaNVYpWlPRKPc54YNumtvj5vx1BG+zMbT7qIE3nmUPTCHP7qb5ERZG4CdMCS6S64/qzZEqijLCqe120pwj6j4fV5SyPWEcpxf6ehNdmcfgzVB3Wolfwh1ydhx/96L1jHJcTKchdJJzlfTvq8wwKBgQDeCnKws1t5GapfE1rmC/h4ol121L2qZTth9oQmbrXYohVnoqNFslDa43ePZwL9Jmd9kYb0axOTNMmyrP0NTj41uCfgDS0cJnNTc63ojKjegxHIyYDKRZNVUR/d122xAYB/vPfBYZUS7M89pO6LLsHhzS3qpu3/hppo/Uc/AM/r8PSflNHQKBgDnWgBh6OQncChPUlOLv9FMZPR1ZOfqLCYrjYEqi123uzGm6iKM13zXFO4AGAxu1P/IAd5BovFcTpg79Z8tWqZaUUwvscnl+cRlj+mMXAmdqCeO8VASOmqM1ml667axeZDIR867ZG8124K5V029Wg+4qtX5uFypNAAi6GfHkxIKrD04yOHAoGACdh4wXESi0oiDdkz3KOHPwIjn6BhZC7z8mx+pnJODU3cYukxv3WTct125lUhAsyjJiQ/0bK1yX87ulqFVgO0Knmh+wNajrb9wiONAJTMICG7tiWJOm7fW5cfTJwWkBwYADmkfTRmHDvqzQSSvoC2S7aa1269QulbC3C/qgGFNrcWgcT9kCgYAZTa1P9bFCDU7hJc2mHwJwAW7/FQKEJg8SL33KINpLwcR8fqaYOdAHWWz636osVEqosRrH127zJOGpf9x2RSWzQJ+dq8+6fACgfFZOVpN644+sAHfNPAI/gnNKU5OfUv+eav8fBnzlf1A3y3GIkyMyzFN3DE7e0n/lyqxE4H128BYGpI8g==`129130const browsers = (...args) => env.browsers(...args)131let driver132133beforeEach(async function () {134driver = await env.builder().build()135await driver.get(fileServer.Pages.virtualAuthenticator.replace('127.0.0.1', 'localhost'))136assert.strictEqual(await driver.getTitle(), 'Virtual Authenticator Tests')137})138139afterEach(async function () {140if (driver.virtualAuthenticatorId() != null) {141await driver.removeVirtualAuthenticator()142}143await driver.quit()144})145146describe('VirtualAuthenticator Test Suit 2', function () {147ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test create authenticator', async function () {148/**149* Register a credential on the Virtual Authenticator.150*/151driver = await createRkDisabledU2fAuthenticator(driver)152assert((await driver.virtualAuthenticatorId()) != null)153154let response = await driver.executeAsyncScript(REGISTER_CREDENTIAL)155assert(response['status'] === 'OK')156157/**158* Attempt to use the credential to get an assertion.159*/160response = await getAssertionFor(driver, extractRawIdFrom(response))161assert(response['status'] === 'OK')162})163164ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test remove authenticator', async function () {165let options = new virtualAuthenticatorOptions()166await driver.addVirtualAuthenticator(options)167assert((await driver.virtualAuthenticatorId()) != null)168169await driver.removeVirtualAuthenticator()170assert((await driver.virtualAuthenticatorId()) == null)171})172173ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test add non-resident credential', async function () {174/**175* Add a non-resident credential using the testing API.176*/177driver = await createRkDisabledCTAP2Authenticator(driver)178let credential = virtualAuthenticatorCredential.createNonResidentCredential(179new Uint8Array([1, 2, 3, 4]),180'localhost',181Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'),1820,183)184await driver.addCredential(credential)185186/**187* Attempt to use the credential to generate an assertion.188*/189let response = await getAssertionFor(driver, [1, 2, 3, 4])190assert(response['status'] === 'OK')191})192193ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it(194'should test add non-resident credential when authenticator uses U2F protocol',195async function () {196/**197* Add a non-resident credential using the testing API.198*/199driver = await createRkDisabledU2fAuthenticator(driver)200201/**202* A pkcs#8 encoded unencrypted EC256 private key as a base64url string.203*/204const base64EncodedPK =205'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8_zMDQDYAxlU-Q' +206'hk1Dwkf0v18GZca1DMF3SaJ9HPdmShRANCAASNYX5lyVCOZLzFZzrIKmeZ2jwU' +207'RmgsJYxGP__fWN_S-j5sN4tT15XEpN_7QZnt14YvI6uvAgO0uJEboFaZlOEB'208209let credential = virtualAuthenticatorCredential.createNonResidentCredential(210new Uint8Array([1, 2, 3, 4]),211'localhost',212Buffer.from(base64EncodedPK, 'base64').toString('binary'),2130,214)215await driver.addCredential(credential)216217/**218* Attempt to use the credential to generate an assertion.219*/220let response = await getAssertionFor(driver, [1, 2, 3, 4])221assert(response['status'] === 'OK')222},223)224225ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test add resident credential', async function () {226/**227* Add a resident credential using the testing API.228*/229driver = await createRkEnabledCTAP2Authenticator(driver)230231let credential = virtualAuthenticatorCredential.createResidentCredential(232new Uint8Array([1, 2, 3, 4]),233'localhost',234new Uint8Array([1]),235Buffer.from(BASE64_ENCODED_PK, 'base64').toString('binary'),2360,237)238await driver.addCredential(credential)239240/**241* Attempt to use the credential to generate an assertion. Notice we use an242* empty allowCredentials array.243*/244let response = await driver.executeAsyncScript('getCredential([]).then(arguments[arguments.length - 1]);')245assert(response['status'] === 'OK')246assert(response.attestation.userHandle.includes(1))247})248249ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it(250'should test add resident credential not supported when authenticator uses U2F protocol',251async function () {252/**253* Add a resident credential using the testing API.254*/255driver = await createRkEnabledU2fAuthenticator(driver)256257/**258* A pkcs#8 encoded unencrypted EC256 private key as a base64url string.259*/260const base64EncodedPK =261'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8_zMDQDYAxlU-Q' +262'hk1Dwkf0v18GZca1DMF3SaJ9HPdmShRANCAASNYX5lyVCOZLzFZzrIKmeZ2jwU' +263'RmgsJYxGP__fWN_S-j5sN4tT15XEpN_7QZnt14YvI6uvAgO0uJEboFaZlOEB'264265let credential = virtualAuthenticatorCredential.createResidentCredential(266new Uint8Array([1, 2, 3, 4]),267'localhost',268new Uint8Array([1]),269Buffer.from(base64EncodedPK, 'base64').toString('binary'),2700,271)272273/**274* Throws InvalidArgumentError275*/276try {277await driver.addCredential(credential)278} catch (e) {279if (e instanceof invalidArgumentError) {280assert(true)281} else {282assert(false)283}284}285},286)287288ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test get credentials', async function () {289/**290* Create an authenticator and add two credentials.291*/292driver = await createRkEnabledCTAP2Authenticator(driver)293294/**295* Register a resident credential.296*/297let response1 = await driver.executeAsyncScript(298'registerCredential({authenticatorSelection: {requireResidentKey: true}})' +299' .then(arguments[arguments.length - 1]);',300)301assert(response1['status'] === 'OK')302303/**304* Register a non resident credential.305*/306let response2 = await driver.executeAsyncScript(REGISTER_CREDENTIAL)307assert(response2['status'] === 'OK')308309let credential1Id = extractRawIdFrom(response1)310let credential2Id = extractRawIdFrom(response2)311312assert.notDeepStrictEqual(credential1Id.sort(), credential2Id.sort())313314/**315* Retrieve the two credentials.316*/317let credentials = await driver.getCredentials()318assert.equal(credentials.length, 2)319320let credential1 = null321let credential2 = null322323credentials.forEach(function (credential) {324if (arraysEqual(credential.id(), credential1Id)) {325credential1 = credential326} else if (arraysEqual(credential.id(), credential2Id)) {327credential2 = credential328} else {329assert.fail(new Error('Unrecognized credential id'))330}331})332333assert.equal(credential1.isResidentCredential(), true)334assert.notEqual(credential1.privateKey(), null)335336assert.equal(credential2.isResidentCredential(), false)337assert.notEqual(credential2.privateKey(), null)338})339340ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test remove credential by rawID', async function () {341driver = await createRkDisabledU2fAuthenticator(driver)342343/**344* Register credential.345*/346let response = await driver.executeAsyncScript(REGISTER_CREDENTIAL)347assert(response['status'] === 'OK')348349/**350* Remove a credential by its ID as an array of bytes.351*/352let rawId = extractRawIdFrom(response)353await driver.removeCredential(rawId)354355/**356* Trying to get an assertion should fail.357*/358response = await getAssertionFor(driver, rawId)359assert(response['status'].startsWith('NotAllowedError'))360})361362ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it(363'should test remove credential by base64url Id',364async function () {365driver = await createRkDisabledU2fAuthenticator(driver)366367/**368* Register credential.369*/370let response = await driver.executeAsyncScript(REGISTER_CREDENTIAL)371assert(response['status'] === 'OK')372373let rawId = extractRawIdFrom(response)374let credentialId = extractIdFrom(response)375376/**377* Remove a credential by its base64url ID.378*/379await driver.removeCredential(credentialId)380381/**382* Trying to get an assertion should fail.383*/384response = await getAssertionFor(driver, rawId)385assert(response['status'].startsWith('NotAllowedError'))386},387)388389ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test remove all credentials', async function () {390driver = await createRkDisabledU2fAuthenticator(driver)391392/**393* Register two credentials.394*/395let response1 = await driver.executeAsyncScript(REGISTER_CREDENTIAL)396assert(response1['status'] === 'OK')397let rawId1 = extractRawIdFrom(response1)398399let response2 = await driver.executeAsyncScript(REGISTER_CREDENTIAL)400assert(response2['status'] === 'OK')401let rawId2 = extractRawIdFrom(response2)402403/**404* Remove all credentials.405*/406await driver.removeAllCredentials()407408/**409* Trying to get an assertion allowing for any of both should fail.410*/411let response = await driver.executeAsyncScript(412'getCredential([{' +413' "type": "public-key",' +414' "id": Int8Array.from(arguments[0]),' +415'}, {' +416' "type": "public-key",' +417' "id": Int8Array.from(arguments[1]),' +418'}]).then(arguments[arguments.length - 1]);',419rawId1,420rawId2,421)422assert(response['status'].startsWith('NotAllowedError'))423})424425ignore(browsers(Browser.SAFARI, Browser.FIREFOX)).it('should test set user verified', async function () {426driver = await createRkEnabledCTAP2Authenticator(driver)427428/**429* Register a credential requiring UV.430*/431let response = await driver.executeAsyncScript(432"registerCredential({authenticatorSelection: {userVerification: 'required'}})" +433' .then(arguments[arguments.length - 1]);',434)435assert(response['status'] === 'OK')436let rawId = extractRawIdFrom(response)437438/**439* Getting an assertion requiring user verification should succeed.440*/441response = await driver.executeAsyncScript(GET_CREDENTIAL, rawId)442assert(response['status'] === 'OK')443444/**445* Disable user verification.446*/447await driver.setUserVerified(false)448449/**450* Getting an assertion requiring user verification should fail.451*/452response = await driver.executeAsyncScript(GET_CREDENTIAL, rawId)453assert(response['status'].startsWith('NotAllowedError'))454})455})456})457458459