Browse Source

Derive the root Umbrel seed at login at persist it to disk (#39)

electrum-hidden-service
Luke Childs 4 years ago
committed by GitHub
parent
commit
a6ff213bf6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      README.md
  2. 26
      logic/auth.js
  3. 12
      logic/disk.js
  4. 1
      package.json
  5. 7
      services/disk.js
  6. 1
      utils/const.js
  7. 61
      yarn.lock

1
README.md

@ -44,6 +44,7 @@ Set the following environment variables directly or by placing them in `.env` fi
| `JWT_PRIVATE_KEY_FILE` | Path to the JWT private key (automatically created) | `/db/jwt-public-key/jwt.key` |
| `JWT_EXPIRATION` | JWT expiration in miliseconds | `3600` |
| `DOCKER_COMPOSE_DIRECTORY` | Path to directory containing `docker-compose.yml` | `/docker-compose` |
| `UMBREL_SEED_FILE` | Path to the seed used to deterministically generate entropy | `'/db/umbrel-seed/seed'` |
| `UMBREL_DASHBOARD_HIDDEN_SERVICE_FILE` | Path to Tor hostname of [`umbrel-dashboard`](https://github.com/getumbrel/umbrel-dashboard) | `/var/lib/tor/dashboard/hostname` |
| `BITCOIN_P2P_HIDDEN_SERVICE_FILE` | Path to P2P hidden service hostname of `bitcoin` | `/var/lib/tor/bitcoin-p2p/hostname` |
| `BITCOIN_P2P_PORT` | P2P port of `bitcoin` | `8333` |

26
logic/auth.js

@ -1,5 +1,7 @@
const path = require('path');
const bcrypt = require('bcrypt');
const crypto = require('crypto');
const { CipherSeed } = require('aezeed');
const iocane = require("iocane");
const compose = require("docker-compose");
const diskLogic = require('logic/disk.js');
@ -137,6 +139,21 @@ async function isRegistered() {
}
}
// Derives the root umbrel seed and persists it to disk to be used for
// determinstically deriving further entropy for any other Umbrel service.
async function deriveUmbrelSeed(user) {
if (await diskLogic.umbrelSeedFileExists()) {
return;
}
const mnemonic = (await seed(user)).seed.join(' ');
const {entropy} = CipherSeed.fromMnemonic(mnemonic);
const umbrelSeed = crypto
.createHmac('sha256', entropy)
.update('umbrel-seed')
.digest('hex');
return diskLogic.writeUmbrelSeedFile(umbrelSeed);
}
// Log the user into the device. Caches the password if login is successful. Then returns jwt.
async function login(user) {
try {
@ -149,6 +166,8 @@ async function login(user) {
//unlock lnd wallet
// await lndApiService.unlock(user.plainTextPassword, jwt);
deriveUmbrelSeed(user)
return { jwt: jwt };
} catch (error) {
@ -206,6 +225,13 @@ async function register(user, seed) {
throw new NodeError('Unable to register user');
}
//derive Umbrel seed
try {
await deriveUmbrelSeed(user);
} catch (error) {
throw new NodeError('Unable to create Umbrel seed');
}
//generate JWt
let jwt;
try {

12
logic/disk.js

@ -64,6 +64,16 @@ async function writeUserFile(json) {
return diskService.writeJsonFile(constants.USER_FILE, json);
}
async function writeUmbrelSeedFile(umbrelSeed) {
return diskService.ensureWriteFile(constants.UMBREL_SEED_FILE, umbrelSeed);
}
async function umbrelSeedFileExists(umbrelSeed) {
return diskService.readFile(constants.UMBREL_SEED_FILE)
.then(() => Promise.resolve(true))
.catch(() => Promise.resolve(false));
}
function settingsFileExists() {
return diskService.readJsonFile(constants.SETTINGS_FILE)
.then(() => Promise.resolve(true))
@ -185,6 +195,8 @@ module.exports = {
writeAppVersionFile,
writeSettingsFile,
writeUserFile,
writeUmbrelSeedFile,
umbrelSeedFileExists,
settingsFileExists,
hiddenServiceFileExists,
readAppVersionFile,

1
package.json

@ -11,6 +11,7 @@
"postcoverage": "codecov"
},
"dependencies": {
"aezeed": "^0.0.4",
"axios": "^0.19.2",
"bcrypt": "^5.0.0",
"big.js": "^5.2.2",

7
services/disk.js

@ -129,6 +129,12 @@ function writeFile(filePath, data, encoding) {
}));
}
// Like writeFile but will create the file if it doesn't already exist
async function ensureWriteFile(filePath, data, encoding) {
await fse.ensureFile(filePath);
return await writeFile(filePath, data, encoding);
}
function writeJsonFile(filePath, obj) {
const tempFileName = `${filePath}.${crypto.randomBytes(uint32Bytes).readUInt32LE(0)}`;
@ -183,4 +189,5 @@ module.exports = {
writeJsonFile,
writeKeyFile,
writeFile,
ensureWriteFile,
};

1
utils/const.js

@ -8,6 +8,7 @@ module.exports = {
JWT_PUBLIC_KEY_FILE: process.env.JWT_PUBLIC_KEY_FILE || '/db/jwt-public-key/jwt.pem',
JWT_PRIVATE_KEY_FILE: process.env.JWT_PRIVATE_KEY_FILE || '/db/jwt-private-key/jwt.key',
DOCKER_COMPOSE_DIRECTORY: process.env.DOCKER_COMPOSE_DIRECTORY || '/docker-compose',
UMBREL_SEED_FILE: process.env.UMBREL_SEED_FILE || '/db/umbrel-seed/seed',
UMBREL_DASHBOARD_HIDDEN_SERVICE_FILE: process.env.UMBREL_DASHBOARD_HIDDEN_SERVICE_FILE || '/var/lib/tor/web/hostname',
BITCOIN_P2P_HIDDEN_SERVICE_FILE: process.env.BITCOIN_P2P_HIDDEN_SERVICE_FILE || '/var/lib/tor/bitcoin-p2p/hostname',
BITCOIN_P2P_PORT: process.env.BITCOIN_P2P_PORT || 8333,

61
yarn.lock

@ -314,6 +314,24 @@ acorn@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe"
integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==
aez@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/aez/-/aez-1.0.1.tgz#e4efbef7113a92102f03754bfdf08380c4b0dec8"
integrity sha1-5O++9xE6khAvA3VL/fCDgMSw3sg=
dependencies:
blakejs "^1.1.0"
safe-buffer "^5.1.1"
aezeed@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/aezeed/-/aezeed-0.0.4.tgz#8fce8778d34f5566328f61df7706351cb15873a9"
integrity sha512-KAv2y2AtbqpdtsabCLE+C0G0h4BZLeMHsLCRga3VicYLxD17RflUBJ++c5qdpN6B6fkvK90r6bWg52Z/gMC7gQ==
dependencies:
aez "^1.0.1"
crc-32 "npm:junderw-crc32c@^1.2.0"
randombytes "^2.1.0"
scryptsy "^2.1.0"
agent-base@4, agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
@ -621,6 +639,11 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
blakejs@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5"
integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U=
bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
@ -1237,6 +1260,14 @@ cors@^2.8.5:
object-assign "^4"
vary "^1"
"crc-32@npm:junderw-crc32c@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/junderw-crc32c/-/junderw-crc32c-1.2.0.tgz#2ef07ddcde1e368acb82e5c9e2f9c5a5a026159c"
integrity sha512-tP0w5QOrunUS/XgsDBoZfw2jKNFhnUrdM96IXzuJtCyuXd19Hj47Hfd5+WFd81QDQFosiPffpc/jnSrZ35IV+A==
dependencies:
exit-on-epipe "~1.0.1"
printj "~1.1.0"
create-error-class@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
@ -1820,6 +1851,11 @@ execa@^1.0.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
exit-on-epipe@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
express@^4.16.3:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
@ -3999,7 +4035,6 @@ npm@^6.14.6:
cmd-shim "^3.0.3"
columnify "~1.5.4"
config-chain "^1.1.12"
debuglog "*"
detect-indent "~5.0.0"
detect-newline "^2.1.0"
dezalgo "~1.0.3"
@ -4014,7 +4049,6 @@ npm@^6.14.6:
has-unicode "~2.0.1"
hosted-git-info "^2.8.8"
iferr "^1.0.2"
imurmurhash "*"
infer-owner "^1.0.4"
inflight "~1.0.6"
inherits "^2.0.4"
@ -4033,14 +4067,8 @@ npm@^6.14.6:
libnpx "^10.2.2"
lock-verify "^2.1.0"
lockfile "^1.0.4"
lodash._baseindexof "*"
lodash._baseuniq "~4.6.0"
lodash._bindcallback "*"
lodash._cacheindexof "*"
lodash._createcache "*"
lodash._getnative "*"
lodash.clonedeep "~4.5.0"
lodash.restparam "*"
lodash.union "~4.6.0"
lodash.uniq "~4.5.0"
lodash.without "~4.4.0"
@ -4590,6 +4618,11 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
printj@~1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@ -4747,6 +4780,13 @@ qw@~1.0.1:
resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4"
integrity sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=
randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
dependencies:
safe-buffer "^5.1.0"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@ -5103,6 +5143,11 @@ sax@^1.2.4:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
scryptsy@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790"
integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==
semver-diff@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"

Loading…
Cancel
Save