Commit 179def42b32ae59684cbfc0339d843a6dabbdc04
refactor so that loading/saving to fs is separated
Dominic Tarr committed on 6/18/2016, 12:51:08 PMParent: 9c15c175db13dc25adbd2ff3211ed9013b6b1917
Files changed
index.js | changed |
test/index.js | changed |
fs.js | added |
util.js | added |
index.js | ||
---|---|---|
@@ -1,7 +1,4 @@ | ||
1 | -var fs = require('fs') | |
2 | -var mkdirp = require('mkdirp') | |
3 | -var path = require('path') | |
4 | 1 | var deepEqual = require('deep-equal') |
5 | 2 | |
6 | 3 | var crypto = require('crypto') |
7 | 4 | var createHmac = require('hmac') |
@@ -10,15 +7,22 @@ | ||
10 | 7 | var ssbref = require('ssb-ref') |
11 | 8 | |
12 | 9 | var pb = require('private-box') |
13 | 10 | |
11 | +var u = require('./util') | |
12 | + | |
14 | 13 | var isBuffer = Buffer.isBuffer |
15 | 14 | |
16 | 15 | function isString (s) { |
17 | 16 | return 'string' === typeof s |
18 | 17 | } |
19 | 18 | //UTILS |
20 | 19 | |
20 | + | |
21 | +function hasSigil (s) { | |
22 | + return /^(@|%|&)/.test(s) | |
23 | +} | |
24 | + | |
21 | 25 | function clone (obj) { |
22 | 26 | var _obj = {} |
23 | 27 | for(var k in obj) { |
24 | 28 | if(Object.hasOwnProperty.call(obj, k)) |
@@ -52,14 +56,8 @@ | ||
52 | 56 | function isString(s) { |
53 | 57 | return 'string' === typeof s |
54 | 58 | } |
55 | 59 | |
56 | -function hasSigil (s) { | |
57 | - return /^(@|%|&)/.test(s) | |
58 | -} | |
59 | - | |
60 | -function empty(v) { return !!v } | |
61 | - | |
62 | 60 | //crazy hack to make electron not crash |
63 | 61 | function base64ToBuffer(s) { |
64 | 62 | var l = s.length * 6 / 8 |
65 | 63 | if(s[s.length - 2] == '=') |
@@ -80,149 +78,13 @@ | ||
80 | 78 | var start = (hasSigil(buf)) ? 1 : 0 |
81 | 79 | return base64ToBuffer(buf.substring(start, ~i ? i : buf.length)) |
82 | 80 | } |
83 | 81 | |
84 | -function toUint8(buf) { | |
85 | - return new Uint8Array(toBuffer(buf)) | |
86 | -} | |
82 | +//function toUint8(buf) { | |
83 | +// return new Uint8Array(toBuffer(buf)) | |
84 | +//} | |
87 | 85 | |
88 | -function getTag (string) { | |
89 | - var i = string.indexOf('.') | |
90 | - return string.substring(i+1) | |
91 | -} | |
92 | 86 | |
93 | -exports.getTag = getTag | |
94 | - | |
95 | -function tag (key, tag) { | |
96 | - if(!tag) throw new Error('no tag for:' + key.toString('base64')) | |
97 | - return key.toString('base64')+'.' + tag.replace(/^\./, '') | |
98 | -} | |
99 | - | |
100 | -function keysToJSON(keys, curve) { | |
101 | - curve = (keys.curve || curve) | |
102 | - | |
103 | - var pub = tag(keys.public.toString('base64'), curve) | |
104 | - return { | |
105 | - curve: curve, | |
106 | - public: pub, | |
107 | - private: keys.private ? tag(keys.private.toString('base64'), curve) : undefined, | |
108 | - id: '@'+(curve === 'ed25519' ? pub : hash(pub)) | |
109 | - } | |
110 | -} | |
111 | - | |
112 | -//(DE)SERIALIZE KEYS | |
113 | - | |
114 | -function constructKeys(keys, legacy) { | |
115 | - if(!keys) throw new Error('*must* pass in keys') | |
116 | - | |
117 | - return [ | |
118 | - '# this is your SECRET name.', | |
119 | - '# this name gives you magical powers.', | |
120 | - '# with it you can mark your messages so that your friends can verify', | |
121 | - '# that they really did come from you.', | |
122 | - '#', | |
123 | - '# if any one learns this name, they can use it to destroy your identity', | |
124 | - '# NEVER show this to anyone!!!', | |
125 | - '', | |
126 | - legacy ? keys.private : JSON.stringify(keys, null, 2), | |
127 | - '', | |
128 | - '# WARNING! It\'s vital that you DO NOT edit OR share your secret name', | |
129 | - '# instead, share your public name', | |
130 | - '# your public name: ' + keys.id | |
131 | - ].join('\n') | |
132 | -} | |
133 | - | |
134 | -function reconstructKeys(keyfile) { | |
135 | - var private = keyfile | |
136 | - .replace(/\s*\#[^\n]*/g, '') | |
137 | - .split('\n').filter(empty).join('') | |
138 | - | |
139 | - //if the key is in JSON format, we are good. | |
140 | - try { | |
141 | - var keys = JSON.parse(private) | |
142 | - if(!hasSigil(keys.id)) keys.id = '@' + keys.public | |
143 | - return keys | |
144 | - } catch (_) {} | |
145 | - | |
146 | - //else, reconstruct legacy curve... | |
147 | - | |
148 | - var curve = getTag(private) | |
149 | - | |
150 | - if(curve !== 'k256') | |
151 | - throw new Error('expected legacy curve (k256) but found:' + curve) | |
152 | - | |
153 | - var ecc = require('./eccjs') | |
154 | - | |
155 | - return keysToJSON(ecc.restore(toBuffer(private)), 'k256') | |
156 | -} | |
157 | - | |
158 | -var toNameFile = exports.toNameFile = function (namefile) { | |
159 | - if(isObject(namefile)) | |
160 | - return path.join(namefile.path, 'secret') | |
161 | - return namefile | |
162 | -} | |
163 | - | |
164 | -exports.load = function(namefile, cb) { | |
165 | - namefile = toNameFile(namefile) | |
166 | - fs.readFile(namefile, 'ascii', function(err, privateKeyStr) { | |
167 | - if (err) return cb(err) | |
168 | - try { cb(null, reconstructKeys(privateKeyStr)) } | |
169 | - catch (e) { cb(err) } | |
170 | - }) | |
171 | -} | |
172 | - | |
173 | -exports.loadSync = function(namefile) { | |
174 | - namefile = toNameFile(namefile) | |
175 | - return reconstructKeys(fs.readFileSync(namefile, 'ascii')) | |
176 | -} | |
177 | - | |
178 | -exports.create = function(namefile, curve, legacy, cb) { | |
179 | - if(isFunction(legacy)) | |
180 | - cb = legacy, legacy = null | |
181 | - if(isFunction(curve)) | |
182 | - cb = curve, curve = null | |
183 | - | |
184 | - namefile = toNameFile(namefile) | |
185 | - var keys = exports.generate(curve) | |
186 | - var keyfile = constructKeys(keys, legacy) | |
187 | - mkdirp(path.dirname(namefile), function (err) { | |
188 | - if(err) return cb(err) | |
189 | - fs.writeFile(namefile, keyfile, function(err) { | |
190 | - if (err) return cb(err) | |
191 | - cb(null, keys) | |
192 | - }) | |
193 | - }) | |
194 | -} | |
195 | - | |
196 | -exports.createSync = function(namefile, curve, legacy) { | |
197 | - namefile = toNameFile(namefile) | |
198 | - var keys = exports.generate(curve) | |
199 | - var keyfile = constructKeys(keys, legacy) | |
200 | - mkdirp.sync(path.dirname(namefile)) | |
201 | - fs.writeFileSync(namefile, keyfile) | |
202 | - return keys | |
203 | -} | |
204 | - | |
205 | -exports.loadOrCreate = function (namefile, cb) { | |
206 | - namefile = toNameFile(namefile) | |
207 | - exports.load(namefile, function (err, keys) { | |
208 | - if(!err) return cb(null, keys) | |
209 | - exports.create(namefile, cb) | |
210 | - }) | |
211 | -} | |
212 | - | |
213 | -exports.loadOrCreateSync = function (namefile) { | |
214 | - namefile = toNameFile(namefile) | |
215 | - try { | |
216 | - return exports.loadSync(namefile) | |
217 | - } catch (err) { | |
218 | - return exports.createSync(namefile) | |
219 | - } | |
220 | -} | |
221 | - | |
222 | - | |
223 | -// DIGITAL SIGNATURES | |
224 | - | |
225 | 87 | var curves = {} |
226 | 88 | curves.ed25519 = require('./sodium') |
227 | 89 | try { curves.k256 = require('./eccjs') } |
228 | 90 | catch (_) {} |
@@ -254,11 +116,15 @@ | ||
254 | 116 | |
255 | 117 | if(!curves[curve]) |
256 | 118 | throw new Error('unknown curve:'+curve) |
257 | 119 | |
258 | - return keysToJSON(curves[curve].generate(seed), curve) | |
120 | + return u.keysToJSON(curves[curve].generate(seed), curve) | |
259 | 121 | } |
260 | 122 | |
123 | +//import functions for loading/saving keys from storage | |
124 | +var FS = require('./fs')(exports.generate) | |
125 | +for(var key in FS) exports[key] = FS[key] | |
126 | + | |
261 | 127 | //takes a public key and a hash and returns a signature. |
262 | 128 | //(a signature must be a node buffer) |
263 | 129 | |
264 | 130 | exports.sign = function (keys, msg) { |
@@ -330,4 +196,6 @@ | ||
330 | 196 | if(msg) return JSON.parse(''+msg) |
331 | 197 | } |
332 | 198 | |
333 | 199 | |
200 | + | |
201 | + |
test/index.js | ||
---|---|---|
@@ -5,20 +5,23 @@ | ||
5 | 5 | var fs = require('fs') |
6 | 6 | |
7 | 7 | tape('create and load async', function (t) { |
8 | 8 | try { fs.unlinkSync(path) } catch(e) {} |
9 | + console.log(ssbkeys) | |
9 | 10 | ssbkeys.create(path, function(err, k1) { |
10 | 11 | if (err) throw err |
11 | 12 | ssbkeys.load(path, function(err, k2) { |
12 | 13 | if (err) throw err |
14 | + console.log(k1, k2) | |
13 | 15 | t.equal(k1.id.toString('hex'), k2.id.toString('hex')) |
14 | 16 | t.equal(k1.private.toString('hex'), k2.private.toString('hex')) |
15 | 17 | t.equal(k1.public.toString('hex'), k2.public.toString('hex')) |
16 | 18 | t.end() |
17 | 19 | }) |
18 | 20 | }) |
19 | 21 | }) |
20 | 22 | |
23 | +return | |
21 | 24 | tape('create and load sync', function (t) { |
22 | 25 | try { fs.unlinkSync(path) } catch(e) {} |
23 | 26 | var k1 = ssbkeys.createSync(path) |
24 | 27 | var k2 = ssbkeys.loadSync(path) |
@@ -180,4 +183,6 @@ | ||
180 | 183 | t.end() |
181 | 184 | }) |
182 | 185 | |
183 | 186 | }) |
187 | + | |
188 | + |
fs.js | ||
---|---|---|
@@ -1,0 +1,140 @@ | ||
1 | +var fs = require('fs') | |
2 | +var mkdirp = require('mkdirp') | |
3 | +var path = require('path') | |
4 | +var u = require('./util') | |
5 | + | |
6 | +function isObject (o) { | |
7 | + return 'object' === typeof o | |
8 | +} | |
9 | + | |
10 | +function isFunction (f) { | |
11 | + return 'function' === typeof f | |
12 | +} | |
13 | + | |
14 | +function empty(v) { return !!v } | |
15 | + | |
16 | +module.exports = function (generate) { | |
17 | + | |
18 | + var exports = {} | |
19 | + | |
20 | + //(DE)SERIALIZE KEYS | |
21 | + | |
22 | + function constructKeys(keys, legacy) { | |
23 | + if(!keys) throw new Error('*must* pass in keys') | |
24 | + | |
25 | + return [ | |
26 | + '# this is your SECRET name.', | |
27 | + '# this name gives you magical powers.', | |
28 | + '# with it you can mark your messages so that your friends can verify', | |
29 | + '# that they really did come from you.', | |
30 | + '#', | |
31 | + '# if any one learns this name, they can use it to destroy your identity', | |
32 | + '# NEVER show this to anyone!!!', | |
33 | + '', | |
34 | + legacy ? keys.private : JSON.stringify(keys, null, 2), | |
35 | + '', | |
36 | + '# WARNING! It\'s vital that you DO NOT edit OR share your secret name', | |
37 | + '# instead, share your public name', | |
38 | + '# your public name: ' + keys.id | |
39 | + ].join('\n') | |
40 | + } | |
41 | + | |
42 | + function reconstructKeys(keyfile) { | |
43 | + var private = keyfile | |
44 | + .replace(/\s*\#[^\n]*/g, '') | |
45 | + .split('\n').filter(empty).join('') | |
46 | + | |
47 | + //if the key is in JSON format, we are good. | |
48 | + try { | |
49 | + var keys = JSON.parse(private) | |
50 | + if(!u.hasSigil(keys.id)) keys.id = '@' + keys.public | |
51 | + return keys | |
52 | + } catch (_) { console.error(_.stack) } | |
53 | + | |
54 | + //else, reconstruct legacy curve... | |
55 | + | |
56 | + var curve = u.getTag(private) | |
57 | + | |
58 | + if(curve !== 'k256') | |
59 | + throw new Error('expected legacy curve (k256) but found:' + curve) | |
60 | + | |
61 | + var fool_browserify = require | |
62 | + var ecc = fool_browserify('./eccjs') | |
63 | + | |
64 | + return keysToJSON(ecc.restore(toBuffer(private)), 'k256') | |
65 | + } | |
66 | + | |
67 | + var toNameFile = exports.toNameFile = function (namefile) { | |
68 | + if(isObject(namefile)) | |
69 | + return path.join(namefile.path, 'secret') | |
70 | + return namefile | |
71 | + } | |
72 | + | |
73 | + exports.load = function(namefile, cb) { | |
74 | + namefile = toNameFile(namefile) | |
75 | + fs.readFile(namefile, 'ascii', function(err, privateKeyStr) { | |
76 | + if (err) return cb(err) | |
77 | + var keys | |
78 | + try { keys = reconstructKeys(privateKeyStr) } | |
79 | + catch (err) { return cb(err) } | |
80 | + cb(null, keys) | |
81 | + }) | |
82 | + } | |
83 | + | |
84 | + exports.loadSync = function(namefile) { | |
85 | + namefile = toNameFile(namefile) | |
86 | + return reconstructKeys(fs.readFileSync(namefile, 'ascii')) | |
87 | + } | |
88 | + | |
89 | + exports.create = function(namefile, curve, legacy, cb) { | |
90 | + if(isFunction(legacy)) | |
91 | + cb = legacy, legacy = null | |
92 | + if(isFunction(curve)) | |
93 | + cb = curve, curve = null | |
94 | + | |
95 | + namefile = toNameFile(namefile) | |
96 | + var keys = generate(curve) | |
97 | + var keyfile = constructKeys(keys, legacy) | |
98 | + mkdirp(path.dirname(namefile), function (err) { | |
99 | + if(err) return cb(err) | |
100 | + fs.writeFile(namefile, keyfile, function(err) { | |
101 | + console.log(namefile, keyfile) | |
102 | + if (err) return cb(err) | |
103 | + cb(null, keys) | |
104 | + }) | |
105 | + }) | |
106 | + } | |
107 | + | |
108 | + exports.createSync = function(namefile, curve, legacy) { | |
109 | + namefile = toNameFile(namefile) | |
110 | + var keys = exports.generate(curve) | |
111 | + var keyfile = constructKeys(keys, legacy) | |
112 | + mkdirp.sync(path.dirname(namefile)) | |
113 | + fs.writeFileSync(namefile, keyfile) | |
114 | + return keys | |
115 | + } | |
116 | + | |
117 | + exports.loadOrCreate = function (namefile, cb) { | |
118 | + namefile = toNameFile(namefile) | |
119 | + exports.load(namefile, function (err, keys) { | |
120 | + if(!err) return cb(null, keys) | |
121 | + exports.create(namefile, cb) | |
122 | + }) | |
123 | + } | |
124 | + | |
125 | + exports.loadOrCreateSync = function (namefile) { | |
126 | + namefile = toNameFile(namefile) | |
127 | + try { | |
128 | + return exports.loadSync(namefile) | |
129 | + } catch (err) { | |
130 | + return exports.createSync(namefile) | |
131 | + } | |
132 | + } | |
133 | + | |
134 | + return exports | |
135 | +} | |
136 | + | |
137 | + | |
138 | + | |
139 | + | |
140 | + |
util.js | ||
---|---|---|
@@ -1,0 +1,32 @@ | ||
1 | + | |
2 | +exports.hasSigil = function hasSigil (s) { | |
3 | + return /^(@|%|&)/.test(s) | |
4 | +} | |
5 | + | |
6 | + | |
7 | +function tag (key, tag) { | |
8 | + if(!tag) throw new Error('no tag for:' + key.toString('base64')) | |
9 | + return key.toString('base64')+'.' + tag.replace(/^\./, '') | |
10 | +} | |
11 | + | |
12 | +exports.keysToJSON = function keysToJSON(keys, curve) { | |
13 | + curve = (keys.curve || curve) | |
14 | + | |
15 | + var pub = tag(keys.public.toString('base64'), curve) | |
16 | + return { | |
17 | + curve: curve, | |
18 | + public: pub, | |
19 | + private: keys.private ? tag(keys.private.toString('base64'), curve) : undefined, | |
20 | + id: '@'+(curve === 'ed25519' ? pub : hash(pub)) | |
21 | + } | |
22 | +} | |
23 | + | |
24 | +exports.getTag = function getTag (string) { | |
25 | + var i = string.indexOf('.') | |
26 | + return string.substring(i+1) | |
27 | +} | |
28 | + | |
29 | + | |
30 | + | |
31 | + | |
32 | + |
Built with git-ssb-web