mirror of https://github.com/ethereum/go-ethereum
docs: mobile pages -> new single page (#25075)
* initial commit for new mobile page * refine mobile page * Update docs/_dapp/mobile.md fix typo Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> * Update mobile.md Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: rjl493456442 <garyrong0905@gmail.com>pull/25999/head
parent
824d5177de
commit
392cf67949
@ -1,323 +0,0 @@ |
|||||||
--- |
|
||||||
title: Mobile Account Management |
|
||||||
sort_key: G |
|
||||||
--- |
|
||||||
|
|
||||||
To provide Ethereum integration for your mobile applications, the very first thing you |
|
||||||
should be interested in doing is account management. |
|
||||||
|
|
||||||
Although all current leading Ethereum implementations provide account management built in, |
|
||||||
it is ill advised to keep accounts in any location that is shared between multiple |
|
||||||
applications and/or multiple people. The same way you do not entrust your ISP (who is |
|
||||||
after all your gateway into the internet) with your login credentials; you should not |
|
||||||
entrust an Ethereum node (who is your gateway into the Ethereum network) with your |
|
||||||
credentials either. |
|
||||||
|
|
||||||
The proper way to handle user accounts in your mobile applications is to do client side |
|
||||||
account management, everything self-contained within your own application. This way you |
|
||||||
can ensure as fine grained (or as coarse) access permissions to the sensitive data as |
|
||||||
deemed necessary, without relying on any third party application's functionality and/or |
|
||||||
vulnerabilities. |
|
||||||
|
|
||||||
To support this, `go-ethereum` provides a simple, yet thorough accounts library that gives |
|
||||||
you all the tools to do properly secured account management via encrypted keystores and |
|
||||||
passphrase protected accounts. You can leverage all the security of the `go-ethereum` |
|
||||||
crypto implementation while at the same time running everything in your own application. |
|
||||||
|
|
||||||
## Encrypted keystores |
|
||||||
|
|
||||||
Although handling your users' accounts locally on their own mobile device does provide |
|
||||||
certain security guarantees, access keys to Ethereum accounts should never lay around in |
|
||||||
clear-text form. As such, we provide an encrypted keystore that provides the proper |
|
||||||
security guarantees for you without requiring a thorough understanding from your part of |
|
||||||
the associated cryptographic primitives. |
|
||||||
|
|
||||||
The important thing to know when using the encrypted keystore is that the cryptographic |
|
||||||
primitives used within can operate either in *standard* or *light* mode. The former |
|
||||||
provides a higher level of security at the cost of increased computational burden and |
|
||||||
resource consumption: |
|
||||||
|
|
||||||
* *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 |
|
||||||
|
|
||||||
As such, *light* is more suitable for mobile applications, but you should be aware of the |
|
||||||
trade-offs nonetheless. |
|
||||||
|
|
||||||
*For those interested in the cryptographic and/or implementation details, the key-store |
|
||||||
uses the `secp256k1` elliptic curve as defined in the [Standards for Efficient |
|
||||||
Cryptography](sec2), implemented by the [`libsecp256k`][secp256k1] library and wrapped by |
|
||||||
[`github.com/ethereum/go-ethereum/accounts`][accounts-go]. Accounts are stored on disk in |
|
||||||
the [Web3 Secret Storage][secstore] format.* |
|
||||||
|
|
||||||
### Keystores on Android (Java) |
|
||||||
|
|
||||||
The encrypted keystore on Android is implemented by the `KeyStore` class from the |
|
||||||
`org.ethereum.geth` package. The configuration constants (for the *standard* or *light* |
|
||||||
security modes described above) are located in the `Geth` abstract class, similarly from |
|
||||||
the `org.ethereum.geth` package. Hence to do client side account management on Android, |
|
||||||
you'll need to import two classes into your Java code: |
|
||||||
|
|
||||||
```java |
|
||||||
import org.ethereum.geth.Geth; |
|
||||||
import org.ethereum.geth.KeyStore; |
|
||||||
``` |
|
||||||
|
|
||||||
Afterwards you can create a new encrypted keystore via: |
|
||||||
|
|
||||||
```java |
|
||||||
KeyStore ks = new KeyStore("/path/to/keystore", Geth.LightScryptN, Geth.LightScryptP); |
|
||||||
``` |
|
||||||
|
|
||||||
The path to the keystore folder needs to be a location that is writable by the local |
|
||||||
mobile application but non-readable for other installed applications (for security reasons |
|
||||||
obviously), so we'd recommend placing it inside your app's data directory. If you are |
|
||||||
creating the `KeyStore` from within a class extending an Android object, you will most |
|
||||||
probably have access to the `Context.getFilesDir()` method via `this.getFilesDir()`, so |
|
||||||
you could set the keystore path 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. You can choose between |
|
||||||
`Geth.StandardScryptN, Geth.StandardScryptP`, `Geth.LightScryptN, Geth.LightScryptP` or |
|
||||||
specify your own numbers (please make sure you understand the underlying cryptography for |
|
||||||
this). We recommend using the *light* version. |
|
||||||
|
|
||||||
### Keystores on iOS (Swift 3) |
|
||||||
|
|
||||||
The encrypted keystore on iOS is implemented by the `GethKeyStore` class from the `Geth` |
|
||||||
framework. The configuration constants (for the *standard* or *light* security modes |
|
||||||
described above) are located in the same namespace as global variables. Hence to do client |
|
||||||
side account management on iOS, you'll need to import the framework into your Swift code: |
|
||||||
|
|
||||||
```swift |
|
||||||
import Geth |
|
||||||
``` |
|
||||||
|
|
||||||
Afterwards you can create a new encrypted account manager via: |
|
||||||
|
|
||||||
```swift |
|
||||||
let ks = GethNewKeyStore("/path/to/keystore", GethLightScryptN, GethLightScryptP); |
|
||||||
``` |
|
||||||
|
|
||||||
The path to the keystore folder needs to be a location that is writable by the local |
|
||||||
mobile application but non-readable for other installed applications (for security reasons |
|
||||||
obviously), so we'd recommend placing it inside your app's document directory. You should |
|
||||||
be able to retrieve the document directory via `let datadir = |
|
||||||
NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]`, so you |
|
||||||
could set the keystore path to `datadir + "/keystore"`. |
|
||||||
|
|
||||||
The last two arguments of the `GethNewKeyStore` factory method are the crypto parameters |
|
||||||
defining how resource-intensive the keystore encryption should be. You can choose between |
|
||||||
`GethStandardScryptN, GethStandardScryptP`, `GethLightScryptN, GethLightScryptP` or |
|
||||||
specify your own numbers (please make sure you understand the underlying cryptography for |
|
||||||
this). We recommend using the *light* version. |
|
||||||
|
|
||||||
## Account lifecycle |
|
||||||
|
|
||||||
Having created an encrypted keystore for your Ethereum accounts, you can use this for the |
|
||||||
entire account lifecycle requirements of your mobile application. This includes the basic |
|
||||||
functionality of creating new accounts and deleting existing ones; as well as the more |
|
||||||
advanced functionality of updating access credentials, exporting existing accounts, and |
|
||||||
importing them on another device. |
|
||||||
|
|
||||||
Although the keystore defines the encryption strength it uses to store your 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 however 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, the lack of which |
|
||||||
will forever forfeit using the newly created account. |
|
||||||
* 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 any more. |
|
||||||
* 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 passhprase 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 we already have an instance of a `KeyStore` called |
|
||||||
`ks` from the previous section, we can easily execute all of the described lifecycle |
|
||||||
operations 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 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 we already have an instance of a `GethKeyStore` called `ks` from the |
|
||||||
previous section, we can easily execute all of the described lifecycle operations 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 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, but 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, each having its advantages and drawbacks. Since the different methods have |
|
||||||
wildly 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). |
|
||||||
|
|
||||||
*Note, creating transactions is out of scope here, so the remainder of this section will |
|
||||||
assume we already have a transaction to sign, and will focus only on creating an |
|
||||||
authorized version of it. Creating an actually meaningful transaction will be covered |
|
||||||
later.* |
|
||||||
|
|
||||||
### Signing on Android (Java) |
|
||||||
|
|
||||||
Assuming we already have an instance of a `KeyStore` called `ks` from the previous |
|
||||||
sections, we can create a new account to sign transactions with via it's already |
|
||||||
demonstrated `newAccount` method; and to avoid going into transaction creation for now, we |
|
||||||
can hard-code a random transaction to sign instead. |
|
||||||
|
|
||||||
```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 |
|
||||||
``` |
|
||||||
|
|
||||||
With the boilerplate out of the way, we can now sign transaction 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 we already have an instance of a `GethKeyStore` called `ks` from the previous |
|
||||||
sections, we can create a new account to sign transactions with via it's already |
|
||||||
demonstrated `newAccount` method; and to avoid going into transaction creation for now, we |
|
||||||
can hard-code a random transaction to sign instead. |
|
||||||
|
|
||||||
```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 |
|
||||||
``` |
|
||||||
|
|
||||||
*Note, 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 |
|
||||||
fixed are implemented upstream in the `gomobile` project.* |
|
||||||
|
|
||||||
With the boilerplate out of the way, we can now sign transaction 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) |
|
||||||
``` |
|
||||||
|
|
||||||
[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 |
|
@ -1,180 +1,368 @@ |
|||||||
--- |
--- |
||||||
title: Mobile API |
title: Geth for Mobile |
||||||
sort_key: F |
sort_key: F |
||||||
--- |
--- |
||||||
|
|
||||||
The Ethereum blockchain along with its two extension protocols Whisper and Swarm was |
Embedding clients into mobile devices is an important part of Ethereum's decentralization vision. |
||||||
originally conceptualized to become the supporting pillar of web3, providing the |
This is because being able to verify data, follow the chain and submit transactions without |
||||||
consensus, messaging and storage backbone for a new generation of distributed (actually, |
relying on centralized intermediaries is critical for censorship resistant access |
||||||
decentralized) applications called DApps. |
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). |
||||||
|
|
||||||
The first incarnation towards this dream of web3 was a command line client providing an |
## Download and install |
||||||
RPC interface into the peer-to-peer protocols. The client was soon enough extended with a |
|
||||||
web-browser-like graphical user interface, permitting developers to write DApps based on |
|
||||||
the tried and proven HTML/CSS/JS technologies. |
|
||||||
|
|
||||||
As many DApps have more complex requirements than what a browser environment can handle, |
### Android |
||||||
it became apparent that providing programmatic access to the web3 pillars would open the |
|
||||||
door towards a new class of applications. As such, the second incarnation of the web |
|
||||||
dream is to open up all our technologies for other projects as reusable components. |
|
||||||
|
|
||||||
Starting with the 1.5 release family of `go-ethereum`, we transitioned away from providing |
#### Android Studio |
||||||
only a full blown Ethereum client and started shipping official Go packages that could be |
|
||||||
embedded into third party desktop and server applications. It took only a small leap from |
|
||||||
here to begin porting our code to mobile platforms. |
|
||||||
|
|
||||||
## Quick overview |
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`. |
||||||
|
|
||||||
Similarly to our reusable Go libraries, the mobile wrappers also focus on four main usage |
It is also necessary to configure `gradle` to link the mobile library bundle to the |
||||||
areas: |
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 |
- Simplified client side account management |
||||||
- Remote node interfacing via different transports |
- Remote node interfacing via different transports |
||||||
- Contract interactions through auto-generated bindings |
- Contract interactions through auto-generated bindings |
||||||
- In-process Ethereum, Whisper and Swarm peer-to-peer node |
|
||||||
|
|
||||||
You can watch a quick overview about these in Peter's (@karalabe) talk titled "Import |
The Geth mobile API is broadly equivalent to the [Go API](/docs/dapp/native). |
||||||
Geth: Ethereum from Go and beyond", presented at the Ethereum Devcon2 developer conference |
The source code can be found in the `mobile` section of Geth's |
||||||
in September, 2016 (Shanghai). Slides are [available |
[Github](https://github.com/ethereum/go-ethereum/tree/master/mobile). |
||||||
here](https://ethereum.karalabe.com/talks/2016-devcon.html). |
|
||||||
|
|
||||||
[![Peter's Devcon2 talk](https://img.youtube.com/vi/R0Ia1U9Gxjg/0.jpg)](https://www.youtube.com/watch?v=R0Ia1U9Gxjg) |
## Mobile Account Management |
||||||
|
|
||||||
## Library bundles |
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. |
||||||
|
|
||||||
The `go-ethereum` mobile library is distributed either as an Android `.aar` archive |
To support this, Geth provides an accounts library that includes the tools required |
||||||
(containing binaries for `arm-7`, `arm64`, `x86` and `x64`); or as an iOS XCode framework |
for secure account management via encrypted keystores and passphrase protected accounts, |
||||||
(containing binaries for `arm-7`, `arm64` and `x86`). We do not provide library bundles |
similarly to running a full Geth node. |
||||||
for Windows phone the moment. |
|
||||||
|
|
||||||
### Android archive |
### Encrypted keystores |
||||||
|
|
||||||
The simplest way to use `go-ethereum` in your Android project is through a Maven |
Access keys to Ethereum accounts should never be stored in plain-text. Instead, they should |
||||||
dependency. We provide bundles of all our stable releases (starting from v1.5.0) through |
be stored encrypted so that even if the mobile device is accessed by a malicious third party |
||||||
Maven Central, and also provide the latest develop bundle through the Sonatype OSS |
the keys are still hidden under an additional layer of security. Geth provides a keystore |
||||||
repository. |
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. |
||||||
|
|
||||||
#### Stable dependency (Maven Central) |
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. |
||||||
|
|
||||||
To add an Android dependency to the **stable** library release of `go-ethereum`, you'll |
* *standard* needs 256MB memory and 1 second processing on a modern CPU to access a key |
||||||
need to ensure that the Maven Central repository is enabled in your Android project, and |
* *light* needs 4MB memory and 100 millisecond processing on a modern CPU to access a key |
||||||
that the `go-ethereum` code is listed as a required dependency of your application. You |
|
||||||
can do both of these by editing the `build.gradle` script in your Android app's folder: |
|
||||||
|
|
||||||
```gradle |
### Keystores on Android (Java) |
||||||
repositories { |
|
||||||
mavenCentral() |
|
||||||
} |
|
||||||
|
|
||||||
dependencies { |
The encrypted keystore on Android is implemented by the `KeyStore` class from the |
||||||
// All your previous dependencies |
`org.ethereum.geth` package. The configuration constants are located in the `Geth` |
||||||
compile 'org.ethereum:geth:1.5.2' // Change the version to the latest release |
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; |
||||||
``` |
``` |
||||||
|
|
||||||
#### Develop dependency (Sonatype) |
Then new encrypted keystore can be created via: |
||||||
|
|
||||||
To add an Android dependency to the current version of `go-ethereum`, you'll need to |
```java |
||||||
ensure that the Sonatype snapshot repository is enabled in your Android project, and that |
KeyStore ks = new KeyStore("/path/to/keystore", Geth.LightScryptN, Geth.LightScryptP); |
||||||
the `go-ethereum` code is listed as a required `SNAPSHOT` dependency of your application. |
``` |
||||||
You can do both of these by editing the `build.gradle` script in your Android app's |
|
||||||
folder: |
|
||||||
|
|
||||||
```gradle |
The keystore should be in a location writable by the local mobile application but |
||||||
repositories { |
on-readable for other installed applications such as inside the app's data directory. |
||||||
maven { |
If the `KeyStore` is created from within a class extending an Android object, access |
||||||
url "https://oss.sonatype.org/content/groups/public" |
to the `Context.getFilesDir()` method is probably provided via `this.getFilesDir()`, |
||||||
} |
so the keystore path could be set to `this.getFilesDir() + "/keystore"`. |
||||||
} |
|
||||||
|
|
||||||
dependencies { |
The last two arguments of the `KeyStore` constructor are the crypto parameters defining |
||||||
// All your previous dependencies |
how resource-intensive the keystore encryption should be. The choices are |
||||||
compile 'org.ethereum:geth:1.5.3-SNAPSHOT' // Change the version to the latest release |
`Geth.StandardScryptN, Geth.StandardScryptP`, `Geth.LightScryptN, Geth.LightScryptP` or |
||||||
} |
custom numbers. The *light* version is recommended. |
||||||
``` |
|
||||||
|
|
||||||
#### Custom dependency |
|
||||||
|
|
||||||
If you prefer not to depend on Maven Central or Sonatype; or would like to access an older |
### Keystores on iOS (Swift 3) |
||||||
develop build not available any more as an online dependency, you can download any bundle |
|
||||||
directly from [our website](https://geth.ethereum.org/downloads/) and insert it into your |
|
||||||
project in Android Studio via `File -> New -> New module... -> Import .JAR/.AAR Package`. |
|
||||||
|
|
||||||
You will also need to configure `gradle` to link the mobile library bundle to your |
The encrypted keystore on iOS is implemented by the `GethKeyStore` class from the `Geth` |
||||||
application. This can be done by adding a new entry to the `dependencies` section of your |
framework. The configuration constants are located in the same namespace as global |
||||||
`build.gradle` script, pointing it to the module you just added (named `geth` by default). |
variables. Hence to do client side account management on iOS, `Geth` framework should be |
||||||
|
imported into the Swift code: |
||||||
|
|
||||||
```gradle |
```swift |
||||||
dependencies { |
import Geth |
||||||
// All your previous dependencies |
|
||||||
compile project(':geth') |
|
||||||
} |
|
||||||
``` |
``` |
||||||
|
|
||||||
#### Manual builds |
Then a new encrypted account manager can be created using: |
||||||
|
|
||||||
Lastly, if you would like to make modifications to the `go-ethereum` mobile code and/or |
```swift |
||||||
build it yourself locally instead of downloading a pre-built bundle, you can do so using a |
let ks = GethNewKeyStore("/path/to/keystore", GethLightScryptN, GethLightScryptP); |
||||||
`make` command. This will create an Android archive called `geth.aar` in the `build/bin` |
``` |
||||||
folder that you can import into your Android Studio as described above. |
|
||||||
|
|
||||||
```bash |
The keystore folder needs to be in a location writable by the local mobile application |
||||||
$ make android |
but non-readable for other installed applications such as inside the app's document |
||||||
[...] |
directory. The document directory shopuld be retrievable using |
||||||
Done building. |
`let datadir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]`, |
||||||
Import "build/bin/geth.aar" to use the library. |
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"); |
||||||
``` |
``` |
||||||
|
|
||||||
### iOS framework |
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: |
||||||
|
|
||||||
The simplest way to use `go-ethereum` in your iOS project is through a |
```swift |
||||||
[CocoaPods](https://cocoapods.org/) dependency. We provide bundles of all our stable |
// Create a new account with the specified encryption passphrase. |
||||||
releases (starting from v1.5.3) and also latest develop versions. |
let newAcc = try! ks?.newAccount("Creation password") |
||||||
|
|
||||||
#### Automatic dependency |
// 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") |
||||||
|
|
||||||
To add an iOS dependency to the current stable or latest develop version of `go-ethereum`, |
// Update the passphrase on the account created above inside the local keystore. |
||||||
you'll need to ensure that your iOS XCode project is configured to use CocoaPods. |
try! ks?.update(newAcc, passphrase: "Creation password", newPassphrase: "Update password") |
||||||
Detailing that is out of scope in this document, but you can find a guide in the upstream |
|
||||||
[Using CocoaPods](https://guides.cocoapods.org/using/using-cocoapods.html) page. |
|
||||||
Afterwards you can edit your `Podfile` to list `go-ethereum` as a dependency: |
|
||||||
|
|
||||||
```ruby |
// Delete the account updated above from the local keystore. |
||||||
target 'MyApp' do |
try! ks?.delete(newAcc, passphrase: "Update password") |
||||||
# All your previous dependencies |
|
||||||
pod 'Geth', '1.5.4' # Change the version to the latest release |
// Import back the account we've exported (and then deleted) above with yet |
||||||
end |
// again a fresh passphrase. |
||||||
|
let impAcc = try! ks?.importKey(jsonKey, passphrase: "Export password", newPassphrase: "Import password") |
||||||
``` |
``` |
||||||
|
|
||||||
Alternatively, if you'd like to use the latest develop version, replace the package |
Although instances of `GethAccount` can be used to access various information about |
||||||
version `1.5.4` with `~> 1.5.5-unstable` to switch to pre-releases and to always pull in |
specific Ethereum accounts, they do not contain any sensitive data (such as passphrases or |
||||||
the latest bundle from a particular release family. |
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 |
||||||
|
``` |
||||||
|
|
||||||
#### Custom dependency |
The transaction `tx` can be signed using the authorization mechanisms described above: |
||||||
|
|
||||||
If you prefer not to depend on CocoaPods; or would like to access an older develop build |
```java |
||||||
not available any more as an online dependency, you can download any bundle directly from |
// Sign a transaction with a single authorization |
||||||
[our website](https://geth.ethereum.org/downloads/) and insert it into your project in |
Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain); |
||||||
XCode via `Project Settings -> Build Phases -> Link Binary With Libraries`. |
|
||||||
|
|
||||||
Do not forget to extract the framework from the compressed `.tar.gz` archive. You can do |
// Sign a transaction with multiple manually cancelled authorizations |
||||||
that either using a GUI tool or from the command line via (replace the archive with your |
ks.unlock(signer, "Signer password"); |
||||||
downloaded file): |
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); |
||||||
``` |
``` |
||||||
tar -zxvf geth-ios-all-1.5.3-unstable-e05d35e6.tar.gz |
|
||||||
|
### 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 |
||||||
``` |
``` |
||||||
|
|
||||||
#### Manual builds |
*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.* |
||||||
|
|
||||||
Lastly, if you would like to make modifications to the `go-ethereum` mobile code and/or |
The transaction `tx` can now be signed using the authorization methods described above: |
||||||
build it yourself locally instead of downloading a pre-built bundle, you can do so using a |
|
||||||
`make` command. This will create an iOS XCode framework called `Geth.framework` in the |
|
||||||
`build/bin` folder that you can import into XCode as described above. |
|
||||||
|
|
||||||
```bash |
```swift |
||||||
$ make ios |
// Sign a transaction with a single authorization |
||||||
[...] |
var signed = try! ks?.signTxPassphrase(signer, passphrase: "Signer password", tx: tx, chainID: chain) |
||||||
Done building. |
|
||||||
Import "build/bin/Geth.framework" to use the library. |
// 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 |
||||||
|
Loading…
Reference in new issue