Package com.aurelienpierre.sioul
Class SecurityKeyLink
java.lang.Object
com.aurelienpierre.sioul.SecurityKeyLink
Your security key held against the phone or plugged into its USB-C port
(crates/sioul-app/src/nfc.rs, crates/sioul-sync/src/securitykey/phone.rs,
docs/android.md "Your security key"), for Rust through android/main.cpp.
- Reader mode only while asked. While a band of Sioul's waits for
the key and Sioul is in front, NFC is read in reader mode
(
NfcAdapter.enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle): NFC-A and B, no NDEF check, a presence check every 50 ms), ahead of other apps; never otherwise, so that OpenKeychain, Yubico Authenticator and the key's Yubico OTP (its NDEF address) work as before. Android keeps it on the activity across a pause and a return, until Sioul lets it go. No tag filter in the manifest: holding the key never opens Sioul. - The key held is an
IsoDep, connected at once, five seconds an exchange; its APDUs go throughtransceive(byte[]), on Rust's thread, never Android's main one. A key that leaves the field throwsTagLostException, which Rust says as a key moved away. - Let go when Sioul no longer asks:
NfcAdapter.ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler), so that Android hands the key, still in the field, to no other app until it has left it, then reader mode ends. - Plugged in: while a band asks for the key, a key plugged in
(before or after) is looked for among the USB devices, by its smart card
interface (class 0x0B) alone: never its keyboard (Yubico OTP types there,
untouched) nor its FIDO interface. Android's permission is asked once for
each time it is plugged in: refused, Sioul says so and asks again only
once it has been unplugged. Its interface claimed, the APDUs go as CCID
messages (
Ccid), on Rust's thread; a key unplugged meanwhile is a key gone, as over NFC (TagLostException). Claimed by another app (Yubico Authenticator, OpenKeychain), it is said so. When Sioul no longer asks, the key is powered off, its interface released and its connection closed: other apps have it again. No USB filter in the manifest: plugging the key never opens Sioul. - The screen kept from captures (FLAG_SECURE) while Sioul asks for the key: its PIN is typed then.
- Nothing logged: never a byte of an APDU, a PIN's among them; each command's array is wiped once sent.
-
Field Summary
FieldsModifier and TypeFieldDescriptionKeys plugged in that Android was asked for (by device name), until unplugged.(package private) static final intHow long a key let go must stay away before Android reads it again.(package private) static final StringBefore Android 13 a receiver registered in code is exported: only a sender that holds this permission (signature level: Sioul itself, for Android's answer, and the system, for plugging and unplugging) reaches it, so that no other app can play Android's answer or a key unplugged.private static StringOne that another app holds, until unplugged or asked again.private static Ccidprivate static UsbInterfaceprivate static UsbDeviceConnectionprivate static IsoDepThe key held now.private static TagIts tag, to let it go.private static Application.ActivityLifecycleCallbacksSioul's activity going to the background or coming back, heard once a key was wanted.private static final Objectprivate static StringA key Android was asked for and has not answered yet: its dialog is in front.(package private) static final StringThe permission's answer, for this package's receiver alone.private static UsbDeviceA key plugged in, open: its connection, its interface, its messages.(package private) static final intThe longest APDU sent to a key plugged in: short APDUs, chained by Rust, and their header.private static booleanprivate static ActivityThe activity reader mode is on for.Those Android refused, until unplugged.(package private) static final intHow long one exchange may take: Ed25519 and Curve25519 take far less; the margin is for RSA and slower keys.private static booleanUnplugged while open: what is sent now fails as a key gone.private static UsbManagerAndroid's USB service, once a key was wanted: what is plugged in now.private static BroadcastReceiverThe receiver of plugging, unplugging and Android's answer, while a key is wanted and Sioul is in front. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescription(package private) static UsbInterfacecardInterface(UsbDevice device) The device's smart card interface (CCID, class 0x0B), with its two bulk endpoints; null when it has none.private static voidclose()private static voidprivate static voidconsider(Activity activity, UsbManager manager, UsbDevice device) A USB device: a security key when it has a smart card interface; opened, or Android asked once.private static UsbDeviceThe device an intent names, as Android before 13 gives it.private static UsbEndpointendpoint(UsbInterface face, int direction) private static byte[]private static voidSioul in the background while a key is wanted: the key plugged in let go and the receiver unregistered, so that other apps have it meanwhile, unless Android's own dialog (its question for the key) is what came in front; back in front, looked for again (asked no more: Android's answer stands until it is unplugged).private static voidA tag in reach, on NFC's thread: kept when it speaks ISO-DEP (an OpenPGP card does), Rust told.private static voidheard(Activity activity, UsbManager manager, Intent intent) (package private) static int[]held()The key in reach now, as {what (0 none, 1 a key, 2 refused by Android, 3 held by another app), longest exchange, extended length (1) or not (0), plugged in (1) or held (0)}: a key plugged in first, else one held.private static voidletGo(NfcAdapter adapter) The key held let go: ignored by Android until it has left the field, then closed.private static voidPlugging, unplugging and Android's answer heard while a key is wanted; a key plugged in already looked for.private static voidRust told that a key came (android/main.cpp).private static voidopen(UsbManager manager, UsbDevice device) The key plugged in, its smart card interface claimed alone; another app holding it, said so.private static byte[]One APDU to the key plugged in, powered on first; unplugged meanwhile, a key gone.(package private) static voidAndroid's NFC settings, where it is switched on.private static void(package private) static intNFC in this phone: 0 none, 1 switched off, 2 ready.private static voidprivate static voidtold()Rust told that something changed: a key came, went, or was refused.(package private) static byte[]transceive(byte[] command) One APDU to the key in reach, on Rust's thread; the command wiped once sent.private static voidNo longer asked: the key plugged in powered off, its interface given back, and Android's receiver let go.(package private) static booleanWhether this phone takes a key plugged into its USB-C port (USB host).(package private) static voidA key wanted (reader mode on, the screen kept from captures), or no longer (the key let go, reader mode off).
-
Field Details
-
TIMEOUT_MS
How long one exchange may take: Ed25519 and Curve25519 take far less; the margin is for RSA and slower keys.- See Also:
-
AWAY_MS
How long a key let go must stay away before Android reads it again.- See Also:
-
lock
-
held
The key held now. -
heldTag
Its tag, to let it go. -
reading
The activity reader mode is on for. -
PERMISSION
The permission's answer, for this package's receiver alone.- See Also:
-
plugged
A key plugged in, open: its connection, its interface, its messages. -
connection
-
claimed
-
ccid
-
powered
-
unplugged
Unplugged while open: what is sent now fails as a key gone. -
asked
Keys plugged in that Android was asked for (by device name), until unplugged. -
refused
Those Android refused, until unplugged. -
busy
One that another app holds, until unplugged or asked again. -
usb
Android's USB service, once a key was wanted: what is plugged in now. -
usbReceiver
The receiver of plugging, unplugging and Android's answer, while a key is wanted and Sioul is in front. -
pending
A key Android was asked for and has not answered yet: its dialog is in front. -
lifecycle
Sioul's activity going to the background or coming back, heard once a key was wanted. -
BROADCASTS
Before Android 13 a receiver registered in code is exported: only a sender that holds this permission (signature level: Sioul itself, for Android's answer, and the system, for plugging and unplugging) reaches it, so that no other app can play Android's answer or a key unplugged.- See Also:
-
PLUGGED_LONGEST
The longest APDU sent to a key plugged in: short APDUs, chained by Rust, and their header.- See Also:
-
-
Constructor Details
-
SecurityKeyLink
private SecurityKeyLink()
-
-
Method Details
-
usbHost
Whether this phone takes a key plugged into its USB-C port (USB host). -
state
NFC in this phone: 0 none, 1 switched off, 2 ready. -
want
A key wanted (reader mode on, the screen kept from captures), or no longer (the key let go, reader mode off). Any thread. -
start
-
stop
-
letGo
The key held let go: ignored by Android until it has left the field, then closed. -
close
-
found
A tag in reach, on NFC's thread: kept when it speaks ISO-DEP (an OpenPGP card does), Rust told. -
held
The key in reach now, as {what (0 none, 1 a key, 2 refused by Android, 3 held by another app), longest exchange, extended length (1) or not (0), plugged in (1) or held (0)}: a key plugged in first, else one held. -
transceive
One APDU to the key in reach, on Rust's thread; the command wiped once sent.- Throws:
IOException
-
plugged
One APDU to the key plugged in, powered on first; unplugged meanwhile, a key gone.- Throws:
IOException
-
exchange
- Throws:
IOException
-
listen
Plugging, unplugging and Android's answer heard while a key is wanted; a key plugged in already looked for. -
heard
-
deviceOf
The device an intent names, as Android before 13 gives it. -
consider
A USB device: a security key when it has a smart card interface; opened, or Android asked once. -
cardInterface
The device's smart card interface (CCID, class 0x0B), with its two bulk endpoints; null when it has none. -
endpoint
-
open
The key plugged in, its smart card interface claimed alone; another app holding it, said so. -
follow
Sioul in the background while a key is wanted: the key plugged in let go and the receiver unregistered, so that other apps have it meanwhile, unless Android's own dialog (its question for the key) is what came in front; back in front, looked for again (asked no more: Android's answer stands until it is unplugged). Reader mode Android ends and brings back itself. -
unplug
No longer asked: the key plugged in powered off, its interface given back, and Android's receiver let go. -
close
-
told
Rust told that something changed: a key came, went, or was refused. -
settings
Android's NFC settings, where it is switched on. -
nativeHeld
Rust told that a key came (android/main.cpp).
-