index.jsView |
---|
1 | | -var fs = require('fs') |
2 | | -var crypto = require('crypto') |
3 | | -var ecc = require('eccjs') |
| 1 | +var fs = require('fs') |
| 2 | +var mkdirp = require('mkdirp') |
| 3 | +var path = require('path') |
| 4 | +var deepEqual = require('deep-equal') |
4 | 5 | |
5 | | -var Blake2s = require('blake2s') |
6 | | -var mkdirp = require('mkdirp') |
7 | | -var path = require('path') |
8 | | -var nacl = require('ecma-nacl') |
9 | | - |
| 6 | +var crypto = require('crypto') |
10 | 7 | var createHmac = require('hmac') |
11 | | -var deepEqual = require('deep-equal') |
| 8 | +var Blake2s = require('blake2s') |
12 | 9 | |
13 | | -var k256 = curve = ecc.curves.k256 |
| 10 | + |
14 | 11 | |
15 | | - |
16 | 12 | function clone (obj) { |
17 | 13 | var _obj = {} |
18 | 14 | for(var k in obj) { |
19 | 15 | if(Object.hasOwnProperty.call(obj, k)) |
42 | 38 | } |
43 | 39 | |
44 | 40 | function empty(v) { return !!v } |
45 | 41 | |
46 | | -function constructKeys() { |
47 | | - var keys = exports.generate() |
48 | | - |
49 | | - |
50 | | - keys.keyfile = [ |
51 | | - '# this is your SECRET name.', |
52 | | - '# this name gives you magical powers.', |
53 | | - '# with it you can mark your messages so that your friends can verify', |
54 | | - '# that they really did come from you.', |
55 | | - '#', |
56 | | - '# if any one learns this name, they can use it to destroy your identity', |
57 | | - '# NEVER show this to anyone!!!', |
58 | | - '', |
59 | | - keys.private, |
60 | | - '', |
61 | | - '# WARNING! It\'s vital that you DO NOT edit OR share your secret name', |
62 | | - '# instead, share your public name', |
63 | | - '# your public name: ' + keys.id |
64 | | - ].join('\n') |
65 | | - return keys |
66 | | -} |
67 | | - |
68 | | - |
69 | 42 | function toBuffer(buf) { |
70 | 43 | if(buf == null) return buf |
71 | | - return new Buffer(buf.substring(0, buf.indexOf('.')), 'base64') |
| 44 | + if(Buffer.isBuffer(buf)) throw new Error('already a buffer') |
| 45 | + var i = buf.indexOf('.') |
| 46 | + return new Buffer(buf.substring(0, ~i ? i : buf.length), 'base64') |
72 | 47 | } |
73 | 48 | |
74 | 49 | function toUint8(buf) { |
75 | 50 | return new Uint8Array(toBuffer(buf)) |
76 | 51 | } |
77 | 52 | |
78 | | -function keysToBase64 (keys) { |
| 53 | +function getTag (string) { |
| 54 | + var i = string.indexOf('.') |
| 55 | + return string.substring(i+1) |
| 56 | +} |
79 | 57 | |
80 | | - var pub = tag(new Buffer(keys.public), keys.curve) |
| 58 | +function tag (key, tag) { |
| 59 | + if(!tag) throw new Error('no tag for:' + key.toString('base64')) |
| 60 | + return key.toString('base64')+'.' + tag.replace(/^\./, '') |
| 61 | +} |
| 62 | + |
| 63 | +function keyToJSON(keys, curve) { |
| 64 | + var pub = keys.public.toString('base64')+'.'+(keys.curve || curve) |
81 | 65 | return { |
82 | 66 | curve: keys.curve, |
83 | | - type: keys.type, |
84 | 67 | public: pub, |
85 | | - private: tag(keys.private, keys.curve), |
| 68 | + private: keys.private ? keys.private.toString('base64') : undefined, |
86 | 69 | id: hash(pub) |
87 | 70 | } |
88 | 71 | } |
89 | 72 | |
90 | | -function hashToBuffer(hash) { |
91 | | - if(!isHash(hash)) throw new Error('sign expects a hash') |
92 | | - return toBuffer(hash) |
93 | | -} |
| 73 | + |
94 | 74 | |
95 | | -function keysToBuffer(key) { |
96 | | - return isString(key) ? toBuffer(key) : { |
97 | | - public: toBuffer(key.public), |
98 | | - private: toBuffer(key.private) |
99 | | - } |
| 75 | +function constructKeys() { |
| 76 | + var keys = exports.generate() |
| 77 | + |
| 78 | + keys.keyfile = [ |
| 79 | + '# this is your SECRET name.', |
| 80 | + '# this name gives you magical powers.', |
| 81 | + '# with it you can mark your messages so that your friends can verify', |
| 82 | + '# that they really did come from you.', |
| 83 | + '#', |
| 84 | + '# if any one learns this name, they can use it to destroy your identity', |
| 85 | + '# NEVER show this to anyone!!!', |
| 86 | + '', |
| 87 | + JSON.stringify(keys, null, 2), |
| 88 | + '', |
| 89 | + '# WARNING! It\'s vital that you DO NOT edit OR share your secret name', |
| 90 | + '# instead, share your public name', |
| 91 | + '# your public name: ' + keys.id |
| 92 | + ].join('\n') |
| 93 | + |
| 94 | + return keys |
100 | 95 | } |
101 | 96 | |
102 | 97 | function reconstructKeys(keyfile) { |
103 | 98 | var private = keyfile |
104 | 99 | .replace(/\s*\#[^\n]*/g, '') |
105 | 100 | .split('\n').filter(empty).join('') |
106 | 101 | |
107 | | - var i = private.indexOf('.') |
| 102 | + |
| 103 | + try { |
| 104 | + return JSON.parse(private) |
| 105 | + } catch (_) {} |
108 | 106 | |
109 | | - var curve = private.substring(i+1) |
110 | | - |
| 107 | + |
111 | 108 | |
| 109 | + var curve = getTag(private) |
| 110 | + if(curve !== 'k256') |
| 111 | + throw new Error('expected legacy curve (k256) but found:' + curve) |
112 | 112 | |
113 | | - |
114 | | - |
115 | | - |
116 | | - |
117 | | - |
118 | | - |
119 | | - |
120 | | - if(curve === 'ed25519') { |
121 | | - var pub = tag( |
122 | | - new Buffer(nacl.signing.extract_pkey(toUint8(private))), |
123 | | - curve |
124 | | - ) |
125 | | - return { |
126 | | - type: curve === 'ed25519' ? 'nacl' : 'eccjs', |
127 | | - curve: curve, |
128 | | - private: private, |
129 | | - public: pub, |
130 | | - id: hash(pub) |
131 | | - } |
132 | | - |
133 | | - } |
134 | | - |
135 | | - return keysToBase64(ecc.restore(k256, toBuffer(private))) |
| 113 | + return keysToJSON(ecc.restore(k256, toBuffer(private)), 'k256') |
136 | 114 | } |
137 | 115 | |
138 | | -function tag (key, tag) { |
139 | | - if(!tag) throw new Error('no tag for:' + key.toString('base64')) |
140 | | - return key.toString('base64')+'.' + tag.replace(/^\./, '') |
141 | | -} |
142 | | - |
143 | 116 | var toNameFile = exports.toNameFile = function (namefile) { |
144 | 117 | if(isObject(namefile)) |
145 | 118 | return path.join(namefile.path, 'secret') |
146 | 119 | return namefile |
188 | 161 | if(!err) return cb(null, keys) |
189 | 162 | exports.create(namefile, cb) |
190 | 163 | }) |
191 | 164 | } |
| 165 | + |
192 | 166 | exports.loadOrCreateSync = function (namefile) { |
193 | 167 | namefile = toNameFile(namefile) |
194 | 168 | try { |
195 | 169 | return exports.loadSync(namefile) |
197 | 171 | return exports.createSync(namefile) |
198 | 172 | } |
199 | 173 | } |
200 | 174 | |
201 | | - |
202 | | - |
203 | 175 | |
204 | | -exports.generate = function (curve) { |
205 | | - var _keys = nacl.signing.generate_keypair( |
206 | | - new Uint8Array(crypto.randomBytes(32)) |
207 | | - ) |
| 176 | + |
208 | 177 | |
209 | | - if(!curve) curve = 'ed25519' |
| 178 | +var curves = { |
| 179 | + ed25519 : require('./browser-sodium'), |
| 180 | + k256 : require('./eccjs') |
| 181 | +} |
210 | 182 | |
211 | | - if(curve === 'ed25519') |
212 | | - return keysToBase64({ |
213 | | - type: 'nacl', |
214 | | - curve: 'ed25519', |
215 | | - public: new Buffer(_keys.pkey), |
216 | | - private: new Buffer(_keys.skey), |
217 | | - }) |
| 183 | +function getCurve(keys) { |
| 184 | + var curve = keys.curve |
218 | 185 | |
219 | | - else if(curve === 'k256') |
220 | | - return keysToBase64(ecc.restore(curve, crypto.randomBytes(32))) |
| 186 | + if(!keys.curve && isString(keys.public)) |
| 187 | + keys = keys.public |
| 188 | + |
| 189 | + if(!curve && isString(keys)) |
| 190 | + curve = getTag(keys) |
| 191 | + |
| 192 | + if(!curves[curve]) { |
| 193 | + throw new Error( |
| 194 | + 'unkown curve:' + curve + |
| 195 | + ' expected: '+Object.keys(curves) |
| 196 | + ) |
| 197 | + } |
| 198 | + |
| 199 | + return curves[curve] |
221 | 200 | } |
222 | 201 | |
| 202 | + |
| 203 | + |
| 204 | + |
| 205 | +exports.generate = function (curve) { |
| 206 | + curve = curve || 'ed25519' |
| 207 | + return keyToJSON(curves[curve].generate(), curve) |
| 208 | +} |
| 209 | + |
223 | 210 | |
224 | 211 | |
| 212 | + |
225 | 213 | exports.sign = function (keys, hash) { |
226 | 214 | var hashTag = hash.substring(hash.indexOf('.')) |
| 215 | + return getCurve(keys) |
| 216 | + .sign(toBuffer(keys.private), toBuffer(hash)).toString('base64')+'.blake2s.'+keys.curve |
227 | 217 | |
228 | | - if(keys.curve === 'ed25519') |
229 | | - return tag(new Buffer(nacl.signing.sign( |
230 | | - new Uint8Array(hashToBuffer(hash)), |
231 | | - new Uint8Array(toBuffer(keys.private)) |
232 | | - )), |
233 | | - hashTag + '.ed25519' |
234 | | - ) |
235 | | - |
236 | | - else if(keys.curve === 'k256') |
237 | | - return tag( |
238 | | - ecc.sign(curve, keysToBuffer(keys), hashToBuffer(hash)), |
239 | | - hashTag + '.k256' |
240 | | - ) |
241 | | - |
242 | | - else |
243 | | - throw new Error('unknown keys') |
244 | 218 | } |
245 | 219 | |
246 | 220 | |
247 | 221 | |
248 | 222 | exports.verify = function (keys, sig, hash) { |
249 | | - |
250 | | - var curve = keys.curve |
251 | | - if(!keys.curve && isString(keys.public)) |
252 | | - keys = keys.public |
| 223 | + return getCurve(keys).verify( |
| 224 | + toBuffer(keys.public || keys), |
| 225 | + toBuffer(sig), |
| 226 | + toBuffer(hash) |
| 227 | + ) |
| 228 | +} |
253 | 229 | |
254 | | - if(isString(keys)) |
255 | | - curve = keys.substring(keys.indexOf('.')+1) |
| 230 | + |
256 | 231 | |
257 | | - if(curve === 'ed25519') { |
258 | | - return nacl.signing.verify( |
259 | | - new Uint8Array(toBuffer(sig)), |
260 | | - new Uint8Array(hashToBuffer(hash)), |
261 | | - new Uint8Array(toBuffer(keys.public || keys)) |
262 | | - ) |
263 | | - } |
264 | | - else if(keys.curve === 'k256') |
265 | | - return ecc.verify( |
266 | | - curve, |
267 | | - keysToBuffer(keys), |
268 | | - toBuffer(sig), |
269 | | - hashToBuffer(hash) |
270 | | - ) |
271 | | - else |
272 | | - throw new Error('unknown curve:' + JSON.stringify(keys)) |
273 | | -} |
274 | | - |
275 | 232 | function createHash() { |
276 | 233 | return new Blake2s() |
277 | 234 | } |
278 | 235 | |