go-ethereum/docs/_dapp/mobile.md

369 lines
17 KiB
Markdown
Raw Permalink Normal View History

---
title: Geth for Mobile
sort_key: D
---
Embedding clients into mobile devices is an important part of Ethereum's decentralization vision.
This is because being able to verify data, follow the chain and submit transactions without
relying on centralized intermediaries is critical for censorship resistant access
to the network. Doing so on a mobile device is the most convenient route for many users.
This relies on Geth running a [light client](/docs/interface/les) on the mobile
device and exposing an API that developers can use to build mobile apps on top of Geth. This
page outlines how to download Geth for mobile and how to get started with managing Ethereum
accounts in mobile applications. Ethereum mobile development is relatively nascent, but there is
an active developer community. For further information on Geth mobile development visit the
#mobile channel in the [Geth discord](https://discord.gg/wQdpS5aA).
## Download and install
### Android
#### Android Studio
Geth for Mobile bundles can be downloaded directly from [the download page](https://geth.ethereum.org/downloads/)
and inserted into a project in Android Studio via `File -> New -> New module... -> Import .JAR/.AAR Package`.
It is also necessary to configure `gradle` to link the mobile library bundle to the
application. This can be done by adding a new entry to the `dependencies` section of the
`build.gradle` script, pointing it to the module that was just added (named `geth` by default).
```gradle
dependencies {
// All previous dependencies
compile project(':geth')
}
```
#### Manual build
Geth can also be built it locally using a `make` command. This will create an Android
archive called `geth.aar` in the `build/bin` folder that can be imported into Android
Studio as described above.
```shell
$ make android
[...]
Done building.
Import "build/bin/geth.aar" to use the library.
```
### iOS
Geth must be downloaded and built locally for IoS. Building locally is achieved using the
`make` command. This will create an iOS XCode framework called `Geth.framework` in the
`build/bin` folder that can be imported into XCode as described above.
```bash
$ make ios
[...]
Done building.
Import "build/bin/Geth.framework" to use the library.
```
## Mobile API
Similarly to the reusable [Go libraries](/docs/dapp/native), the mobile wrappers focus on
three main usage areas:
- Simplified client side account management
- Remote node interfacing via different transports
- Contract interactions through auto-generated bindings
The Geth mobile API is broadly equivalent to the [Go API](/docs/dapp/native).
The source code can be found in the `mobile` section of Geth's
[Github](https://github.com/ethereum/go-ethereum/tree/master/mobile).
## Mobile Account Management
Best practise for account management is to do it client-side, with all sensitive information
self-contained inside the local application. This ensures the developer/user retains
fine-grained control over the access permissions for user-data instead of outsourcing security
to a third party.
To support this, Geth provides an accounts library that includes the tools required
for secure account management via encrypted keystores and passphrase protected accounts,
similarly to running a full Geth node.
### Encrypted keystores
Access keys to Ethereum accounts should never be stored in plain-text. Instead, they should
be stored encrypted so that even if the mobile device is accessed by a malicious third party
the keys are still hidden under an additional layer of security. Geth provides a keystore
that enables developers to store keys securely using the [`secp256k1` elliptic curve](sec2),
implemented using [`libsecp256k`][secp256k1] and wrapped by [Geth accounts][accounts-go].
Accounts are stored on disk in the [Web3 Secret Storage][secstore] format. Developers should be
aware of these implementation details but are not required to deeply understand the cryptographic
primitives in order to use the keystore.
One thing that should be understood, though, is that the cryptographic primitives underpinning
the keystore can operate in *light* or *standard* mode. Light mode is computationally cheaper, while
standard mode has extra security. Light mode is appropriate for mobile devices, but developers
should be aware that there is a security trade-off.
* *standard* needs 256MB memory and 1 second processing on a modern CPU to access a key
* *light* needs 4MB memory and 100 millisecond processing on a modern CPU to access a key
### Keystores on Android (Java)
The encrypted keystore on Android is implemented by the `KeyStore` class from the
`org.ethereum.geth` package. The configuration constants are located in the `Geth`
abstract class, similarly from the `org.ethereum.geth` package.
Hence to do client side account management on Android, two classes should be
imported into the Java code:
```java
import org.ethereum.geth.Geth;
import org.ethereum.geth.KeyStore;
```
Then new encrypted keystore can be created via:
```java
KeyStore ks = new KeyStore("/path/to/keystore", Geth.LightScryptN, Geth.LightScryptP);
```
The keystore should be in a location writable by the local mobile application but
on-readable for other installed applications such as inside the app's data directory.
If the `KeyStore` is created from within a class extending an Android object, access
to the `Context.getFilesDir()` method is probably provided via `this.getFilesDir()`,
so the keystore path could be set to `this.getFilesDir() + "/keystore"`.
The last two arguments of the `KeyStore` constructor are the crypto parameters defining
how resource-intensive the keystore encryption should be. The choices are
`Geth.StandardScryptN, Geth.StandardScryptP`, `Geth.LightScryptN, Geth.LightScryptP` or
custom numbers. The *light* version is recommended.
### Keystores on iOS (Swift 3)
The encrypted keystore on iOS is implemented by the `GethKeyStore` class from the `Geth`
framework. The configuration constants are located in the same namespace as global
variables. Hence to do client side account management on iOS, `Geth` framework should be
imported into the Swift code:
```swift
import Geth
```
Then a new encrypted account manager can be created using:
```swift
let ks = GethNewKeyStore("/path/to/keystore", GethLightScryptN, GethLightScryptP);
```
The keystore folder needs to be in a location writable by the local mobile application
but non-readable for other installed applications such as inside the app's document
directory. The document directory shopuld be retrievable using
`let datadir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]`,
so the keystore path could be `datadir + "/keystore"`.
The last two arguments of the `GethNewKeyStore` factory method are the crypto parameters
defining how resource-intensive the keystore encryption should be. The choices are
`GethStandardScryptN, GethStandardScryptP`, `GethLightScryptN, GethLightScryptP` or
custom numbers. The *light* version is recommended.
### Account lifecycle
The encrypted keystore can be used for the entire account lifecycle requirements of a mobile
application. This includes the basic functionality of creating new accounts and deleting
existing ones as well as more advanced functions like updating access credentials and account
import/export.
Although the keystore defines the encryption strength it uses to store accounts,
there is no global master password that can grant access to all of them. Rather each
account is maintained individually, and stored on disk in its [encrypted format][secstore]
individually, ensuring a much cleaner and stricter separation of credentials.
This individuality means that any operation requiring access to an account will
need to provide the necessary authentication credentials for that particular account in
the form of a passphrase:
* When creating a new account, the caller must supply a passphrase to encrypt the account
with. This passphrase will be required for any subsequent access.
* When deleting an existing account, the caller must supply a passphrase to verify
ownership of the account. This isn't cryptographically necessary, rather a protective
measure against accidental loss of accounts.
* When updating an existing account, the caller must supply both current and new
passphrases. After completing the operation, the account will not be accessible via the
old passphrase.
* When exporting an existing account, the caller must supply both the current passphrase
to decrypt the account, as well as an export passphrase to re-encrypt it with before
returning the key-file to the user. This is required to allow moving accounts between
devices without sharing original credentials.
* When importing a new account, the caller must supply both the encryption passphrase of
the key-file being imported, as well as a new passphrase with which to store the
account. This is required to allow storing account with different credentials than used
for moving them around.
*Please note, there is no recovery mechanisms for losing the passphrases. The
cryptographic properties of the encrypted keystore (if using the provided parameters)
guarantee that account credentials cannot be brute forced in any meaningful time.*
### Accounts on Android (Java)
An Ethereum account on Android is implemented by the `Account` class from the
`org.ethereum.geth` package. Assuming an instance of a `KeyStore` called
`ks` exists, all of the described lifecycle operations can be executed with
a handful of function calls:
```java
// Create a new account with the specified encryption passphrase.
Account newAcc = ksm.newAccount("Creation password");
// Export the newly created account with a different passphrase. The returned
// data from this method invocation is a JSON encoded, encrypted key-file.
byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password");
// Update the passphrase on the account created above inside the local keystore.
ks.updateAccount(newAcc, "Creation password", "Update password");
// Delete the account updated above from the local keystore.
ks.deleteAccount(newAcc, "Update password");
// Import back the account we've exported (and then deleted) above with yet
// again a fresh passphrase.
Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password");
```
Although instances of `Account` can be used to access various information about specific
Ethereum accounts, they do not contain any sensitive data (such as passphrases or private
keys), rather they act solely as identifiers for client code and the keystore.
### Accounts on iOS (Swift 3)
An Ethereum account on iOS is implemented by the `GethAccount` class from the `Geth`
framework. Assuming an instance of a `GethKeyStore` called `ks` exists, all of the described
lifecycle operations can be executed with a handful of function calls:
```swift
// Create a new account with the specified encryption passphrase.
let newAcc = try! ks?.newAccount("Creation password")
// Export the newly created account with a different passphrase. The returned
// data from this method invocation is a JSON encoded, encrypted key-file.
let jsonKey = try! ks?.exportKey(newAcc!, passphrase: "Creation password", newPassphrase: "Export password")
// Update the passphrase on the account created above inside the local keystore.
try! ks?.update(newAcc, passphrase: "Creation password", newPassphrase: "Update password")
// Delete the account updated above from the local keystore.
try! ks?.delete(newAcc, passphrase: "Update password")
// Import back the account we've exported (and then deleted) above with yet
// again a fresh passphrase.
let impAcc = try! ks?.importKey(jsonKey, passphrase: "Export password", newPassphrase: "Import password")
```
Although instances of `GethAccount` can be used to access various information about
specific Ethereum accounts, they do not contain any sensitive data (such as passphrases or
private keys), rather they act solely as identifiers for client code and the keystore.
## Signing authorization
As mentioned above, account objects do not hold the sensitive private keys of the
associated Ethereum accounts - they are merely placeholders to identify the cryptographic
keys with. All operations that require authorization (e.g. transaction signing) are
performed by the account manager after granting it access to the private keys.
There are a few different ways one can authorize the account manager to execute signing
operations. Since the different methods have very different security guarantees,
it is essential to be clear on how each works:
* **Single authorization**: The simplest way to sign a transaction via the keystore is to
provide the passphrase of the account every time something needs to be signed, which
will ephemerally decrypt the private key, execute the signing operation and immediately
throw away the decrypted key. The drawbacks are that the passphrase needs to be queried
from the user every time, which can become annoying if done frequently; or the
application needs to keep the passphrase in memory, which can have security
consequences if not done properly; and depending on the keystore's configured strength,
constantly decrypting keys can result in non-negligible resource requirements.
* **Multiple authorizations**: A more complex way of signing transactions via the
keystore is to unlock the account via its passphrase once, and allow the account
manager to cache the decrypted private key, enabling all subsequent signing requests to
complete without the passphrase. The lifetime of the cached private key may be managed
manually (by explicitly locking the account back up) or automatically (by providing a
timeout during unlock). This mechanism is useful for scenarios where the user may need
to sign many transactions or the application would need to do so without requiring user
input. The crucial aspect to remember is that **anyone with access to the account
manager can sign transactions while a particular account is unlocked** (e.g. device
left unattended; application running untrusted code).
### Signing on Android (Java)
Assuming an instance of a `KeyStore` called `ks` exists, a new account to sign transactions
can be created using its `newAccount` method. For this demonstation a hard-coded
example transaction is created to sign:
```java
// Create a new account to sign transactions with
Account signer = ks.newAccount("Signer password");
Transaction tx = new Transaction(
1, new Address("0x0000000000000000000000000000000000000000"),
new BigInt(0), new BigInt(0), new BigInt(1), null); // Random empty transaction
BigInt chain = new BigInt(1); // Chain identifier of the main net
```
The transaction `tx` can be signed using the authorization mechanisms described above:
```java
// Sign a transaction with a single authorization
Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain);
// Sign a transaction with multiple manually cancelled authorizations
ks.unlock(signer, "Signer password");
signed = ks.signTx(signer, tx, chain);
ks.lock(signer.getAddress());
// Sign a transaction with multiple automatically cancelled authorizations
ks.timedUnlock(signer, "Signer password", 1000000000);
signed = ks.signTx(signer, tx, chain);
```
### Signing on iOS (Swift 3)
Assuming an instance of a `GethKeyStore` called `ks` exists, a new account
can be created to sign transactions with its `newAccount` method. For
this demonstation a hard-coded example transaction is created to sign:
```swift
// Create a new account to sign transactions with
var error: NSError?
let signer = try! ks?.newAccount("Signer password")
let to = GethNewAddressFromHex("0x0000000000000000000000000000000000000000", &error)
let tx = GethNewTransaction(1, to, GethNewBigInt(0), GethNewBigInt(0), GethNewBigInt(0), nil) // Random empty transaction
let chain = GethNewBigInt(1) // Chain identifier of the main net
```
*Although Swift usually rewrites `NSError` returns to throws, this particular
instance seems to have been missed for some reason (possibly due to it being a
constructor). It will be fixed in a later version of the iOS bindings when the appropriate
fixes are implemented upstream in the `gomobile` project.*
The transaction `tx` can now be signed using the authorization methods described above:
```swift
// Sign a transaction with a single authorization
var signed = try! ks?.signTxPassphrase(signer, passphrase: "Signer password", tx: tx, chainID: chain)
// Sign a transaction with multiple manually cancelled authorizations
try! ks?.unlock(signer, passphrase: "Signer password")
signed = try! ks?.signTx(signer, tx: tx, chainID: chain)
try! ks?.lock(signer?.getAddress())
// Sign a transaction with multiple automatically cancelled authorizations
try! ks?.timedUnlock(signer, passphrase: "Signer password", timeout: 1000000000)
signed = try! ks?.signTx(signer, tx: tx, chainID: chain)
```
## Summary
This page introduced Geth for mobile. In addition to download and installation instructions, basic
account management was demonstrated for mobile applications on iOS and Android.
[sec2]: https://www.secg.org/sec2-v2.pdf
[accounts-go]: https://godoc.org/github.com/ethereum/go-ethereum/accounts
[secp256k1]: https://github.com/bitcoin-core/secp256k1
[secstore]: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition