Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fc3f59e
initial implementation, read certificates
AdamVe Mar 18, 2025
d3b2432
code style fixes
AdamVe Jul 25, 2025
8bb013c
add missing cases for certificates
AdamVe Jul 25, 2025
41897d3
add method channel stubs
AdamVe Jul 25, 2025
a3d86c4
workaround precommit checks
AdamVe Jul 25, 2025
d607385
implement move key, delete ops (WIP)
AdamVe Jul 25, 2025
9a900e0
implement PIN ops, certificate ops, tests (WIP)
AdamVe Aug 5, 2025
a67a693
improve file parser, add tests
AdamVe Aug 7, 2025
f2360bf
bump python version
AdamVe Aug 8, 2025
57298ac
use JSONObject.toString()
AdamVe Aug 8, 2025
8cfe2d2
bump file_picker
AdamVe Aug 8, 2025
a9f4195
handle focus when submitting in PIV dialogs
AdamVe Aug 8, 2025
64056db
fix file save for Android
AdamVe Aug 8, 2025
6c5ee77
temporarily remove update of device Info
AdamVe Aug 8, 2025
2745a3d
implement generate key outputs
AdamVe Aug 8, 2025
5c9b9af
export certificate
AdamVe Aug 11, 2025
524122b
Merge branch 'main' into adamve/android_piv
AdamVe Aug 12, 2025
daef23a
fix format
AdamVe Aug 12, 2025
886091b
Update ConnectionHelper, support SCP
AdamVe Aug 13, 2025
25712ca
implement generate CHUID
AdamVe Aug 13, 2025
741ca27
fix ISO date formatting
AdamVe Aug 13, 2025
09d58f1
Support for pivman data/pivman protected data
AdamVe Aug 20, 2025
b047f5a
reorganize pivmandata
AdamVe Aug 20, 2025
315a8dc
support keys without metadata and serial features
AdamVe Aug 20, 2025
32be318
update R8
AdamVe Aug 20, 2025
b64dfd6
Wait for key removal during NFC actions
AdamVe Aug 20, 2025
fac3460
adjust generate key dialog for Android
AdamVe Aug 25, 2025
25b6c21
support cancellations, add forgotten file
AdamVe Aug 25, 2025
bbd579a
fix authentication for key generation
AdamVe Aug 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ android {
}
}

packagingOptions {
exclude 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'
exclude 'META-INF/versions/9/OSGI-INF/bcprov.provider.bc.Licenses'
exclude 'META-INF/versions/9/OSGI-INF/bcpkix.provider.bc.Licenses'
}

}

