corestore 5.8.0 → 6.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test-node.yml +24 -0
- package/README.md +31 -90
- package/index.js +160 -369
- package/lib/keys.js +120 -0
- package/package.json +15 -19
- package/test/all.js +108 -437
- package/test/helpers/index.js +3 -35
- package/test/keys.js +94 -0
- package/.travis.yml +0 -5
- package/CHANGELOG.md +0 -5
- package/LICENSE +0 -21
package/lib/keys.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// TODO: Extract this into a standalone module
|
|
2
|
+
const fs = require('fs').promises
|
|
3
|
+
const p = require('path')
|
|
4
|
+
const os = require('os')
|
|
5
|
+
|
|
6
|
+
const raf = require('random-access-file')
|
|
7
|
+
const sodium = require('sodium-universal')
|
|
8
|
+
const blake2b = require('blake2b-universal')
|
|
9
|
+
|
|
10
|
+
const SYSTEM_PROFILE_STORAGE = p.join(os.homedir(), '.hyperspace', 'profiles')
|
|
11
|
+
const DEFAULT_PROFILE_NAME = 'default'
|
|
12
|
+
const PROFILE_FILENAME = 'profile'
|
|
13
|
+
|
|
14
|
+
const DEFAULT_TOKEN = Buffer.alloc(0)
|
|
15
|
+
const NAMESPACE = Buffer.from('@hyperspace/key-manager')
|
|
16
|
+
|
|
17
|
+
module.exports = class KeyManager {
|
|
18
|
+
constructor (profile, opts = {}) {
|
|
19
|
+
this.profile = profile
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
_sign (keyPair, message) {
|
|
23
|
+
if (!keyPair._secretKey) throw new Error('Invalid key pair')
|
|
24
|
+
const signature = Buffer.allocUnsafe(sodium.crypto_sign_BYTES)
|
|
25
|
+
sodium.crypto_sign_detached(signature, message, keyPair._secretKey)
|
|
26
|
+
return signature
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
createSecret (name, token) {
|
|
30
|
+
return deriveSeed(this.profile, token, name)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
createHypercoreKeyPair (name, token) {
|
|
34
|
+
const keyPair = {
|
|
35
|
+
publicKey: Buffer.allocUnsafe(sodium.crypto_sign_PUBLICKEYBYTES),
|
|
36
|
+
_secretKey: Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES),
|
|
37
|
+
sign: (msg) => this._sign(keyPair, msg)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair._secretKey, this.createSecret(name, token))
|
|
41
|
+
|
|
42
|
+
return keyPair
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static createToken () {
|
|
46
|
+
return randomBytes(32)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static async forProfile (name, opts = {}) {
|
|
50
|
+
if (typeof name === 'object') {
|
|
51
|
+
opts = name
|
|
52
|
+
name = null
|
|
53
|
+
}
|
|
54
|
+
const profilePath = p.join(opts.dir || SYSTEM_PROFILE_STORAGE, name || DEFAULT_PROFILE_NAME)
|
|
55
|
+
|
|
56
|
+
let { profile, err } = await fs.readFile(profilePath).then(profile => ({ profile }), err => ({ err }))
|
|
57
|
+
if (err) {
|
|
58
|
+
if (err.code !== 'ENOENT') throw err
|
|
59
|
+
profile = randomBytes(32)
|
|
60
|
+
await fs.mkdir(p.dirname(profilePath), { recursive: true })
|
|
61
|
+
await fs.writeFile(profilePath, profile)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return new this(profile, opts)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static async fromStorage (storage, opts = {}) {
|
|
68
|
+
if (typeof storage === 'string') {
|
|
69
|
+
const root = storage
|
|
70
|
+
storage = path => raf(p.join(root, path))
|
|
71
|
+
}
|
|
72
|
+
if (typeof storage !== 'object' && typeof storage !== 'function') throw new Error('Storage must be a random-access-storage instance or a Function')
|
|
73
|
+
const profileStorage = typeof storage === 'function' ? storage(opts.name || PROFILE_FILENAME) : storage
|
|
74
|
+
|
|
75
|
+
const profile = await new Promise((resolve, reject) => {
|
|
76
|
+
profileStorage.stat((err, st) => {
|
|
77
|
+
if (err && err.code !== 'ENOENT') return reject(err)
|
|
78
|
+
if (err || st.size < 32 || opts.overwrite) {
|
|
79
|
+
const key = randomBytes(32)
|
|
80
|
+
return profileStorage.write(0, key, err => {
|
|
81
|
+
if (err) return reject(err)
|
|
82
|
+
profileStorage.close(err => {
|
|
83
|
+
if (err) return reject(err)
|
|
84
|
+
return resolve(key)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
profileStorage.read(0, 32, (err, key) => {
|
|
89
|
+
if (err) return reject(err)
|
|
90
|
+
profileStorage.close(err => {
|
|
91
|
+
if (err) return reject(err)
|
|
92
|
+
return resolve(key)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return new this(profile, opts)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function deriveSeed (profile, token, name, output) {
|
|
103
|
+
if (token && token.length < 32) throw new Error('Token must be a Buffer with length >= 32')
|
|
104
|
+
if (!name || typeof name !== 'string') throw new Error('name must be a String')
|
|
105
|
+
if (!output) output = Buffer.alloc(32)
|
|
106
|
+
|
|
107
|
+
blake2b.batch(output, [
|
|
108
|
+
NAMESPACE,
|
|
109
|
+
token || DEFAULT_TOKEN,
|
|
110
|
+
Buffer.from(Buffer.byteLength(name, 'ascii') + '\n' + name, 'ascii')
|
|
111
|
+
], profile)
|
|
112
|
+
|
|
113
|
+
return output
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function randomBytes (n) {
|
|
117
|
+
const buf = Buffer.allocUnsafe(n)
|
|
118
|
+
sodium.randombytes_buf(buf)
|
|
119
|
+
return buf
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corestore",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A Hypercore factory that
|
|
3
|
+
"version": "6.0.0-alpha.1",
|
|
4
|
+
"description": "A Hypercore factory that simplifies managing collections of cores.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "standard && tape test/*.js"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/
|
|
11
|
+
"url": "git+https://github.com/hypercore-protocol/corestore.git"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"corestore"
|
|
@@ -16,25 +16,21 @@
|
|
|
16
16
|
"author": "Andrew Osheroff <andrewosh@gmail.com>",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"bugs": {
|
|
19
|
-
"url": "https://github.com/
|
|
19
|
+
"url": "https://github.com/hypercore-protocol/corestore/issues"
|
|
20
20
|
},
|
|
21
|
-
"homepage": "https://github.com/
|
|
21
|
+
"homepage": "https://github.com/hypercore-protocol/corestore#readme",
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"random-access-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
23
|
+
"random-access-memory": "^3.1.2",
|
|
24
|
+
"standardx": "^7.0.0",
|
|
25
|
+
"tape": "^5.3.1",
|
|
26
|
+
"tmp-promise": "^3.0.2"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"hypercore-crypto": "^2.0.0",
|
|
36
|
-
"hypercore-protocol": "^8.0.0",
|
|
37
|
-
"nanoresource": "^1.3.0",
|
|
38
|
-
"refpool": "^1.2.0"
|
|
29
|
+
"blake2b-universal": "^1.0.1",
|
|
30
|
+
"derive-key": "^1.0.1",
|
|
31
|
+
"hypercore": "next",
|
|
32
|
+
"hypercore-crypto": "^2.3.0",
|
|
33
|
+
"random-access-file": "^2.2.0",
|
|
34
|
+
"sodium-universal": "^3.0.4"
|
|
39
35
|
}
|
|
40
36
|
}
|