ACINQ / phoenix-kmm

Apache License 2.0
23 stars 4 forks source link

Cloud backup for transactions #191

Closed robbiehanson closed 3 years ago

robbiehanson commented 3 years ago

Phoenix iOS can now optionally backup transactions in iCloud:

(This is a huge PR, so I'll be splitting this into multiple smaller PR's in the next few days.)

Overview:

We upload completed transactions to the user's iCloud.

More specifically, we use Apple's CloudKit, which provides us with a database-like API. The API is limited to {iCloud-user, application}. Which means only the logged-in iCloud user can access the database. And only our app can access our database(s).

We create a different "container" for each wallet. So each wallet is properly segregated. And mainnet/testnet segregation is handled too.

Security considerations:

1) The data stored in the cloud is encrypted, and can only be decrypted with the seed. We serialize each payment into a blob, and then we encrypt the blob before uploading it. The cloudKey used for encryption/decryption is derived from the seed using a derivation path. (The derivation path is different for mainnet vs testnet.)

2) Each wallet gets its own segregated "cloud container". The name of the container is Hash(cloudKey + nodeID). This prevents leakage of the user's nodeID.

3) The size of a serialized IncomingPayment is generally smaller than an OutgoingPayment. In order to obfuscate the payment type, we add padding to the serialized cleartext prior to encryption. The current algorithm is:

4) The other known attack uses metadata & timestamps. By default, Alice's wallet syncs a payment to the cloud as soon as it completes. However, this associates Alice with a completed Lightning payment made at that time. Thus, Alice can opt-into "randomized upload delays", where the SyncManager injects a random delay automatically. In addition, this dely is exposed to Alice within the UI, and she can manually skip it if needed.

Changes in the Kotlin layer:

Changes in iOS UI:

New options in the settings to control cloud backup:

Screen Shot 2021-07-29 at 13 00 08

The "cloud sync status" has been integrated into the AppStatusButton on the HomeScreen (upper left corner), as well as the AppStatusPopover:

Screen Shot 2021-07-29 at 11 25 45

The status is dynamic & interactive:

Screen Shot 2021-07-29 at 11 51 49
robbiehanson commented 3 years ago

Implementation Notes:

Cloud encryption key

In Wallet.kt there is a new function cloudKeyAndEncryptionNodeId(). It derives a "cloudKey" for the wallet, which is then used to encrypt/decrypt the content that's synced to iCloud.

It also returns an "encrypted nodeID", which is just some string that is derived from the wallet, but which doesn't leak identifying information. We use this to segregate wallets within iCloud. (Each wallet gets its own cloud "container", which is essentially a separate database.)

These values are ultimately returned to the Swift layer via PhoenixBusiness.loadWallet()

CloudKitPayments.sq

There's a new SQLDelight file. The design is specific to Apple's CloudKit syncing requirements, so it's named accordingly. There's also a migrations file.

Note that this is shared between iOS & Android. Which means Android will create these tables too, even though it won't ever use them. I don't know if there's an easy way around this. More drastic measures might not be worth the risk. I assume that Android will eventually get its own dedicated tables too, and iOS will quietly create them, even though they're not needed. But it's a one-time operation.

Encryption routine

In SyncManager.swift, there's a function serializeAndEncryptRow which performs the encryption routine:

dpad85 commented 3 years ago

Which means Android will create these tables too, even though it won't ever use them. I don't know if there's an easy way around this. More drastic measures might not be worth the risk.

I agree, I don't think there would be any easier solution than simply ignore this database when on Android 👍

robbiehanson commented 3 years ago

Testing Notes:

If you already have an older version installed, then upgrading to this version means that the existing payment history will get automatically synced to the cloud.

If you disable/enable cloud sync (via Settings / Cloud backup), there is a 30 second timer that's used before the changes go into effect. The timer can be seen via the AppStatusPopover.

If you disable cloud sync, we delete the cloud container. (i.e. we delete the CloudKit RecordZone for the wallet) If you re-enable cloud sync, we automatically re-upload all existing transactions.

This branch increments the local database to v2 (via SQLDelight migration). If you try to re-install an older version (e.g. switch back to master, and build-and-go, without deleting app from simulator/device), then SQLDelight will crash the app.

robbiehanson commented 3 years ago

If Phoenix exceeds the quota allowed on iCloud, what happens in the app?

For any kind of unusual error, the sync system executes exponential backoff. And the UI displays some useful information about the error:

Screen Shot 2021-08-19 at 15 48 32
robbiehanson commented 3 years ago

The cloud backup setting screen should mention that only payments are uploaded and not the seed, to prevent any misunderstandings.

Done in d188ce7

Screen Shot 2021-08-20 at 09 33 52
dpad85 commented 3 years ago
Screen Shot 2021-08-20 at 09 33 52

Looks good!

Regarding the seed backup, I think we should mention after the You are responsible for backing up your seed title that backing up the mnemonics yourself is the more secure option, if done correctly. Also in a future PR we will have to add a help ? button like in the App Access screen, that provides technical details about this feature and explains the trade-offs.

robbiehanson commented 3 years ago

I think we should mention after the You are responsible for backing up your seed title that backing up the mnemonics yourself is the more secure option, if done correctly

Screen Shot 2021-08-23 at 11 31 30

Also in a future PR we will have to add a help ? button like in the App Access screen, that provides technical details about this feature and explains the trade-offs.

That's a good idea :thumbsup: