git ssb

1+

Dominic / ssb-keys



Commit 54b49310175b4189f17dacff86a9b1a509b15952

refactor out wrappers for the different curves

Dominic Tarr committed on 5/29/2015, 1:52:53 PM
Parent: 847fc7720a79e522843771339cac8a07a7cbb0ec

Files changed

index.jschanged
eccjs.jsadded
sodium.jsadded
index.jsView
@@ -1,19 +1,15 @@
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')
45
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')
107 var createHmac = require('hmac')
11-var deepEqual = require('deep-equal')
8+var Blake2s = require('blake2s')
129
13-var k256 = curve = ecc.curves.k256
10+//UTILS
1411
15-
1612 function clone (obj) {
1713 var _obj = {}
1814 for(var k in obj) {
1915 if(Object.hasOwnProperty.call(obj, k))
@@ -42,105 +38,82 @@
4238 }
4339
4440 function empty(v) { return !!v }
4541
46-function constructKeys() {
47- var keys = exports.generate()
48- //var privateKey = crypto.randomBytes(32)
49- //var k = keysToBase64(ecc.restore(k256, privateKey))
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-
6942 function toBuffer(buf) {
7043 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')
7247 }
7348
7449 function toUint8(buf) {
7550 return new Uint8Array(toBuffer(buf))
7651 }
7752
78-function keysToBase64 (keys) {
53+function getTag (string) {
54+ var i = string.indexOf('.')
55+ return string.substring(i+1)
56+}
7957
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)
8165 return {
8266 curve: keys.curve,
83- type: keys.type,
8467 public: pub,
85- private: tag(keys.private, keys.curve),
68+ private: keys.private ? keys.private.toString('base64') : undefined,
8669 id: hash(pub)
8770 }
8871 }
8972
90-function hashToBuffer(hash) {
91- if(!isHash(hash)) throw new Error('sign expects a hash')
92- return toBuffer(hash)
93-}
73+//(DE)SERIALIZE KEYS
9474
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
10095 }
10196
10297 function reconstructKeys(keyfile) {
10398 var private = keyfile
10499 .replace(/\s*\#[^\n]*/g, '')
105100 .split('\n').filter(empty).join('')
106101
107- var i = private.indexOf('.')
102+ //if the key is in JSON format, we are good.
103+ try {
104+ return JSON.parse(private)
105+ } catch (_) {}
108106
109- var curve = private.substring(i+1)
110-// private = private.substring(0, i)
107+ //else, reconstruct legacy curve...
111108
109+ var curve = getTag(private)
110+ if(curve !== 'k256')
111+ throw new Error('expected legacy curve (k256) but found:' + curve)
112112
113-// var privateKey = (
114-// !/\./.test(privateKeyStr)
115-// ? new Buffer(privateKeyStr, 'hex')
116-// : toBuffer(privateKeyStr)
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')
136114 }
137115
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-
143116 var toNameFile = exports.toNameFile = function (namefile) {
144117 if(isObject(namefile))
145118 return path.join(namefile.path, 'secret')
146119 return namefile
@@ -188,8 +161,9 @@
188161 if(!err) return cb(null, keys)
189162 exports.create(namefile, cb)
190163 })
191164 }
165+
192166 exports.loadOrCreateSync = function (namefile) {
193167 namefile = toNameFile(namefile)
194168 try {
195169 return exports.loadSync(namefile)
@@ -197,82 +171,65 @@
197171 return exports.createSync(namefile)
198172 }
199173 }
200174
201-//this should return a key pair:
202-// {public: Buffer, private: Buffer}
203175
204-exports.generate = function (curve) {
205- var _keys = nacl.signing.generate_keypair(
206- new Uint8Array(crypto.randomBytes(32))
207- )
176+// DIGITAL SIGNATURES
208177
209- if(!curve) curve = 'ed25519'
178+var curves = {
179+ ed25519 : require('./browser-sodium'),
180+ k256 : require('./eccjs') //LEGACY
181+}
210182
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
218185
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]
221200 }
222201
202+//this should return a key pair:
203+// {curve: curve, public: Buffer, private: Buffer}
204+
205+exports.generate = function (curve) {
206+ curve = curve || 'ed25519'
207+ return keyToJSON(curves[curve].generate(), curve)
208+}
209+
223210 //takes a public key and a hash and returns a signature.
224211 //(a signature must be a node buffer)
212+
225213 exports.sign = function (keys, hash) {
226214 var hashTag = hash.substring(hash.indexOf('.'))
215+ return getCurve(keys)
216+ .sign(toBuffer(keys.private), toBuffer(hash)).toString('base64')+'.blake2s.'+keys.curve
227217
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')
244218 }
245219
246220 //takes a public key, signature, and a hash
247221 //and returns true if the signature was valid.
248222 exports.verify = function (keys, sig, hash) {
249- //types all match.
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+}
253229
254- if(isString(keys))
255- curve = keys.substring(keys.indexOf('.')+1)
230+// OTHER CRYTPO FUNCTIONS
256231
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-
275232 function createHash() {
276233 return new Blake2s()
277234 }
278235
eccjs.jsView
@@ -1,0 +1,26 @@
1+
2+
3+var eccjs = require('eccjs')
4+
5+module.exports = {
6+
7+ curves: ['k256'],
8+
9+ generate: function () {
10+ var keys = ecc.generate(crypto.randomBytes(32))
11+ return {
12+ curve: 'k256',
13+ public: keys.public,
14+ private: keys.private
15+ }
16+ },
17+
18+ sign: function (private, message) {
19+ return ecc.sign(private, message)
20+ },
21+
22+ verify: function (public, sig, message) {
23+ return ecc.verify(public, sig, message)
24+ }
25+
26+}
sodium.jsView
@@ -1,0 +1,29 @@
1+
2+var sodium = require('sodium').api
3+var crypto = require('crypto')
4+
5+module.exports = {
6+
7+ curves: ['ed25519'],
8+
9+ generate: function () {
10+ var keys = sodium.crypto_sign_keypair(crypto.randomBytes(32))
11+ return {
12+ curve: 'ed25519',
13+ public: keys.publicKey,
14+
15+ //so that this works with either sodium
16+ //or libsodium-wrappers (in browser)
17+ private: keys.privateKey || keys.secretKey
18+ }
19+ },
20+
21+ sign: function (private, message) {
22+ return sodium.crypto_sign_detached(message, private)
23+ },
24+
25+ verify: function (public, sig, message) {
26+ return sodium.crypto_sign_verify_detached(sig, message, public)
27+ }
28+
29+}

Built with git-ssb-web