Class SecurityKeyLink

java.lang.Object
com.aurelienpierre.sioul.SecurityKeyLink

public final class SecurityKeyLink extends Object
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 through transceive(byte[]), on Rust's thread, never Android's main one. A key that leaves the field throws TagLostException, 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

    Fields
    Modifier and Type
    Field
    Description
    private static final Set<String>
    Keys plugged in that Android was asked for (by device name), until unplugged.
    (package private) static final int
    How long a key let go must stay away before Android reads it again.
    (package private) static final String
    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.
    private static String
    One that another app holds, until unplugged or asked again.
    private static Ccid
     
    private static UsbInterface
     
    private static UsbDeviceConnection
     
    private static IsoDep
    The key held now.
    private static Tag
    Its tag, to let it go.
    Sioul's activity going to the background or coming back, heard once a key was wanted.
    private static final Object
     
    private static String
    A key Android was asked for and has not answered yet: its dialog is in front.
    (package private) static final String
    The permission's answer, for this package's receiver alone.
    private static UsbDevice
    A key plugged in, open: its connection, its interface, its messages.
    (package private) static final int
    The longest APDU sent to a key plugged in: short APDUs, chained by Rust, and their header.
    private static boolean
     
    private static Activity
    The activity reader mode is on for.
    private static final Set<String>
    Those Android refused, until unplugged.
    (package private) static final int
    How long one exchange may take: Ed25519 and Curve25519 take far less; the margin is for RSA and slower keys.
    private static boolean
    Unplugged while open: what is sent now fails as a key gone.
    private static UsbManager
    Android's USB service, once a key was wanted: what is plugged in now.
    private static BroadcastReceiver
    The receiver of plugging, unplugging and Android's answer, while a key is wanted and Sioul is in front.
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    private
     
  • Method Summary

    Modifier and Type
    Method
    Description
    (package private) static UsbInterface
    The device's smart card interface (CCID, class 0x0B), with its two bulk endpoints; null when it has none.
    private static void
     
    private static void
     
    private static void
    consider(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 UsbDevice
    deviceOf(Intent intent)
    The device an intent names, as Android before 13 gives it.
    private static UsbEndpoint
    endpoint(UsbInterface face, int direction)
     
    private static byte[]
    exchange(Ccid messages, byte[] command)
     
    private static void
    follow(Activity activity)
    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).
    private static void
    found(Tag tag)
    A tag in reach, on NFC's thread: kept when it speaks ISO-DEP (an OpenPGP card does), Rust told.
    private static void
    heard(Activity activity, UsbManager manager, Intent intent)
     
    (package private) static int[]
    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 void
    letGo(NfcAdapter adapter)
    The key held let go: ignored by Android until it has left the field, then closed.
    private static void
    listen(Activity activity)
    Plugging, unplugging and Android's answer heard while a key is wanted; a key plugged in already looked for.
    private static void
    Rust told that a key came (android/main.cpp).
    private static void
    open(UsbManager manager, UsbDevice device)
    The key plugged in, its smart card interface claimed alone; another app holding it, said so.
    private static byte[]
    plugged(Ccid messages, byte[] command)
    One APDU to the key plugged in, powered on first; unplugged meanwhile, a key gone.
    (package private) static void
    settings(Context context)
    Android's NFC settings, where it is switched on.
    private static void
    start(Activity activity)
     
    (package private) static int
    state(Context context)
    NFC in this phone: 0 none, 1 switched off, 2 ready.
    private static void
    stop(Activity activity)
     
    private static void
    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 void
    unplug(Activity activity)
    No longer asked: the key plugged in powered off, its interface given back, and Android's receiver let go.
    (package private) static boolean
    usbHost(Context context)
    Whether this phone takes a key plugged into its USB-C port (USB host).
    (package private) static void
    want(Context context, boolean wanted)
    A key wanted (reader mode on, the screen kept from captures), or no longer (the key let go, reader mode off).

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • TIMEOUT_MS

      static final int 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

      static final int AWAY_MS
      How long a key let go must stay away before Android reads it again.
      See Also:
    • lock

      private static final Object lock
    • held

      private static IsoDep held
      The key held now.
    • heldTag

      private static Tag heldTag
      Its tag, to let it go.
    • reading

      private static Activity reading
      The activity reader mode is on for.
    • PERMISSION

      static final String PERMISSION
      The permission's answer, for this package's receiver alone.
      See Also:
    • plugged

      private static UsbDevice plugged
      A key plugged in, open: its connection, its interface, its messages.
    • connection

    • claimed

      private static UsbInterface claimed
    • ccid

      private static Ccid ccid
    • powered

      private static boolean powered
    • unplugged

      private static boolean unplugged
      Unplugged while open: what is sent now fails as a key gone.
    • asked

      private static final Set<String> asked
      Keys plugged in that Android was asked for (by device name), until unplugged.
    • refused

      private static final Set<String> refused
      Those Android refused, until unplugged.
    • busy

      private static String busy
      One that another app holds, until unplugged or asked again.
    • usb

      private static UsbManager 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

      private static String 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

      static final String 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

      static final int PLUGGED_LONGEST
      The longest APDU sent to a key plugged in: short APDUs, chained by Rust, and their header.
      See Also:
  • Constructor Details

  • Method Details

    • usbHost

      static boolean usbHost(Context context)
      Whether this phone takes a key plugged into its USB-C port (USB host).
    • state

      static int state(Context context)
      NFC in this phone: 0 none, 1 switched off, 2 ready.
    • want

      static void want(Context context, boolean wanted)
      A key wanted (reader mode on, the screen kept from captures), or no longer (the key let go, reader mode off). Any thread.
    • start

      private static void start(Activity activity)
    • stop

      private static void stop(Activity activity)
    • letGo

      private static void letGo(NfcAdapter adapter)
      The key held let go: ignored by Android until it has left the field, then closed.
    • close

      private static void close(IsoDep iso)
    • found

      private static void found(Tag tag)
      A tag in reach, on NFC's thread: kept when it speaks ISO-DEP (an OpenPGP card does), Rust told.
    • held

      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.
    • transceive

      static byte[] transceive(byte[] command) throws IOException
      One APDU to the key in reach, on Rust's thread; the command wiped once sent.
      Throws:
      IOException
    • plugged

      private static byte[] plugged(Ccid messages, byte[] command) throws IOException
      One APDU to the key plugged in, powered on first; unplugged meanwhile, a key gone.
      Throws:
      IOException
    • exchange

      private static byte[] exchange(Ccid messages, byte[] command) throws IOException
      Throws:
      IOException
    • listen

      private static void listen(Activity activity)
      Plugging, unplugging and Android's answer heard while a key is wanted; a key plugged in already looked for.
    • heard

      private static void heard(Activity activity, UsbManager manager, Intent intent)
    • deviceOf

      private static UsbDevice deviceOf(Intent intent)
      The device an intent names, as Android before 13 gives it.
    • consider

      private static void consider(Activity activity, UsbManager manager, UsbDevice device)
      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

      private static UsbEndpoint endpoint(UsbInterface face, int direction)
    • open

      private static void open(UsbManager manager, UsbDevice device)
      The key plugged in, its smart card interface claimed alone; another app holding it, said so.
    • follow

      private static void follow(Activity activity)
      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

      private static void unplug(Activity activity)
      No longer asked: the key plugged in powered off, its interface given back, and Android's receiver let go.
    • close

      private static void close()
    • told

      private static void told()
      Rust told that something changed: a key came, went, or was refused.
    • settings

      static void settings(Context context)
      Android's NFC settings, where it is switched on.
    • nativeHeld

      private static void nativeHeld()
      Rust told that a key came (android/main.cpp).