apply from: "signing.gradle"
Expand All @@ -89,9 +95,11 @@ flutter {

dependencies {
api "com.yubico.yubikit:android:$project.yubiKitVersion"
api "com.yubico.yubikit:core:$project.yubiKitVersion"
api "com.yubico.yubikit:management:$project.yubiKitVersion"
api "com.yubico.yubikit:oath:$project.yubiKitVersion"
api "com.yubico.yubikit:fido:$project.yubiKitVersion"
api "com.yubico.yubikit:piv:$project.yubiKitVersion"
api "com.yubico.yubikit:support:$project.yubiKitVersion"

implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0'
Expand All @@ -107,6 +115,9 @@ dependencies {

implementation 'com.github.tony19:logback-android:3.0.0'

implementation 'org.bouncycastle:bcprov-jdk18on:1.80'
implementation 'org.bouncycastle:bcpkix-jdk18on:1.79'

// testing dependencies
testImplementation "junit:junit:$project.junitVersion"
testImplementation "org.mockito:mockito-core:$project.mockitoVersion"
Expand Down
16 changes: 15 additions & 1 deletion android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,18 @@
-keepclassmembers class org.slf4j.impl.** { *; }
-keepattributes *Annotation*

-keep public class ch.qos.logback.classic.android.LogcatAppender
-keep public class ch.qos.logback.classic.android.LogcatAppender


-keep class org.bouncycastle.** { *; }

# these are not part of Android SDK
-dontwarn javax.naming.Binding
-dontwarn javax.naming.NamingEnumeration
-dontwarn javax.naming.NamingException
-dontwarn javax.naming.directory.Attribute
-dontwarn javax.naming.directory.Attributes
-dontwarn javax.naming.directory.DirContext
-dontwarn javax.naming.directory.InitialDirContext
-dontwarn javax.naming.directory.SearchControls
-dontwarn javax.naming.directory.SearchResult
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class AppPreferences(context: Context) {
"passkeys" -> OperationContext.FidoPasskeys
"fingerprints" -> OperationContext.FidoFingerprints
"accounts" -> OperationContext.Oath
"certificates" -> OperationContext.Piv
else -> OperationContext.Default
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ import com.yubico.authenticator.management.ManagementManager
import com.yubico.authenticator.oath.AppLinkMethodChannel
import com.yubico.authenticator.oath.OathManager
import com.yubico.authenticator.oath.OathViewModel
import com.yubico.authenticator.piv.PivManager
import com.yubico.authenticator.piv.PivViewModel
import com.yubico.authenticator.yubikit.DeviceInfoHelper.Companion.getDeviceInfo
import com.yubico.authenticator.yubikit.NfcState
import com.yubico.authenticator.yubikit.NfcStateDispatcher
Expand All @@ -77,11 +79,13 @@ import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.io.Closeable
import java.io.IOException
import java.security.NoSuchAlgorithmException
import java.security.Security
import java.util.concurrent.Executors
import javax.crypto.Mac

Expand All @@ -91,6 +95,7 @@ class MainActivity : FlutterFragmentActivity() {
}
private val oathViewModel: OathViewModel by viewModels()
private val fidoViewModel: FidoViewModel by viewModels()
private val pivViewModel: PivViewModel by viewModels()

private val nfcConfiguration = NfcConfiguration().timeout(5000)

Expand Down Expand Up @@ -124,6 +129,9 @@ class MainActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Security.removeProvider("BC")
Security.insertProviderAt(BouncyCastleProvider(), 1)

if (isPortraitOnly()) {
forcePortraitOrientation()
}
Expand Down Expand Up @@ -367,7 +375,7 @@ class MainActivity : FlutterFragmentActivity() {
logger.debug("Processing pending action in context {}", it)
if (it.processYubiKey(device)) {
if (device is NfcYubiKeyDevice) {
appMethodChannel.nfcStateChanged(NfcState.SUCCESS)
appMethodChannel.nfcStateChanged(NfcState.getSuccessState())
}
}
if (device is NfcYubiKeyDevice) {
Expand Down Expand Up @@ -417,6 +425,7 @@ class MainActivity : FlutterFragmentActivity() {
OperationContext.Oath,
OperationContext.FidoPasskeys,
OperationContext.FidoFingerprints,
OperationContext.Piv,
OperationContext.Home
)

Expand All @@ -439,7 +448,7 @@ class MainActivity : FlutterFragmentActivity() {
val requestHandled = it.processYubiKey(device)
if (requestHandled) {
if (device is NfcYubiKeyDevice) {
appMethodChannel.nfcStateChanged(NfcState.SUCCESS)
appMethodChannel.nfcStateChanged(NfcState.getSuccessState())
}
}
if (!switchedContextManager && device is NfcYubiKeyDevice) {
Expand All @@ -449,7 +458,7 @@ class MainActivity : FlutterFragmentActivity() {
}
} catch (e: Exception) {
logger.debug("Caught Exception during YubiKey processing: ", e)
appMethodChannel.nfcStateChanged(NfcState.FAILURE)
appMethodChannel.nfcStateChanged(NfcState.getFailureState())
}
}

Expand Down Expand Up @@ -497,6 +506,8 @@ class MainActivity : FlutterFragmentActivity() {
fidoViewModel.fingerprints.streamTo(this, messenger, "android.fido.fingerprints"),
fidoViewModel.resetState.streamTo(this, messenger, "android.fido.reset"),
fidoViewModel.registerFingerprint.streamTo(this, messenger, "android.fido.registerFp"),
pivViewModel.state.streamTo(this, messenger, "android.piv.state"),
pivViewModel.slots.streamTo(this, messenger, "android.piv.slots")
)

viewModel.appContext.observe(this) {
Expand Down Expand Up @@ -528,6 +539,15 @@ class MainActivity : FlutterFragmentActivity() {
fidoViewModel,
viewModel
)
val pivContextManager = PivManager(
messenger,
deviceManager,
this,
appMethodChannel,
nfcOverlayManager,
pivViewModel,
viewModel
)
val managementContextManager = ManagementManager(messenger, deviceManager)

contextManagers = mapOf(
Expand All @@ -536,6 +556,7 @@ class MainActivity : FlutterFragmentActivity() {
OperationContext.FidoPasskeys to fidoContextManager,
OperationContext.FidoFingerprints to fidoContextManager,
OperationContext.Management to managementContextManager,
OperationContext.Piv to pivContextManager,
// currently not supported
OperationContext.FidoU2f to homeContextManager,
OperationContext.HsmAuth to homeContextManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ enum class OperationContext(val value: Int) {
FidoU2f(2),
FidoFingerprints(3),
FidoPasskeys(4),
YubiOtp(5),
Piv(6),
Piv(5),
YubiOtp(6),
Settings(7),
OpenPgp(8),
HsmAuth(9),
Expand All @@ -48,6 +48,7 @@ enum class OperationContext(val value: Int) {

val capabilitiesToContext = mapOf(
Capability.OATH to Oath,
Capability.PIV to Piv,
Capability.FIDO2 to FidoPasskeys
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,10 @@ class DeviceManager(

try {
return onNfc.invoke().value.also {
appMethodChannel.nfcStateChanged(NfcState.SUCCESS)
appMethodChannel.nfcStateChanged(NfcState.getSuccessState())
}
} catch (e: Exception) {
appMethodChannel.nfcStateChanged(NfcState.FAILURE)
appMethodChannel.nfcStateChanged(NfcState.getFailureState())
throw e
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class FidoResetHelper(
FidoManager.updateDeviceInfo.set(true)
connectionHelper.useSessionNfc { fidoSession ->
doReset(fidoSession)
appMethodChannel.nfcStateChanged(NfcState.SUCCESS)
appMethodChannel.nfcStateChanged(NfcState.getSuccessState())
continuation.resume(Unit)
}.value
} catch (cancellationException: CancellationException) {
Expand All @@ -233,7 +233,7 @@ class FidoResetHelper(
} catch (e: Throwable) {
// on NFC, clean device info in this situation
mainViewModel.setDeviceInfo(null)
appMethodChannel.nfcStateChanged(NfcState.FAILURE)
appMethodChannel.nfcStateChanged(NfcState.getFailureState())
logger.error("Failure during FIDO reset:", e)
continuation.resumeWithException(e)
}
Expand Down
Loading
Loading