taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

gsoc-23-ivan-avalos-report.md (9220B)


      1 [Link to the Android source code](https://git.taler.net/taler-android.git/tree/anastasis?h=dev/ivan-avalos/anastasis)
      2 
      3 # Goals of the project
      4 
      5 Anastasis is a key recovery system that allows users to create
      6 encrypted backups of their keys or secrets, split across multiple
      7 service providers, that can be recovered without needing a
      8 password. The keys are protected using a key derived from the identity
      9 attributes of the user, and can only be recovered by answering a
     10 series of verification steps defined by the user.
     11 
     12 The main goal of this Google Summer of Code project was to integrate
     13 Anastasis into the Taler Android wallet, in order to allow users to
     14 create encrypted and distributed backups of their wallet keys. This
     15 could only be achieved by either implementing Anastasis functionality
     16 within the wallet app, or creating a separate Anastasis app and
     17 enabling integration between the two apps.
     18 
     19 # What I did
     20 
     21 I went for the second option and created a separate Anastasis app,
     22 which has the advantage of allowing Anastasis to be easily used for
     23 other use cases that don't involve the wallet. Besides, it gave me the
     24 freedom to build the app with a more suitable architecture.
     25 
     26 The core functionality for both the wallet and the Anastasis client is
     27 built into the same component: “wallet-core”, written in
     28 TypeScript. The Taler mobile apps and the web extension communicate
     29 with wallet-core using a consistent and well-documented JSON API.
     30 
     31 * [wallet-core.git](https://git.taler.net/wallet-core.git)
     32 * [wallet-core API docs](https://docs.taler.net/wallet/wallet-core.html)
     33 
     34 In order to run wallet-core in the mobile apps, a TypeScript runtime
     35 needs to be embedded. There are many embedable runtimes, but some of
     36 them are too big, and not all of them are optimal for the purposes of
     37 Taler. QuickJS was chosen because of its small size and footprint,
     38 while having good standards compatibility (ES2020).
     39 
     40 * [QuickJS](https://bellard.org/quickjs/)
     41 
     42 The Taler developers then created a cross-platform library built on
     43 top of QuickJS, tailor-made for running wallet-core in mobile, called
     44 “quickjs-tart” (nicknamed “qtart,” which stands for “**Q**uickJS
     45 **TA**ler **R**un**T**ime”). This library implements native QuickJS
     46 modules for cryptographic functions used by wallet-core, pre-compiles
     47 wallet-core to C, and provides a simple callback-based API that can be
     48 easily used from the mobile apps using native bridges (e.g. JNI).
     49 
     50 * [quickjs-tart.git](https://git.taler.net/quickjs-tart.git)
     51 
     52 The repository also includes qtart bindings for Android and iOS, which
     53 handle the bridging so that the apps don't have to. The Android
     54 bindings are available as a Maven library in Maven Central.
     55 
     56 The main challenge for the Anastasis app was that Anastasis uses a
     57 different API within wallet-core, that was originally not exposed to
     58 qtart. This API is designed as a stateless reducer, that takes a state
     59 and a transition and returns another state.
     60 
     61 * [Reducer API docs](https://docs.anastasis.lu/reducer.html)
     62 
     63 This is where things start to get interesting. In order to add
     64 Anastasis support to qtart, I had to expose the Anastasis API in
     65 wallet-core, implement a native QuickJS module for the Argon2 hash
     66 function required by Anastasis, and ensure that the “anastasis-core”
     67 subpackage compiles for QuickJS.
     68 
     69 Once I had initial Anastasis support in qtart, the next step was to
     70 add support for the API in the new Android app. I reused from the
     71 wallet the code that manages the runtime and abstracts the API
     72 operations as requests, but modified it to work with the stateless
     73 model of the reducer. I also wrote Kotlin data structures and
     74 serialization logic for all the JSON objects in the API.
     75 
     76 At this point, I already had the starting point for the rest of the
     77 app. I wrote the app using Jetpack Compose, a technology that I wasn't
     78 very familiar with, so I had to learn it on the go, using the code of
     79 other free-software apps I've used before as examples.
     80 
     81 After some time, I managed to successfully create an abstraction for
     82 the reducer on top of Compose, using a global view model injected to
     83 the composables with Hilt. This view model contains the reducer state
     84 as a StateFlow, and a ReducerManager object that allows the views to
     85 safely interact with the API and update the state accordingly.
     86 
     87 The routing part was easy, as the reducer state contains a property
     88 that indicates the current step in the backup/recovery flow. The root
     89 composable listens to this property and shows the screen that
     90 corresponds to the backup/recovery step.
     91 
     92 At some point when implementing the backup flow, I realized that I had
     93 to use another (undocumented) API, separate to the reducer API, to
     94 contact the backup providers and fetch the list of supported
     95 verification methods. Again, I had to expose this API from
     96 wallet-core, build quickjs-tart with this modification, and implement
     97 this API on the app.
     98 
     99 And, at some point when implementing the recovery flow, I learned
    100 about YET another undocumented API, also separate to the reducer API,
    101 that was needed to contact the backup providers and fetch the list of
    102 secrets and recovery policies that match the identity attributes of
    103 the user.
    104 
    105 This resulted in another adventure, as this time, there was an issue
    106 with data encoding and decoding in qtart related to some cryptographic
    107 function that resulted in this API returning garbage. Florian helped
    108 me fix this issue, and soon afterwards, the API was working just fine!
    109 
    110 After some more work, I managed to have fully working backup and
    111 recovery, with many missing features, of course, but with the basics
    112 working just fine. The deadline was two days away, and I still had to
    113 implement the Taler integration! Oh, no!
    114 
    115 Well, unfortunately I couldn't get Taler integration working before
    116 the deadline, but at least I got the backup fees showing in the UI,
    117 and also added support for adding and removing backup providers, so
    118 that users can choose where to backup their keys and learn about the
    119 fees each provider demands beforehand. Isn't it cool?
    120 
    121 # The current state
    122 
    123 The Anastasis app, as it stands now, provides basic support for backup
    124 and recovery of plain text keys (no files!). It allows users to manage
    125 the backup providers and learn about the fees they demand, as well as
    126 the fees per verification challenge (e.g. e-mail, SMS, question).
    127 
    128 # What's left to do
    129 
    130 There are many missing features:
    131 
    132 * Some challenge types are not yet supported, such as IBAN, physical
    133   mail, and TOTP.
    134 * It is only possible to backup plain-text secrets, as there's no
    135   functionality in the app to upload and download files.
    136 * There's no support for paying backup providers with Taler.
    137 * There's no mechanism to trigger a key backup from the wallet.
    138 * The reducer state can't be saved or restored from a JSON file, nor
    139   is it stored in shared preferences.
    140 
    141 I already have a working Anastasis instance that requires payments
    142 with Taler, which I've been using to test Taler integration and
    143 implement it in the app. This shouldn't take too long to fully
    144 implement, but afterwards, better integration with the wallet would
    145 still be needed to make the UX simpler.
    146 
    147 Of course, a mechanism to trigger a backup or recovery from the wallet
    148 is also missing. This will be implemented with an intent to the
    149 Anastasis app with the data to backup or recover, and another intent
    150 to the wallet app to hand-in the recovered data. Not too difficult!
    151 
    152 # Merged code
    153 
    154 All the required code in wallet-core and qtart has already been
    155 merged. However, the latest wallet-core release (v0.9.3-dev.19, as of
    156 now) still doesn't include them. The code for the Anastasis app,
    157 present in the branch `dev/ivan-avalos/anastasis` of the taler-android
    158 mono-repo, hasn't been merged yet, and it's still pending review from
    159 the Taler developers.
    160 
    161 * [taler-android.git](https://git.taler.net/taler-android.git)
    162 
    163 # What I learned / challenges
    164 
    165 One of the hardest parts, and the one that took me the longest, was
    166 getting familiar with Jetpack Compose, especially creating an entire
    167 app using only Compose. How would the architecture look like? How was
    168 I supposed to translate the reducer model, not only to a screen, but
    169 to an entire app? 
    170 
    171 I had to look in a lot of places for inspiration, as I wanted
    172 something clean, easy to work with, and above all, something that
    173 actually made sense. I ended up modelling the app after the
    174 architecture of the existing Anastasis web UI, written in React. As it
    175 turned out, Compose is not too different from React!
    176 
    177 Once I solved the initial challenges, I had a burst of creativity,
    178 which helped me work fast on implementing most of the functionality of
    179 the app. I refactored the app multiple times, and it always resulted
    180 in a cleaner codebase and a better architecture. I did my best to have
    181 a solid and sleek initial version, and so far it has paid off: less
    182 headaches when working with my own code, and a modern and good-looking
    183 app that I hope that will be a pleasure to use in the future.
    184 
    185 During this Google Summer of Code, I learned to not be afraid of new
    186 things, as in the end, there's always a way to figure them out. With
    187 enough help from other people, and enough exploration, it's possible
    188 for this process to be much shorter.