git ssb

1+

Dominic / ssb-keys



Tree: 67b3a3af5c6a3e1800589a93d0152b7e8c90a2ec

Files: 67b3a3af5c6a3e1800589a93d0152b7e8c90a2ec / index.js

5253 bytesRaw
1var fs = require('fs')
2var crypto = require('crypto')
3var ecc = require('eccjs')
4var k256 = ecc.curves.k256
5var Blake2s = require('blake2s')
6var mkdirp = require('mkdirp')
7var path = require('path')
8var curve = ecc.curves.k256
9var createHmac = require('hmac')
10var deepEqual = require('deep-equal')
11
12function clone (obj) {
13 var _obj = {}
14 for(var k in obj) {
15 if(Object.hasOwnProperty.call(obj, k))
16 _obj[k] = obj[k]
17 }
18 return _obj
19}
20
21function hash (data, enc) {
22 return new Blake2s().update(data, enc).digest('base64') + '.blake2s'
23}
24
25
26function isHash (data) {
27 return isString(data) && /^[A-Za-z0-9\/+]{43}=\.blake2s$/.test(data)
28}
29
30exports.isHash = isHash
31exports.hash = hash
32
33function isString(s) {
34 return 'string' === typeof s
35}
36
37function empty(v) { return !!v }
38
39function constructKeys() {
40 var privateKey = crypto.randomBytes(32)
41 var k = keysToBase64(ecc.restore(k256, privateKey))
42 k.keyfile = [
43 '# this is your SECRET name.',
44 '# this name gives you magical powers.',
45 '# with it you can mark your messages so that your friends can verify',
46 '# that they really did come from you.',
47 '#',
48 '# if any one learns this name, they can use it to destroy your identity',
49 '# NEVER show this to anyone!!!',
50 '',
51 k.private.toString('hex'),
52 '',
53 '# WARNING! It\'s vital that you DO NOT edit OR share your secret name',
54 '# instead, share your public name',
55 '# your public name: ' + k.id.toString('hex')
56 ].join('\n')
57 return k
58}
59
60
61function toBuffer(buf) {
62 if(buf == null) return buf
63 return new Buffer(buf.substring(0, buf.indexOf('.')), 'base64')
64}
65
66function keysToBase64 (keys) {
67 var pub = tag(keys.public, 'k256')
68 return {
69 public: pub,
70 private: tag(keys.private, 'k256'),
71 id: hash(pub)
72 }
73}
74
75function hashToBuffer(hash) {
76 if(!isHash(hash)) throw new Error('sign expects a hash')
77 return toBuffer(hash)
78}
79
80function keysToBuffer(key) {
81 return isString(key) ? toBuffer(key) : {
82 public: toBuffer(key.public),
83 private: toBuffer(key.private)
84 }
85}
86
87function reconstructKeys(privateKeyStr) {
88 privateKeyStr = privateKeyStr
89 .replace(/\s*\#[^\n]*/g, '')
90 .split('\n').filter(empty).join('')
91
92 var privateKey = (
93 !/\./.test(privateKeyStr)
94 ? new Buffer(privateKeyStr, 'hex')
95 : toBuffer(privateKeyStr)
96 )
97
98 return keysToBase64(ecc.restore(k256, privateKey))
99}
100
101function tag (key, tag) {
102 return key.toString('base64')+'.' + tag.replace(/^\./, '')
103}
104
105exports.load = function(namefile, cb) {
106 fs.readFile(namefile, 'ascii', function(err, privateKeyStr) {
107 if (err) return cb(err)
108 try { cb(null, reconstructKeys(privateKeyStr)) }
109 catch (e) { cb(err) }
110 })
111}
112
113exports.loadSync = function(namefile) {
114 return reconstructKeys(fs.readFileSync(namefile, 'ascii'))
115}
116
117exports.create = function(namefile, cb) {
118 var k = constructKeys()
119 mkdirp(path.dirname(namefile), function (err) {
120 if(err) return cb(err)
121 fs.writeFile(namefile, k.keyfile, function(err) {
122 if (err) return cb(err)
123 delete k.keyfile
124 cb(null, k)
125 })
126 })
127}
128
129exports.createSync = function(namefile) {
130 var k = constructKeys()
131 mkdirp.sync(path.dirname(namefile))
132 fs.writeFileSync(namefile, k.keyfile)
133 delete k.keyfile
134 return k
135}
136
137exports.loadOrCreate = function (namefile, cb) {
138 exports.load(namefile, function (err, keys) {
139 if(!err) return cb(null, keys)
140 exports.create(namefile, cb)
141 })
142}
143exports.loadOrCreateSync = function (namefile) {
144 try {
145 return exports.loadSync(namefile)
146 } catch (err) {
147 return exports.createSync(namefile)
148 }
149}
150
151//this should return a key pair:
152// {public: Buffer, private: Buffer}
153
154exports.generate = function () {
155 return keysToBase64(ecc.restore(curve, crypto.randomBytes(32)))
156}
157
158//takes a public key and a hash and returns a signature.
159//(a signature must be a node buffer)
160exports.sign = function (keys, hash) {
161 var hashTag = hash.substring(hash.indexOf('.'))
162 return tag(
163 ecc.sign(curve, keysToBuffer(keys), hashToBuffer(hash)),
164 hashTag + '.k256'
165 )
166}
167
168//takes a public key, signature, and a hash
169//and returns true if the signature was valid.
170exports.verify = function (pub, sig, hash) {
171 return ecc.verify(curve, keysToBuffer(pub), toBuffer(sig), hashToBuffer(hash))
172}
173
174function createHash() {
175 return new Blake2s()
176}
177
178exports.hmac = function (data, key) {
179 return createHmac(createHash, 64, key)
180 .update(data).digest('base64')+'.blake2s.hmac'
181}
182
183exports.signObj = function (keys, obj) {
184 var _obj = clone(obj)
185 var str = JSON.stringify(_obj, null, 2)
186 var h = hash(str, 'utf8')
187 _obj.signature = sign(keys, h)
188 return _obj
189}
190
191exports.verifyObj = function (keys, obj) {
192 obj = clone(obj)
193 var sig = obj.signature
194 delete obj.signature
195 var str = JSON.stringify(obj, null, 2)
196 var h = hash(str, 'utf8')
197 return exports.verify(keys, sig, h)
198}
199
200exports.signObjHmac = function (secret, obj) {
201 obj = clone(obj)
202 var str = JSON.stringify(obj, null, 2)
203 obj.hmac = exports.hmac(str, secret)
204 return obj
205}
206
207exports.verifyObjHmac = function (secret, obj) {
208 obj = clone(obj)
209 var hmac = obj.hmac
210 delete obj.hmac
211 var str = JSON.stringify(obj, null, 2)
212 var _hmac = exports.hmac(str, secret)
213 return deepEqual(hmac, _hmac)
214}

Built with git-ssb-web