diff --git a/AndroidDemo/build.gradle b/AndroidDemo/build.gradle index 23268496..2dfcf0c9 100755 --- a/AndroidDemo/build.gradle +++ b/AndroidDemo/build.gradle @@ -59,7 +59,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1' implementation 'androidx.core:core-ktx:1.12.0' - implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.fragment:fragment-ktx:1.6.2' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.3.2' @@ -71,7 +71,7 @@ dependencies { implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" // Navigation - def nav_version = '2.7.4' + def nav_version = '2.7.5' implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version" diff --git a/NEWS b/NEWS index 50ddc91b..cc0538fa 100644 --- a/NEWS +++ b/NEWS @@ -1,67 +1,79 @@ +* Version 2.4.0 (released 2023-11-21) + ** fido module (new): + - support for WebAuthn Level 2 + - support for CTAP2.1 features: Credential Management, Client PIN, Config, Enterprise Attestation + ** openpgp module (new): + - PIN operations: (un)verification, user and admin password management + - key operations: import/generate, sign/verify, encrypt/decrypt + ** android module: + - targetSdk is now 34 (Android 14) + ** core module: + - added support for Le in APDU + - added PublicKeyValues and PrivateKeyValues classes for unified handling of asymmetric keys + ** PIV module: + - deprecated classes: InvalidPinException, Padding + - deprecated methods: SlotMetadata.getPublicKey(), PivSession.generateKey(), PivSession.putKey() + * Version 2.4.0-beta01 (released 2023-09-01) ** core module: - *** added support for Le in APDU - *** added PublicKeyValues and PrivateKeyValues classes for unified handling of asymmetric keys + - added support for Le in APDU + - added PublicKeyValues and PrivateKeyValues classes for unified handling of asymmetric keys ** PIV module: - *** deprecated classes: InvalidPinException, Padding - *** deprecated methods: SlotMetadata.getPublicKey(), PivSession.generateKey(), PivSession.putKey() - ** fido module: - *** added support for WebAuthn and CTAP2 + - deprecated classes: InvalidPinException, Padding + - deprecated methods: SlotMetadata.getPublicKey(), PivSession.generateKey(), PivSession.putKey() + ** fido module (new): + - added initial support for WebAuthn and CTAP2 * Version 2.3.0 (released 2023-05-29) ** core module: - *** deprecated `com.yubico.yubikit.core.Logger` - *** added `YubiKeyDevice.openConnection()` to public API + - deprecated `com.yubico.yubikit.core.Logger` + - added `YubiKeyDevice.openConnection()` to public API ** android module: - *** updated code to support Android 14 behavior changes + - updated code to support Android 14 behavior changes ** PIV module: - *** implemented support for compressed certificates + - implemented support for compressed certificates ** general updates: - *** adapted for use with slf4j logging system - *** added support for building with Java 17 - *** updated build dependencies and libraries + - adapted for use with slf4j logging system + - added support for building with Java 17 + - updated build dependencies and libraries * Version 2.2.0 (released 2023-01-17) ** core module: - *** Added a public class containing YubiKey smartcard application ids (core.smartcard.AppId) + - Added a public class containing YubiKey smartcard application ids (core.smartcard.AppId) ** android module: - *** Added SmartCardConnection.getAtr() and USB and NFC implementations for getting ATR and ATS - *** Updated dependency versions, compile and build with latest SDKs - *** Bug fixes related to Android 13 - *** Bug fixes related to communication over NFC - *** All library resources are now prefixed with 'yubikit_' prefix (thanks to @ajarl for contribution) - *** Library resources which are available to override by clients are explicitly marked as public + - Added SmartCardConnection.getAtr() and USB and NFC implementations for getting ATR and ATS + - Updated dependency versions, compile and build with latest SDKs + - Bug fixes related to Android 13 + - Bug fixes related to communication over NFC + - All library resources are now prefixed with 'yubikit_' prefix (thanks to @ajarl for contribution) + - Library resources which are available to override by clients are explicitly marked as public ** support module: - *** Added a helper method NfcYubiKeyDevice.isYubiKey() for probing NFC devices - *** Updated DeviceUtil.getName() to supports additional hardware security keys by Yubico + - Added a helper method NfcYubiKeyDevice.isYubiKey() for probing NFC devices + - Updated DeviceUtil.getName() to supports additional hardware security keys by Yubico ** AndroidDemo module: - *** bug fixes and improvements + - bug fixes and improvements * Version 2.1.0 (released 2022-07-25) - ** Added a new support module with utility functions for getting device - information/metadata. + ** Added a new support module with utility functions for getting device information/metadata. ** PIV: - *** Added a JCA Provider implementation. - *** Added Slot getStringAlias and fromStringAlias methods. + - Added a JCA Provider implementation. + - Added Slot getStringAlias and fromStringAlias methods. ** OATH: - *** Fixed OathSession.calculateCodes so that it never triggers touch. + - Fixed OathSession.calculateCodes so that it never triggers touch. ** Management: - *** DeviceInfo now provides isFips and isSky properties. + - DeviceInfo now provides isFips and isSky properties. ** Deprecations (will be removed in 3.0.0): - *** PivSession.sign has been deprecated in favor of using the JCA Provider. - *** OathSession.hasAccessKey has been deprecated and replaced with - OathSession.isAccessKeySet. - *** UsbInterface in the management module has been deprecated, replaced with - UsbInterface in the core module. + - PivSession.sign has been deprecated in favor of using the JCA Provider. + - OathSession.hasAccessKey has been deprecated and replaced with OathSession.isAccessKeySet. + - UsbInterface in the management module has been deprecated, replaced with UsbInterface in + the core module. ** Added testing-android module which can run tests on physical devices - *** currently Piv and Piv Jca tests are implemented + - currently Piv and Piv Jca tests are implemented ** Bug fixes and improvements - * Version 2.1.0-alpha.1 (released 2022-06-01) ** Public preview of 2.1.0 - * Version 2.0.0 (released 2021-04-01) ** BACKWARDS INCOMPATIBLE: Major structural overhaul from 1.0. ** The yubikit module is replaced by the core and android modules. @@ -69,17 +81,14 @@ ** Several classes have been renamed and/or moved. ** Connection handling is now asynchronous. - * Version 2.0.0-beta02 (released 2021-02-24) ** Connection handling is now asynchronous. ** YubiKitManager listeners have been replaced with more generic Callbacks. ** Release artifacts are compiled to run on Java 8. - * Version 2.0.0-beta01 (released 2020-12-07) ** Public preview of 2.0.0, a major restructuring of modules. - * Version 1.0.0 (released 2020-06-10) ** Documentation improvements. ** Removal of unused code and properties. @@ -87,7 +96,6 @@ ** yubikit: ATR is read upon opening an Iso7816Connection (USB). ** otp: The KeyListener interface is no longer public. - * Version 1.0.0-beta06 (released 2020-05-08) ** Various naming changes to classes and methods to better represent what they do. ** Various additional refactorings and minor changes to improve readability and consistency. @@ -99,7 +107,6 @@ ** FIDO2 module removed. ** 'Smartcard demo' removed. - * Version 1.0.0-beta05 (released 2020-03-31) ** yubikit: Provides callback to users on whether permissions (for USB plug-in device) from user were accepted or denied. ** yubikit: Provides configurations mechanism for NFC discovery (e.g. play sound, read NDEF tag, etc.). @@ -109,21 +116,17 @@ ** piv: Fixing PIV signing (issue with RSA PKCS1.15 padding). ** fido: Allow launching of FIDO intents from fragment as well as from activity. - * Version 1.0.0-beta04 (released 2020-02-06) ** Added YubiKey configuration capabilities, programming OTP slots. ** HMAC-SHA1 challenge-response. - * Version 1.0.0-beta03 (released 2019-10-15) ** Making QR/play-services-vision dependency optional for OATH module. - * Version 1.0.0-beta02 (released 2019-10-04) ** Smart Card functionality based on the Personal Identity Verification (PIV) interface. ** Management API to enable/disable interfaces on YubiKey. - * Version 1.0.0-beta01 (released 2019-08-06) ** Supports raw APDU communication with YubiKey over NFC and USB. ** Provides high level API for OATH applet. diff --git a/README.adoc b/README.adoc index 33cc5dc1..5a3e71b3 100644 --- a/README.adoc +++ b/README.adoc @@ -36,7 +36,7 @@ This module provides concrete implementations for the interfaces in *core*, the various reusable UI elements. link:./fido/[Fido]:: -This module adds FIDO2 support. Current implementation supports Webauthn Level 2 and CTAP 2.0 for managing FIDO credentials on YubiKeys. +This module adds FIDO2 support. Current implementation supports Webauthn Level 2 and CTAP 2.1 for managing FIDO credentials on YubiKeys. link:./management/[Management]:: This module provides the ability to read out metadata from a YubiKey, such as @@ -48,6 +48,10 @@ This module lets you configure the YubiOTP application. This includes configuring the two "keyboard slots", and using the Challenge-Response functionality. +link:./openpgp/[OpenPGP]:: +This module lets you use the OpenPGP smart card application on a YubiKey and execute operations such as OpenPGP PIN management, +key import and generation (including RSA4096, ECDSA and Curve25519 on supported YubiKeys), encryption, decryption, signature and signature verification. + link:./oath/[OATH]:: This module lets you configure and use the OATH application on a YubiKey. It can store and use up to 32 OATH (TOTP or HOTP) credentials. diff --git a/android/build.gradle b/android/build.gradle index a3534a89..0ba48d4b 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -37,12 +37,12 @@ dependencies { api project(':core') compileOnly 'androidx.annotation:annotation:1.7.0' - compileOnly 'com.github.spotbugs:spotbugs-annotations:4.7.3' + compileOnly 'com.github.spotbugs:spotbugs-annotations:4.8.0' testImplementation project(':testing') testImplementation 'androidx.test.ext:junit:1.1.5' testImplementation 'org.robolectric:robolectric:4.10.3' - testImplementation 'org.mockito:mockito-core:5.6.0' + testImplementation 'org.mockito:mockito-core:5.7.0' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test:runner:1.5.2' diff --git a/build.gradle b/build.gradle index 85d6cd29..69f4df9b 100755 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:8.1.2' + classpath 'com.android.tools.build:gradle:8.1.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -35,7 +35,7 @@ allprojects { } subprojects { - version = '2.4.0-SNAPSHOT' + version = '2.4.0' ext.pomName = "Yubico YubiKit ${project.name.capitalize()}" diff --git a/buildSrc/src/main/groovy/project-convention-logging.gradle b/buildSrc/src/main/groovy/project-convention-logging.gradle index fc3c6d50..329ce033 100644 --- a/buildSrc/src/main/groovy/project-convention-logging.gradle +++ b/buildSrc/src/main/groovy/project-convention-logging.gradle @@ -1,3 +1,3 @@ dependencies { - implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'org.slf4j:slf4j-api:2.0.9' } \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle index 74f20253..69db6b06 100755 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'yubikit-java-library' dependencies { - api 'org.slf4j:slf4j-api:2.0.7' + api 'org.slf4j:slf4j-api:2.0.9' } description = "The core module is the base library, with common interfaces and utilities used throughout the rest of the modules." diff --git a/core/src/main/java/com/yubico/yubikit/core/internal/codec/Base64.java b/core/src/main/java/com/yubico/yubikit/core/internal/codec/Base64.java index 4543c513..58d22719 100644 --- a/core/src/main/java/com/yubico/yubikit/core/internal/codec/Base64.java +++ b/core/src/main/java/com/yubico/yubikit/core/internal/codec/Base64.java @@ -41,7 +41,7 @@ public class Base64 { * @param data date to encode * @return Encoded data in Base64 URL safe format */ - public static String encode(byte[] data) { + public static String toUrlSafeString(byte[] data) { return base64Codec.toUrlSafeString(data); } @@ -52,7 +52,7 @@ public static String encode(byte[] data) { * @param data data to decode in Base64 URL safe format * @return decoded data */ - public static byte[] decode(String data) { + public static byte[] fromUrlSafeString(String data) { return base64Codec.fromUrlSafeString(data); } diff --git a/core/src/test/java/com/yubico/yubikit/core/keys/PrivateKeyValuesTest.java b/core/src/test/java/com/yubico/yubikit/core/keys/PrivateKeyValuesTest.java index e820af87..3f4ca9c3 100644 --- a/core/src/test/java/com/yubico/yubikit/core/keys/PrivateKeyValuesTest.java +++ b/core/src/test/java/com/yubico/yubikit/core/keys/PrivateKeyValuesTest.java @@ -24,17 +24,17 @@ public class PrivateKeyValuesTest { @Test public void testParsePkcs8RsaKeyValues() { - PrivateKeyValues.Rsa.parsePkcs8RsaKeyValues(Base64.decode("MIICdQIBADANBgkqhkiG9w0BAQEFA" + - "ASCAl8wggJbAgEAAoGBALWeZ0E5O2l_iHfck9mokf1iWH2eZDWQoJoQKUOAeVoKUecNp250J5tL3EHO" + - "NqWoF6VLO-B-6jTET4Iz97BeUj7gOJHmEw-nqFfguTVmNeeiZ711TNYNpF7kwW7yWghWG-Q7iQEoMXf" + - "Y3x4BL33H2gKRWtMHK66GJViL1l9s3qDXAgMBAAECgYBO753pFzrfS3LAxbns6_snqcrULjdXoJhs3Y" + - "FRuVEE9V9LkP-oXguoz3vXjgzqSvib-ur3U7HvZTM5X-TTXutXdQ5CyORLLtXEZcyCKQI9ihH5fSNJR" + - "WRbJ3xe-xi5NANRkRDkro7tm4a5ZD4PYvO4r29yVB5PXlMkOTLoxNSwwQJBAN5lW93Agi9Ge5B2-B2E" + - "nKSlUvj0-jJBkHYAFTiHyTZVEj6baeHBvJklhVczpWvTXb6Nr8cjAKVshFbdQoBwHmkCQQDRD7djZGI" + - "WH1Lz0rkL01nDj4z4QYMgUs3AQhnrXPBjEgNzphtJ2u7QrCSOBQQHlmAPBDJ_MTxFJMzDIJGDA10_Ak" + - "ATJjEZz_ilr3D2SHgmuoNuXdneG-HrL-ALeQhavL5jkkGm6GTejnr5yNRJZOYKecGppbOL9wSYOdbPT" + - "-_o9T55AkATXCY6cRBYRhxTcf8q5i6Y2pFOaBqxgpmFJVnrHtcwBXoGWqqKQ1j8QAS-lh5SaY2JtnTK" + - "rI-NQ6Qmqbxv6n7XAkBkhLO7pplInVh2WjqXOV4ZAoOAAJlfpG5-z6mWzCZ9-286OJQLr6OVVQMcYEx" + - "UO9yVocZQX-4XqEIF0qAB7m31")); + PrivateKeyValues.Rsa.parsePkcs8RsaKeyValues(Base64.fromUrlSafeString("MIICdQIBADANBgkqhk" + + "iG9w0BAQEFAASCAl8wggJbAgEAAoGBALWeZ0E5O2l_iHfck9mokf1iWH2eZDWQoJoQKUOAeVoKUecNp" + + "250J5tL3EHONqWoF6VLO-B-6jTET4Iz97BeUj7gOJHmEw-nqFfguTVmNeeiZ711TNYNpF7kwW7yWghW" + + "G-Q7iQEoMXfY3x4BL33H2gKRWtMHK66GJViL1l9s3qDXAgMBAAECgYBO753pFzrfS3LAxbns6_snqcr" + + "ULjdXoJhs3YFRuVEE9V9LkP-oXguoz3vXjgzqSvib-ur3U7HvZTM5X-TTXutXdQ5CyORLLtXEZcyCKQ" + + "I9ihH5fSNJRWRbJ3xe-xi5NANRkRDkro7tm4a5ZD4PYvO4r29yVB5PXlMkOTLoxNSwwQJBAN5lW93Ag" + + "i9Ge5B2-B2EnKSlUvj0-jJBkHYAFTiHyTZVEj6baeHBvJklhVczpWvTXb6Nr8cjAKVshFbdQoBwHmkC" + + "QQDRD7djZGIWH1Lz0rkL01nDj4z4QYMgUs3AQhnrXPBjEgNzphtJ2u7QrCSOBQQHlmAPBDJ_MTxFJMz" + + "DIJGDA10_AkATJjEZz_ilr3D2SHgmuoNuXdneG-HrL-ALeQhavL5jkkGm6GTejnr5yNRJZOYKecGppb" + + "OL9wSYOdbPT-_o9T55AkATXCY6cRBYRhxTcf8q5i6Y2pFOaBqxgpmFJVnrHtcwBXoGWqqKQ1j8QAS-l" + + "h5SaY2JtnTKrI-NQ6Qmqbxv6n7XAkBkhLO7pplInVh2WjqXOV4ZAoOAAJlfpG5-z6mWzCZ9-286OJQL" + + "r6OVVQMcYExUO9yVocZQX-4XqEIF0qAB7m31")); } } diff --git a/fido/src/main/java/com/yubico/yubikit/fido/Cose.java b/fido/src/main/java/com/yubico/yubikit/fido/Cose.java index 86739a58..a97f1354 100644 --- a/fido/src/main/java/com/yubico/yubikit/fido/Cose.java +++ b/fido/src/main/java/com/yubico/yubikit/fido/Cose.java @@ -69,7 +69,7 @@ public static PublicKey getPublicKey(@Nullable Map cosePublicKey) throw new IllegalArgumentException("Unsupported key type: " + kty); } - Logger.debug(logger, "publicKey: {}", Base64.encode(publicKey.getEncoded())); + Logger.debug(logger, "publicKey: {}", Base64.toUrlSafeString(publicKey.getEncoded())); return publicKey; } @@ -87,7 +87,7 @@ private static PublicKey importCoseEdDsaPublicKey(Map cosePublicKey) private static PublicKey importCoseEd25519PublicKey(Map cosePublicKey) throws InvalidKeySpecException, NoSuchAlgorithmException { final byte[] rawKey = (byte[]) Objects.requireNonNull(cosePublicKey.get(-2)); - Logger.debug(logger, "raw: {}", Base64.encode(rawKey)); + Logger.debug(logger, "raw: {}", Base64.toUrlSafeString(rawKey)); return new Cv25519(EllipticCurveValues.Ed25519, rawKey).toPublicKey(); } @@ -98,8 +98,8 @@ private static PublicKey importCoseEcdsaPublicKey(Map cosePublicKey) final byte[] y = (byte[]) Objects.requireNonNull(cosePublicKey.get(-3)); Logger.debug(logger, "crv: {}", crv); - Logger.debug(logger, "x: {}", Base64.encode(x)); - Logger.debug(logger, "y: {}", Base64.encode(y)); + Logger.debug(logger, "x: {}", Base64.toUrlSafeString(x)); + Logger.debug(logger, "y: {}", Base64.toUrlSafeString(y)); EllipticCurveValues ellipticCurveValues; @@ -127,8 +127,8 @@ private static PublicKey importCoseRsaPublicKey(Map cosePublicKey) throws NoSuchAlgorithmException, InvalidKeySpecException { byte[] n = (byte[]) Objects.requireNonNull(cosePublicKey.get(-1)); byte[] e = (byte[]) Objects.requireNonNull(cosePublicKey.get(-2)); - Logger.debug(logger, "n: {}", Base64.encode(n)); - Logger.debug(logger, "e: {}", Base64.encode(e)); + Logger.debug(logger, "n: {}", Base64.toUrlSafeString(n)); + Logger.debug(logger, "e: {}", Base64.toUrlSafeString(e)); return new Rsa(new BigInteger(1, n), new BigInteger(1, e)).toPublicKey(); } } diff --git a/fido/src/main/java/com/yubico/yubikit/fido/ctap/Hkdf.java b/fido/src/main/java/com/yubico/yubikit/fido/ctap/Hkdf.java index 6e33e6b7..b75f2ff7 100644 --- a/fido/src/main/java/com/yubico/yubikit/fido/ctap/Hkdf.java +++ b/fido/src/main/java/com/yubico/yubikit/fido/ctap/Hkdf.java @@ -61,20 +61,29 @@ byte[] expand(byte[] prk, byte[] info, int length) throws InvalidKeyException { .put(info) .put(i) .array(); - t = hmacDigest(prk, data); + Arrays.fill(t, (byte) 0); + byte[] digest = hmacDigest(prk, data); - okm = ByteBuffer.allocate(okm.length + t.length) + byte[] result = ByteBuffer.allocate(okm.length + digest.length) .put(okm) - .put(t) + .put(digest) .array(); + Arrays.fill(okm, (byte) 0); + Arrays.fill(data, (byte) 0); + okm = result; + t = digest; } - return Arrays.copyOf(okm, length); + byte[] result = Arrays.copyOf(okm, length); + Arrays.fill(okm, (byte) 0); + return result; } byte[] digest(byte[] ikm, byte[] salt, byte[] info, int length) throws NoSuchAlgorithmException, InvalidKeyException { byte[] prk = extract(salt, ikm); - return expand(prk, info, length); + byte[] result = expand(prk, info, length); + Arrays.fill(prk, (byte) 0); + return result; } } diff --git a/fido/src/main/java/com/yubico/yubikit/fido/ctap/PinUvAuthProtocolV2.java b/fido/src/main/java/com/yubico/yubikit/fido/ctap/PinUvAuthProtocolV2.java index a6cd0b3a..d35b3ac9 100644 --- a/fido/src/main/java/com/yubico/yubikit/fido/ctap/PinUvAuthProtocolV2.java +++ b/fido/src/main/java/com/yubico/yubikit/fido/ctap/PinUvAuthProtocolV2.java @@ -56,14 +56,16 @@ public int getVersion() { @Override public byte[] kdf(byte[] z) { + byte[] hmacKey = null; + byte[] aesKey = null; try { - byte[] hmacKey = new Hkdf(HKDF_ALG).digest( + hmacKey = new Hkdf(HKDF_ALG).digest( z, HKDF_SALT, HKDF_INFO_HMAC, HKDF_LENGTH); - byte[] aesKey = new Hkdf(HKDF_ALG).digest( + aesKey = new Hkdf(HKDF_ALG).digest( z, HKDF_SALT, HKDF_INFO_AES, @@ -75,13 +77,21 @@ public byte[] kdf(byte[] z) { .array(); } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new IllegalStateException(e); + } finally { + if (hmacKey != null) { + Arrays.fill(hmacKey, (byte) 0); + } + if (aesKey != null) { + Arrays.fill(aesKey, (byte) 0); + } } } @Override public byte[] encrypt(byte[] key, byte[] plaintext) { + byte[] aesKey = null; try { - byte[] aesKey = Arrays.copyOfRange(key, 32, key.length); + aesKey = Arrays.copyOfRange(key, 32, key.length); byte[] iv = RandomUtils.getRandomBytes(16); final byte[] ciphertext = @@ -93,19 +103,27 @@ public byte[] encrypt(byte[] key, byte[] plaintext) { .array(); } catch (IllegalBlockSizeException | BadPaddingException e) { throw new IllegalStateException(e); + } finally { + if (aesKey != null) { + Arrays.fill(aesKey, (byte) 0); + } } } @Override public byte[] decrypt(byte[] key, byte[] ciphertext) { + byte[] aesKey = null; try { - byte[] aesKey = Arrays.copyOfRange(key, 32, key.length); + aesKey = Arrays.copyOfRange(key, 32, key.length); byte[] iv = Arrays.copyOf(ciphertext, 16); byte[] ct = Arrays.copyOfRange(ciphertext, 16, ciphertext.length); - byte[] plaintext = getCipher(Cipher.DECRYPT_MODE, aesKey, iv).doFinal(ct); - return Arrays.copyOf(plaintext, plaintext.length); + return getCipher(Cipher.DECRYPT_MODE, aesKey, iv).doFinal(ct); } catch (BadPaddingException | IllegalBlockSizeException e) { throw new IllegalStateException(e); + } finally { + if (aesKey != null) { + Arrays.fill(aesKey, (byte) 0); + } } } @@ -120,8 +138,7 @@ public byte[] authenticate(byte[] key, byte[] message) { } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new RuntimeException(e); } - byte[] result = mac.doFinal(message); - return Arrays.copyOf(result, result.length); + return mac.doFinal(message); } private Cipher getCipher(int mode, byte[] secret, byte[] iv) { diff --git a/fido/src/main/java/com/yubico/yubikit/fido/webauthn/PublicKeyCredential.java b/fido/src/main/java/com/yubico/yubikit/fido/webauthn/PublicKeyCredential.java index 660b8498..9d297003 100644 --- a/fido/src/main/java/com/yubico/yubikit/fido/webauthn/PublicKeyCredential.java +++ b/fido/src/main/java/com/yubico/yubikit/fido/webauthn/PublicKeyCredential.java @@ -50,7 +50,7 @@ public class PublicKeyCredential extends Credential { */ public PublicKeyCredential(String id, AuthenticatorResponse response) { super(id, PUBLIC_KEY_CREDENTIAL_TYPE); - this.rawId = Base64.decode(id); + this.rawId = Base64.fromUrlSafeString(id); this.response = response; } @@ -63,7 +63,7 @@ public PublicKeyCredential(String id, AuthenticatorResponse response) { * @see AuthenticatorAssertionResponse */ public PublicKeyCredential(byte[] id, AuthenticatorResponse response) { - super(Base64.encode(id), PUBLIC_KEY_CREDENTIAL_TYPE); + super(Base64.toUrlSafeString(id), PUBLIC_KEY_CREDENTIAL_TYPE); this.rawId = id; this.response = response; } diff --git a/fido/src/main/java/com/yubico/yubikit/fido/webauthn/SerializationUtils.java b/fido/src/main/java/com/yubico/yubikit/fido/webauthn/SerializationUtils.java index b00fb5ec..2e17e296 100644 --- a/fido/src/main/java/com/yubico/yubikit/fido/webauthn/SerializationUtils.java +++ b/fido/src/main/java/com/yubico/yubikit/fido/webauthn/SerializationUtils.java @@ -22,7 +22,7 @@ class SerializationUtils { static Object serializeBytes(byte[] value, SerializationType serializationType) { switch (serializationType) { case JSON: { - return Base64.encode(value); + return Base64.toUrlSafeString(value); } case CBOR: { @@ -36,7 +36,7 @@ static Object serializeBytes(byte[] value, SerializationType serializationType) static byte[] deserializeBytes(Object value, SerializationType serializationType) { switch (serializationType) { case JSON: { - return Base64.decode((String) value); + return Base64.fromUrlSafeString((String) value); } case CBOR: { diff --git a/fido/src/test/java/com/yubico/yubikit/fido/CoseTest.java b/fido/src/test/java/com/yubico/yubikit/fido/CoseTest.java index 68465998..5994f6a1 100644 --- a/fido/src/test/java/com/yubico/yubikit/fido/CoseTest.java +++ b/fido/src/test/java/com/yubico/yubikit/fido/CoseTest.java @@ -16,21 +16,19 @@ package com.yubico.yubikit.fido; +import com.yubico.yubikit.core.internal.codec.Base64; + import org.junit.Assert; import org.junit.Test; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; -import java.util.Base64; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nullable; - public class CoseTest { - private static final @Nullable Map NULL_COSE = null; private static final Map EMPTY_COSE = new HashMap<>(); private static final Map RS256 = new HashMap<>(); @@ -189,10 +187,10 @@ public void getPublicKeyEDDSA() } private static byte[] decode(String urlSafeBase64) { - return Base64.getUrlDecoder().decode(urlSafeBase64); + return Base64.fromUrlSafeString(urlSafeBase64); } private static String encode(byte[] data) { - return Base64.getUrlEncoder().withoutPadding().encodeToString(data); + return Base64.toUrlSafeString(data); } } \ No newline at end of file diff --git a/fido/src/test/java/com/yubico/yubikit/fido/webauthn/SerializationTest.java b/fido/src/test/java/com/yubico/yubikit/fido/webauthn/SerializationTest.java index 780fab72..31db6d38 100755 --- a/fido/src/test/java/com/yubico/yubikit/fido/webauthn/SerializationTest.java +++ b/fido/src/test/java/com/yubico/yubikit/fido/webauthn/SerializationTest.java @@ -68,7 +68,7 @@ public void testUserEntity() { Assert.assertEquals(user, PublicKeyCredentialUserEntity.fromMap(cborMap, SerializationType.CBOR)); Map jsonMap = user.toMap(SerializationType.JSON); - Assert.assertEquals(Base64.encode(user.getId()), jsonMap.get("id")); + Assert.assertEquals(Base64.toUrlSafeString(user.getId()), jsonMap.get("id")); Assert.assertEquals(user.getName(), jsonMap.get("name")); Assert.assertEquals(user.getDisplayName(), jsonMap.get("displayName")); Assert.assertEquals(user, PublicKeyCredentialUserEntity.fromMap(jsonMap, SerializationType.JSON)); @@ -121,7 +121,7 @@ public void testDescriptor() { Map jsonMap = descriptor.toMap(SerializationType.JSON); Assert.assertEquals(descriptor.getType(), jsonMap.get("type")); - Assert.assertEquals(Base64.encode(descriptor.getId()), jsonMap.get("id")); + Assert.assertEquals(Base64.toUrlSafeString(descriptor.getId()), jsonMap.get("id")); Assert.assertEquals(descriptor, PublicKeyCredentialDescriptor.fromMap(jsonMap, SerializationType.JSON)); } @@ -295,10 +295,10 @@ AuthenticatorAttestationResponse randomAuthenticatorAttestationResponse() { @SuppressWarnings("SpellCheckingInspection") AuthenticatorData authenticatorData = AuthenticatorData.parseFrom( - ByteBuffer.wrap(Base64.decode("5Yaf4EYzO6ALp_K7s-p-BQLPSCYVYcKLZptoXwxqQztFAAAAA" + - "hSaICGO9kEzlriB-NW38fUAMA5hR7Wj16h_z28qvtukB63QcIhzJ_sUkkJPfsU-KzdCFeaF" + - "2mZ80gSROEtELSHniKUBAgMmIAEhWCAOYUe1o9eof89vKr7bLZhH7nLY4wjKx5oxa66Kv0J" + - "jXiJYIKyPUlRxXHJjLrACafd_1stM7DyX120jDO7BlwqYsJyJ") + ByteBuffer.wrap(Base64.fromUrlSafeString("5Yaf4EYzO6ALp_K7s-p-BQLPSCYVYcKLZptoXw" + + "xqQztFAAAAAhSaICGO9kEzlriB-NW38fUAMA5hR7Wj16h_z28qvtukB63QcIhzJ_sUkkJPf" + + "sU-KzdCFeaF2mZ80gSROEtELSHniKUBAgMmIAEhWCAOYUe1o9eof89vKr7bLZhH7nLY4wjK" + + "x5oxa66Kv0JjXiJYIKyPUlRxXHJjLrACafd_1stM7DyX120jDO7BlwqYsJyJ") )); return new AuthenticatorAttestationResponse( @@ -333,7 +333,7 @@ public void testAttestationResponse() { public void testPublicKeyCredentialCreation() { byte[] credentialId = new byte[1 + random.nextInt(64)]; random.nextBytes(credentialId); - String credentialIdB64UrlEncoded = Base64.encode(credentialId); + String credentialIdB64UrlEncoded = Base64.toUrlSafeString(credentialId); AuthenticatorAttestationResponse response = randomAuthenticatorAttestationResponse(); @@ -362,7 +362,7 @@ public void testPublicKeyCredentialCreation() { public void testPublicKeyCredentialWithAssertion() { byte[] credentialId = new byte[1 + random.nextInt(64)]; random.nextBytes(credentialId); - String credentialIdB64UrlEncoded = Base64.encode(credentialId); + String credentialIdB64UrlEncoded = Base64.toUrlSafeString(credentialId); AuthenticatorAssertionResponse response = randomAuthenticatorAssertionResponse(); @@ -394,7 +394,7 @@ public void testPublicKeyCredentialWithAssertion() { public void testPublicKeyCredentialWithAttestation() { byte[] credentialId = new byte[1 + random.nextInt(64)]; random.nextBytes(credentialId); - String credentialIdB64UrlEncoded = Base64.encode(credentialId); + String credentialIdB64UrlEncoded = Base64.toUrlSafeString(credentialId); AuthenticatorAttestationResponse response = randomAuthenticatorAttestationResponse(); diff --git a/oath/src/main/java/com/yubico/yubikit/oath/OathSession.java b/oath/src/main/java/com/yubico/yubikit/oath/OathSession.java index 7ca1e902..ebd97a12 100755 --- a/oath/src/main/java/com/yubico/yubikit/oath/OathSession.java +++ b/oath/src/main/java/com/yubico/yubikit/oath/OathSession.java @@ -753,7 +753,7 @@ private String getDeviceId() { } messageDigest.update(salt); byte[] digest = messageDigest.digest(); - return Base64.encode(Arrays.copyOfRange(digest, 0, 16)); + return Base64.toUrlSafeString(Arrays.copyOfRange(digest, 0, 16)); } } } diff --git a/piv/src/main/java/com/yubico/yubikit/piv/InvalidPinException.java b/piv/src/main/java/com/yubico/yubikit/piv/InvalidPinException.java index 4e76a71c..3a3d6cf3 100755 --- a/piv/src/main/java/com/yubico/yubikit/piv/InvalidPinException.java +++ b/piv/src/main/java/com/yubico/yubikit/piv/InvalidPinException.java @@ -19,7 +19,7 @@ /** * Thrown when the wrong PIN or PUK is used (or when the PIN or PUK is in a blocked state). * - * @deprecated Use InvalidPinException from the code module instead + * @deprecated Use InvalidPinException from the core module instead */ @Deprecated public class InvalidPinException extends com.yubico.yubikit.core.application.InvalidPinException { diff --git a/testing-android/src/androidTest/java/com/yubico/yubikit/testing/fido/Ctap2SessionResetInstrumentedTests.java b/testing-android/src/androidTest/java/com/yubico/yubikit/testing/fido/Ctap2SessionResetInstrumentedTests.java index 17193364..8888f53f 100644 --- a/testing-android/src/androidTest/java/com/yubico/yubikit/testing/fido/Ctap2SessionResetInstrumentedTests.java +++ b/testing-android/src/androidTest/java/com/yubico/yubikit/testing/fido/Ctap2SessionResetInstrumentedTests.java @@ -16,11 +16,7 @@ package com.yubico.yubikit.testing.fido; -import static com.yubico.yubikit.testing.fido.Ctap2ClientPinInstrumentedTests.supportsPinUvAuthProtocol; - import com.yubico.yubikit.fido.ctap.Ctap2Session; -import com.yubico.yubikit.fido.ctap.PinUvAuthProtocolV1; -import com.yubico.yubikit.fido.ctap.PinUvAuthProtocolV2; import com.yubico.yubikit.testing.framework.FidoInstrumentedTests; import org.junit.Test; @@ -30,7 +26,6 @@ /** * Tests FIDO Reset. *

- *

* Notes: *

    *
  • The tests for different protocols are meant to be ran separately.
  • @@ -45,28 +40,15 @@ public class Ctap2SessionResetInstrumentedTests extends FidoInstrumentedTests { */ private static boolean supportsBioEnroll(Ctap2Session session) { final Map options = session.getCachedInfo().getOptions(); - return Boolean.TRUE.equals(options.get("bioEnroll")); + return options.containsKey("bioEnroll"); } @Test - public void testResetWithPinUVAuthProtocolV1() throws Throwable { + public void testReset() throws Throwable { withCtap2Session( "Skipping reset test - authenticator supports bio enrollment", (device, session) -> !supportsBioEnroll(session), - Ctap2SessionTests::testReset, - new PinUvAuthProtocolV1() - ); - } - - @Test - public void testResetWithPinUVAuthProtocolV2() throws Throwable { - withCtap2Session( - "Skipping reset test - authenticator supports bio enrollment or does not" + - " support pinUvAuthProtocol Two", - (device, session) -> supportsPinUvAuthProtocol(session, 2) && - !supportsBioEnroll(session), - Ctap2SessionTests::testReset, - new PinUvAuthProtocolV2() + Ctap2SessionTests::testReset ); } } diff --git a/testing/src/main/java/com/yubico/yubikit/testing/piv/PivTestUtils.java b/testing/src/main/java/com/yubico/yubikit/testing/piv/PivTestUtils.java index 6e1dac70..c21c0423 100755 --- a/testing/src/main/java/com/yubico/yubikit/testing/piv/PivTestUtils.java +++ b/testing/src/main/java/com/yubico/yubikit/testing/piv/PivTestUtils.java @@ -137,8 +137,8 @@ private KeyPair getKeyPair() { try { KeyFactory kf = KeyFactory.getInstance(keyType.params.algorithm.name()); return new KeyPair( - kf.generatePublic(new X509EncodedKeySpec(Base64.decode(publicKey))), - kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(privateKey))) + kf.generatePublic(new X509EncodedKeySpec(Base64.fromUrlSafeString(publicKey))), + kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.fromUrlSafeString(privateKey))) ); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new IllegalStateException(e);