/* * Copyright (C) 2023 Yubico. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @Tags(['desktop', 'piv']) library; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:yubico_authenticator/app/views/keys.dart'; import 'package:yubico_authenticator/core/state.dart'; import 'package:yubico_authenticator/piv/keys.dart'; import 'utils/piv_test_util.dart'; import 'utils/test_util.dart'; void main() { var binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive; group('PIV Settings', skip: isAndroid, () { const factoryPin = '123456'; const factoryPuk = '12345678'; appTest('Reset PIV (settings-init)', (WidgetTester tester) async { await tester.resetPiv(); await tester.shortWait(); }); appTest('Lock PIN, unlock with PUK', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(managePinAction).hitTestable()); await tester.shortWait(); await tester.lockPinPuk(); await tester.shortWait(); await tester.tap(find.byKey(actionsIconButtonKey).hitTestable()); /// TODO: This expect needs to verify that Pin subtitle is 'Blocked' /// expect(find.byKey(managePinAction), find.byTooltip('Blocked')); await tester.shortWait(); await tester.tap(find.byKey(managePinAction).hitTestable()); await tester.shortWait(); await tester.enterText(find.byKey(pinPukField).hitTestable(), factoryPuk); await tester.shortWait(); await tester.enterText( find.byKey(newPinPukField).hitTestable(), factoryPin); await tester.shortWait(); await tester.enterText( find.byKey(confirmPinPukField).hitTestable(), factoryPin); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.shortWait(); await tester.sendKeyEvent(LogicalKeyboardKey.escape); await tester.longWait(); await tester.resetPiv(); }); appTest('Lock PUK, lock PIN, factory reset', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(managePukAction).hitTestable()); await tester.shortWait(); await tester.lockPinPuk(); await tester.shortWait(); await tester.tap(find.byKey(actionsIconButtonKey).hitTestable()); /// TODO: This expect needs to verify that PUK underline is 'Blocked' /// expect(find.byKey(managePukAction), find.byTooltip('Blocked')); await tester.shortWait(); await tester.tap(find.byKey(managePinAction).hitTestable()); await tester.shortWait(); await tester.lockPinPuk(); await tester.shortWait(); await tester.tap(find.byKey(actionsIconButtonKey).hitTestable()); /// TODO: This expect needs to verify that Pin underline is 'Blocked' /// expect(find.byKey(managePinAction), find.byTooltip('Blocked')); await tester.shortWait(); await tester.tap(find.byKey(managePinAction).hitTestable()); await tester.shortWait(); await tester.tap(find.byKey(managePukAction).hitTestable()); await tester.shortWait(); await tester.sendKeyEvent(LogicalKeyboardKey.escape); await tester.longWait(); await tester.resetPiv(); }); appTest('Change PIN', (WidgetTester tester) async { const newpin = '123123'; await tester.configurePiv(); await tester.tap(find.byKey(managePinAction).hitTestable()); await tester.shortWait(); await tester.enterText(find.byKey(pinPukField).hitTestable(), factoryPin); await tester.shortWait(); await tester.enterText(find.byKey(newPinPukField).hitTestable(), newpin); await tester.shortWait(); await tester.enterText( find.byKey(confirmPinPukField).hitTestable(), newpin); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); await tester.configurePiv(); await tester.tap(find.byKey(managePinAction).hitTestable()); await tester.shortWait(); await tester.enterText(find.byKey(pinPukField).hitTestable(), newpin); await tester.shortWait(); await tester.enterText( find.byKey(newPinPukField).hitTestable(), factoryPin); await tester.shortWait(); await tester.enterText( find.byKey(confirmPinPukField).hitTestable(), factoryPin); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); }); appTest('Change PUK', (WidgetTester tester) async { const newpuk = '12341234'; await tester.configurePiv(); await tester.tap(find.byKey(managePukAction).hitTestable()); await tester.shortWait(); await tester.enterText(find.byKey(pinPukField).hitTestable(), factoryPuk); await tester.shortWait(); await tester.enterText(find.byKey(newPinPukField).hitTestable(), newpuk); await tester.shortWait(); await tester.enterText( find.byKey(confirmPinPukField).hitTestable(), newpuk); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); await tester.configurePiv(); await tester.tap(find.byKey(managePukAction).hitTestable()); await tester.shortWait(); await tester.enterText(find.byKey(pinPukField).hitTestable(), newpuk); await tester.shortWait(); await tester.enterText( find.byKey(newPinPukField).hitTestable(), factoryPuk); await tester.shortWait(); await tester.enterText( find.byKey(confirmPinPukField).hitTestable(), factoryPuk); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); }); group('PIV Management Key', () { const newmanagementkey = 'aaaabbbbccccaaaabbbbccccaaaabbbbccccaaaabbbbcccc'; const boundsmanagementkey = 'llllkkkkmmmmllllkkkkmmmmllllkkkkmmmmllllkkkkmmmm'; const shortmanagementkey = 'aaaabbbbccccaaaabbbbccccaaaabbbbccccaaaabbbbccc'; appTest('Out of bounds managementkey key', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(manageManagementKeyAction).hitTestable()); await tester.longWait(); // testing out of bounds management key does not work await tester.enterText( find.byKey(newPinPukField).hitTestable(), boundsmanagementkey); await tester.longWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); expect(tester.isTextButtonEnabled(saveButton), true); // TODO assert that errorText and errorIcon are shown }); appTest('Short managementkey key', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(manageManagementKeyAction).hitTestable()); await tester.longWait(); // testing too short management key does not work await tester.enterText( find.byKey(newPinPukField).hitTestable(), shortmanagementkey); await tester.longWait(); expect(tester.isTextButtonEnabled(saveButton), false); }); appTest('Change managementkey key', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(manageManagementKeyAction).hitTestable()); await tester.shortWait(); // setting newmanagementkey await tester.enterText( find.byKey(newPinPukField).hitTestable(), newmanagementkey); await tester.longWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); // verifying newmanagementkey await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(manageManagementKeyAction).hitTestable()); await tester.shortWait(); await tester.enterText( find.byKey(managementKeyField).hitTestable(), newmanagementkey); await tester.shortWait(); await tester.enterText( find.byKey(newPinPukField).hitTestable(), newmanagementkey); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); await tester.resetPiv(); }); appTest('Change managementkey type', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(manageManagementKeyAction).hitTestable()); await tester.shortWait(); // TODO: this needs to use manageManagementKeyAction chip await tester.enterText( find.byKey(newPinPukField).hitTestable(), newmanagementkey); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); await tester.resetPiv(); await tester.shortWait(); }); appTest('Change managementkey PIN-lock', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(manageManagementKeyAction).hitTestable()); await tester.shortWait(); // testing out of bounds management key does not work await tester.enterText( find.byKey(newPinPukField).hitTestable(), newmanagementkey); await tester.shortWait(); // TODO: Investigate why chip-tap fails //await tester.tap(find.byKey(pinLockManagementKeyChip).hitTestable()); //await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); await tester.resetPiv(); await tester.shortWait(); }); appTest('Random managementkeytype', (WidgetTester tester) async { await tester.configurePiv(); await tester.shortWait(); await tester.tap(find.byKey(manageManagementKeyAction).hitTestable()); await tester.shortWait(); // rndm 3x, for luck await tester.tap(find.byKey(managementKeyRefresh).hitTestable()); await tester.shortWait(); await tester.tap(find.byKey(managementKeyRefresh).hitTestable()); await tester.shortWait(); await tester.tap(find.byKey(managementKeyRefresh).hitTestable()); await tester.shortWait(); await tester.enterText( find.byKey(newPinPukField).hitTestable(), newmanagementkey); await tester.shortWait(); await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); await tester.resetPiv(); }); appTest('Reset PIV (settings-exit)', (WidgetTester tester) async { await tester.resetPiv(); await tester.shortWait(); }); }); }); // Distinguished name schema according to RFC 4514 // https://www.ietf.org/rfc/rfc4514.txt // CN commonName (2.5.4.3) // L localityName (2.5.4.7) // ST stateOrProvinceName (2.5.4.8) // O organizationName (2.5.4.10) // OU organizationalUnitName (2.5.4.11) // C countryName (2.5.4.6) // STREET streetAddress (2.5.4.9) // DC domainComponent (0.9.2342.19200300.100.1.25) // UID userId (0.9.2342.19200300.100.1.1) // Example: CN=cn,L=l,ST=st,O=o,OU=ou,C=c,STREET=street,DC=dc,DC=net,UID=uid group('PIV Certificate load', skip: isAndroid, () { appTest('Reset PIV (load-init)', (WidgetTester tester) async { await tester.resetPiv(); }); appTest('Generate 9a', (WidgetTester tester) async { // 1. open PIV view var pivDrawerButton = find.byKey(pivAppDrawer).hitTestable(); await tester.tap(pivDrawerButton); await tester.longWait(); // 2. click meatball menu for 9a await tester.tap(find.byKey(meatballButton9a).hitTestable()); await tester.longWait(); // 3. click generate await tester.tap(find.byKey(generateAction).hitTestable()); await tester.longWait(); // 4. enter PIN and click Unlock await tester.enterText( find.byKey(managementKeyField).hitTestable(), '123456'); await tester.longWait(); await tester.tap(find.byKey(unlockButton).hitTestable()); await tester.longWait(); // 5. Enter DN await tester.enterText( find.byKey(subjectField).hitTestable(), 'CN=Generate9a'); await tester.longWait(); // 6. Change algorithm: RSA1024 // 7. Date [unchanged] // 8. click save await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); // 9 Verify Subject, verify Date /* expect(find.byWidgetPredicate((widget) { if (widget is TooltipIfTruncated) { final TooltipIfTruncated textWidget = widget; if (textWidget.key == certInfoSubjectKey && textWidget.text == 'CN=Generate9a') { return true; } } return false; }), findsOneWidget);*/ await tester.pump(const Duration(milliseconds: 5000)); // 10. Export Certificate // await tester.tap(find.byKey(exportAction).hitTestable()); // await tester.enterText( // find.byKey($$Save as$$).hitTestable(), 'Generate9a'); // await tester.tap(find.byKey($$Save button$$).hitTestable()); // await tester.longWait(); // 11. Delete Certificate await tester.tap(find.byKey(deleteAction).hitTestable()); await tester.longWait(); await tester.tap(find.byKey(deleteButton).hitTestable()); await tester.longWait(); }); appTest('Generate 9c', (WidgetTester tester) async { var pivDrawerButton = find.byKey(pivAppDrawer).hitTestable(); await tester.tap(pivDrawerButton); await tester.longWait(); // 2. click meatball menu for 9c await tester.tap(find.byKey(meatballButton9c).hitTestable()); await tester.longWait(); // 3. click generate await tester.tap(find.byKey(generateAction).hitTestable()); await tester.longWait(); // 4. enter PIN and click Unlock await tester.enterText( find.byKey(managementKeyField).hitTestable(), '123456'); await tester.longWait(); await tester.tap(find.byKey(unlockButton).hitTestable()); await tester.longWait(); // 5. Enter DN await tester.enterText( find.byKey(subjectField).hitTestable(), 'CN=Generate9c'); await tester.longWait(); // 6. Change algorithm: RSA2048 // 7. set date // 8. click save await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); // 9 Verify Subject, verify Date // TODO: this seems not to work! // expect(find.byWidgetPredicate((widget) { // if (widget is TooltipIfTruncated) { // final TooltipIfTruncated textWidget = widget; // if (textWidget.key == certInfoSubjectKey && // textWidget.text == 'CN=Generate9c') { // return true; // } // } // return false; // }), findsOneWidget); await tester.longWait(); // 10. Delete Certificate await tester.tap(find.byKey(deleteAction).hitTestable()); await tester.longWait(); await tester.tap(find.byKey(deleteButton).hitTestable()); await tester.longWait(); }); appTest('Generate 9d', (WidgetTester tester) async { // 1. open PIV view var pivDrawerButton = find.byKey(pivAppDrawer).hitTestable(); await tester.tap(pivDrawerButton); await tester.longWait(); // 2. click meatball menu for 9d await tester.tap(find.byKey(meatballButton9d).hitTestable()); await tester.longWait(); // 3. click generate await tester.tap(find.byKey(generateAction).hitTestable()); await tester.longWait(); // 4. enter PIN and click Unlock await tester.enterText( find.byKey(managementKeyField).hitTestable(), '123456'); await tester.longWait(); await tester.tap(find.byKey(unlockButton).hitTestable()); await tester.longWait(); // 5. Enter DN await tester.enterText( find.byKey(subjectField).hitTestable(), 'CN=Generate9d'); await tester.longWait(); // 6. Change algorithm: ECCP256 // 7. Date [unchanged] // 8. click save await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); // 9 Verify Subject, verify Date /* expect(find.byWidgetPredicate((widget) { if (widget is TooltipIfTruncated) { final TooltipIfTruncated textWidget = widget; if (textWidget.key == certInfoSubjectKey && textWidget.text == 'CN=Generate9d') { return true; } } return false; }), findsOneWidget);*/ await tester.longWait(); // 10. Export Certificate // await tester.tap(find.byKey(exportAction).hitTestable()); // await tester.enterText( // find.byKey($$Save as$$).hitTestable(), 'Generate9d'); // await tester.tap(find.byKey($$Save button$$).hitTestable()); // await tester.longWait(); // 11. Delete Certificate await tester.tap(find.byKey(deleteAction).hitTestable()); await tester.longWait(); await tester.tap(find.byKey(deleteButton).hitTestable()); await tester.longWait(); }); appTest('Generate 9e', (WidgetTester tester) async { // 1. open PIV view var pivDrawerButton = find.byKey(pivAppDrawer).hitTestable(); await tester.tap(pivDrawerButton); await tester.longWait(); // 2. click meatball menu for 9e await tester.tap(find.byKey(meatballButton9e).hitTestable()); await tester.longWait(); // 3. click generate await tester.tap(find.byKey(generateAction).hitTestable()); await tester.longWait(); // 4. enter PIN and click Unlock await tester.enterText( find.byKey(managementKeyField).hitTestable(), '123456'); await tester.longWait(); await tester.tap(find.byKey(unlockButton).hitTestable()); await tester.longWait(); // 5. Enter DN await tester.enterText( find.byKey(subjectField).hitTestable(), 'CN=Generate9e'); await tester.longWait(); // 6. Change algorithm: ECCP384 // 7. Date [unchanged] // 8. click save await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); // 9 Verify Subject, verify Date /* expect(find.byWidgetPredicate((widget) { if (widget is TooltipIfTruncated) { final TooltipIfTruncated textWidget = widget; if (textWidget.key == certInfoSubjectKey && textWidget.text == 'CN=Generate9e') { return true; } } return false; }), findsOneWidget);*/ await tester.longWait(); // 10. Export Certificate // await tester.tap(find.byKey(exportAction).hitTestable()); // await tester.enterText( // find.byKey($$Save as$$).hitTestable(), 'Generate9e'); // await tester.tap(find.byKey($$Save button$$).hitTestable()); // await tester.longWait(); // 11. Delete Certificate await tester.tap(find.byKey(deleteAction).hitTestable()); await tester.longWait(); await tester.tap(find.byKey(deleteButton).hitTestable()); await tester.longWait(); }); appTest('Import outdated Key+Certificate from file', (WidgetTester tester) async {}); /// TODO fileload needs to be handled appTest('Import neverexpire Key+Certificate from file', (WidgetTester tester) async { /// TODO fileload needs to be handled }); appTest('Generate a CSR', (WidgetTester tester) async { // 1. open PIV view var pivDrawerButton = find.byKey(pivAppDrawer).hitTestable(); await tester.tap(pivDrawerButton); await tester.longWait(); // 2. click meatball menu for 9e await tester.tap(find.byKey(meatballButton9e).hitTestable()); await tester.longWait(); // 3. click generate await tester.tap(find.byKey(generateAction).hitTestable()); await tester.longWait(); // 4. enter PIN and click Unlock await tester.enterText( find.byKey(managementKeyField).hitTestable(), '123456'); await tester.longWait(); await tester.tap(find.byKey(unlockButton).hitTestable()); await tester.longWait(); // 5. Enter DN await tester.enterText( find.byKey(subjectField).hitTestable(), 'CN=Generate9e-CSR'); await tester.longWait(); // 6. Change 'output format': CSR // enum models.dart, generate_key_dialog.dart // 7. Choose File Name > Save As > 'File Name generate93-csr' // TODO: where are files saved? // 8. click save await tester.tap(find.byKey(saveButton).hitTestable()); await tester.longWait(); // 9 Verify 'No certificate loaded' /* expect(find.byWidgetPredicate((widget) { if (widget is TooltipIfTruncated) { final TooltipIfTruncated textWidget = widget; if (textWidget.key == certInfoSubjectKey && textWidget.text == 'CN=Generate9e') { return true; } } return false; }), findsOneWidget);*/ }); // appTest('Reset PIV (load-exit)', (WidgetTester tester) async { // /// TODO: investigate why this reset randomly fails! // await tester.resetPiv(); // await tester.shortWait(); // }); }); }