Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronous backups #166

Merged
merged 25 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d13a653
chore: wip
Jasonvdb Aug 8, 2023
ead2eeb
Merge branch 'master' into sync-backup
Jasonvdb Aug 21, 2023
3cbb82e
feat: ios remote backup sync
Jasonvdb Aug 22, 2023
32e9a1b
feat: ios backup encryption
Jasonvdb Aug 22, 2023
cd08d8c
feat: fastify server
Jasonvdb Aug 24, 2023
a3fe5fb
wip: slashauth
Jasonvdb Aug 24, 2023
0f9fc53
feat: list backed up files from server, full restore from server
Jasonvdb Aug 31, 2023
135127b
feat: slashauth used in backup server
Jasonvdb Sep 1, 2023
8829635
fix: using slashauth magic link
Jasonvdb Sep 4, 2023
9a5ce5e
wip: sodium
Jasonvdb Sep 6, 2023
71dd44f
feat: android backup encryption
Jasonvdb Sep 13, 2023
98df4c4
feat: android remote backup restore
Jasonvdb Sep 14, 2023
fb7111e
feat: ios signed backup uploads
Jasonvdb Sep 18, 2023
d25c176
Merge branch 'master' into sync-backup
Jasonvdb Sep 20, 2023
901d27b
feat: backup challenge/response for retrieval with node signing
Jasonvdb Sep 20, 2023
c3a9c8c
feat: backup server using storage-abstraction
Jasonvdb Sep 20, 2023
f3494da
feat: server signs client challenge
Jasonvdb Sep 21, 2023
f96e748
feat: ios validating server's signed challenge response
Jasonvdb Sep 22, 2023
467fcba
feat: use ln-verifymessagejs for server message signing
Jasonvdb Sep 25, 2023
9d9de90
feat: ios bearer token auth for restoring backups
Jasonvdb Sep 28, 2023
176497f
feat: android verifying server response on persist
Jasonvdb Sep 29, 2023
d3136e9
feat: android bearer token auth for restoring backups
Jasonvdb Sep 29, 2023
d8fb401
chore: backup documentation
Jasonvdb Oct 2, 2023
e5aa676
fix: clear previously cached bearer token on backup client setup
Jasonvdb Oct 4, 2023
ff9f5d4
Merge branch 'master' into sync-backup
Jasonvdb Oct 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backup-server/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
HOST=0.0.0.0
PORT=3003
# npm run create-keypair
SECRET_KEY=
PUBLIC_KEY=
3 changes: 3 additions & 0 deletions backup-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
local-storage
node_modules
.env
46 changes: 46 additions & 0 deletions backup-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# react-native-ldk backup server

This a server allows apps using the react-native-ldk to persist all node state remotely and can be restored only using the seed.

## Running the server
``` bash
npm i

cp .env.template .env

npm run create-keypair

#Paste new key pair in .env

npm start
```
** Remember to update wallet env with new backup server pub key

## Clients
[Swift](https://github.com/synonymdev/react-native-ldk/blob/master/lib/ios/Classes/BackupClient.swift)
[Kotlin](https://github.com/synonymdev/react-native-ldk/blob/master/lib/android/src/main/java/com/reactnativeldk/classes/BackupClient.kt)
[NodeJS](https://github.com/synonymdev/react-native-ldk/blob/master/backup-server/src/test.js)

## Persiting
All message signing/verifying is done using [LDK's node signing](https://docs.rs/lightning/latest/lightning/util/message_signing/fn.sign.html) on the client and [ln-verifymessagejs](https://github.com/SeverinAlexB/ln-verifymessagejs) on the server.

1. Payload is encrypted using using standard AES/GCM encryption with the encryption key being the node secret.
2. Client creates a hash of encrypted backup and signs it.
3. Client creates unique challenge in this format: `sha256_hash(pubkey+timestamp)`
4. Client uploads encrypted bytes along with node pubkey, signed hash and challenge in request header.
5. Server hashes received payload and validates client's signed hash was actually signed by provided pubkey.
6. Server stores encrypted bytes to disk.
7. Server signs client's challenge and returns signature in response.
8. Client validate that the bytes were stored by the correct server by checking the signature in the response was signed by the server pubkey hard coded in the client.

## Retrieving
Retieving or querying a backup requires a bearer token first done by a fairly standard challenge/response using the same node signing.

1. Client fetches challenge from server by posting timestamp (nonce) and signed (signed timestamp) in body with pubkey in the header.
2. Server validates signature and returns challenge (32 bytes hex string).
3. Client signs challenge.
4. Client posts signed challenge with pubkey in the header.
5. Server validates signature.
6. On success server returns bearer token with 5min expiry. A long expiry isn't needed as token is only used briefly to perform a restore.
7. Client uses bearer token to pull list of backed up files.
8. Client iterates through list and downloads each file and persists to disk.
21 changes: 21 additions & 0 deletions backup-server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const start = require('./src/server');
require('dotenv').config();

const HOST = process.env.HOST;
const PORT = process.env.PORT;
const SECRET_KEY = process.env.SECRET_KEY;
const PUBLIC_KEY = process.env.PUBLIC_KEY;

if (!HOST || !PORT || !SECRET_KEY || !PUBLIC_KEY) {
console.error('HOST, PORT, SECRET_KEY or PUBLIC_KEY environment variable is not set');
process.exit(1);
}

start({host: HOST, port: PORT, keypair: {secretKey: SECRET_KEY, publicKey: PUBLIC_KEY}})
.then(() => {
console.log("Server started");
})
.catch((error) => {
console.error(error);
process.exit(1);
});
Loading
Loading