Files: 9380a0ea407a45d5dc3c9720cb27c322b538739f / index.js
4839 bytesRaw
1 | var deepEqual = require('deep-equal') |
2 | |
3 | var crypto = require('crypto') |
4 | var createHmac = require('hmac') |
5 | |
6 | var sodium = require('chloride') |
7 | var ssbref = require('ssb-ref') |
8 | |
9 | var pb = require('private-box') |
10 | |
11 | var u = require('./util') |
12 | |
13 | var isBuffer = Buffer.isBuffer |
14 | |
15 | function isString (s) { |
16 | return 'string' === typeof s |
17 | } |
18 | //UTILS |
19 | |
20 | |
21 | function hasSigil (s) { |
22 | return /^(@|%|&)/.test(s) |
23 | } |
24 | |
25 | function clone (obj) { |
26 | var _obj = {} |
27 | for(var k in obj) { |
28 | if(Object.hasOwnProperty.call(obj, k)) |
29 | _obj[k] = obj[k] |
30 | } |
31 | return _obj |
32 | } |
33 | |
34 | function hash (data, enc) { |
35 | data = ( |
36 | 'string' === typeof data && enc == null |
37 | ? new Buffer(data, 'binary') |
38 | : new Buffer(data, enc) |
39 | ) |
40 | return crypto.createHash('sha256').update(data).digest('base64')+'.sha256' |
41 | } |
42 | |
43 | var isLink = ssbref.isLink |
44 | var isFeedId = ssbref.isFeedId |
45 | |
46 | exports.hash = hash |
47 | |
48 | function isObject (o) { |
49 | return 'object' === typeof o |
50 | } |
51 | |
52 | function isFunction (f) { |
53 | return 'function' === typeof f |
54 | } |
55 | |
56 | function isString(s) { |
57 | return 'string' === typeof s |
58 | } |
59 | |
60 | //crazy hack to make electron not crash |
61 | function base64ToBuffer(s) { |
62 | var l = s.length * 6 / 8 |
63 | if(s[s.length - 2] == '=') |
64 | l = l - 2 |
65 | else |
66 | if(s[s.length - 1] == '=') |
67 | l = l - 1 |
68 | |
69 | var b = new Buffer(l) |
70 | b.write(s, 'base64') |
71 | return b |
72 | } |
73 | |
74 | function toBuffer(buf) { |
75 | if(buf == null) return buf |
76 | if(Buffer.isBuffer(buf)) throw new Error('already a buffer') |
77 | var i = buf.indexOf('.') |
78 | var start = (hasSigil(buf)) ? 1 : 0 |
79 | return base64ToBuffer(buf.substring(start, ~i ? i : buf.length)) |
80 | } |
81 | |
82 | //function toUint8(buf) { |
83 | // return new Uint8Array(toBuffer(buf)) |
84 | //} |
85 | |
86 | |
87 | var curves = {} |
88 | curves.ed25519 = require('./sodium') |
89 | try { curves.k256 = require('./eccjs') } |
90 | catch (_) {} |
91 | |
92 | function getCurve(keys) { |
93 | var curve = keys.curve |
94 | |
95 | if(!keys.curve && isString(keys.public)) |
96 | keys = keys.public |
97 | |
98 | if(!curve && isString(keys)) |
99 | curve = getTag(keys) |
100 | |
101 | if(!curves[curve]) { |
102 | throw new Error( |
103 | 'unkown curve:' + curve + |
104 | ' expected: '+Object.keys(curves) |
105 | ) |
106 | } |
107 | |
108 | return curve |
109 | } |
110 | |
111 | //this should return a key pair: |
112 | // {curve: curve, public: Buffer, private: Buffer} |
113 | |
114 | exports.generate = function (curve, seed) { |
115 | curve = curve || 'ed25519' |
116 | |
117 | if(!curves[curve]) |
118 | throw new Error('unknown curve:'+curve) |
119 | |
120 | return u.keysToJSON(curves[curve].generate(seed), curve) |
121 | } |
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 | |
127 | |
128 | exports.loadOrCreate = function (filename, cb) { |
129 | exports.load(filename, function (err, keys) { |
130 | if(!err) return cb(null, keys) |
131 | exports.create(filename, cb) |
132 | }) |
133 | } |
134 | |
135 | exports.loadOrCreateSync = function (namefile) { |
136 | try { |
137 | return exports.loadSync(filename) |
138 | } catch (err) { |
139 | return exports.createSync(filename) |
140 | } |
141 | } |
142 | |
143 | |
144 | //takes a public key and a hash and returns a signature. |
145 | //(a signature must be a node buffer) |
146 | |
147 | exports.sign = function (keys, msg) { |
148 | if(isString(msg)) |
149 | msg = new Buffer(msg) |
150 | if(!isBuffer(msg)) |
151 | throw new Error('msg should be buffer') |
152 | var curve = getCurve(keys) |
153 | |
154 | return curves[curve] |
155 | .sign(toBuffer(keys.private || keys), msg) |
156 | .toString('base64')+'.sig.'+curve |
157 | |
158 | } |
159 | |
160 | //takes a public key, signature, and a hash |
161 | //and returns true if the signature was valid. |
162 | exports.verify = function (keys, sig, msg) { |
163 | if(isObject(sig)) |
164 | throw new Error('signature should be base64 string, did you mean verifyObj(public, signed_obj)') |
165 | return curves[getCurve(keys)].verify( |
166 | toBuffer(keys.public || keys), |
167 | toBuffer(sig), |
168 | isBuffer(msg) ? msg : new Buffer(msg) |
169 | ) |
170 | } |
171 | |
172 | // OTHER CRYTPO FUNCTIONS |
173 | |
174 | exports.hmac = function (data, key) { |
175 | return createHmac(createHash, 64, key) |
176 | .update(data).digest('base64')+'.sha256.hmac' |
177 | } |
178 | |
179 | exports.signObj = function (keys, obj) { |
180 | var _obj = clone(obj) |
181 | var b = new Buffer(JSON.stringify(_obj, null, 2)) |
182 | _obj.signature = exports.sign(keys, b) |
183 | return _obj |
184 | } |
185 | |
186 | exports.verifyObj = function (keys, obj) { |
187 | obj = clone(obj) |
188 | var sig = obj.signature |
189 | delete obj.signature |
190 | var b = new Buffer(JSON.stringify(obj, null, 2)) |
191 | return exports.verify(keys, sig, b) |
192 | } |
193 | |
194 | exports.box = function (msg, recipients) { |
195 | msg = new Buffer(JSON.stringify(msg)) |
196 | |
197 | recipients = recipients.map(function (keys) { |
198 | var public = keys.public || keys |
199 | return sodium.crypto_sign_ed25519_pk_to_curve25519(toBuffer(public)) |
200 | }) |
201 | |
202 | //it's since the nonce is 24 bytes (a multiple of 3) |
203 | //it's possible to concatenate the base64 strings |
204 | //and still have a valid base64 string. |
205 | return pb.multibox(msg, recipients).toString('base64')+'.box' |
206 | } |
207 | |
208 | exports.unbox = function (boxed, keys) { |
209 | boxed = toBuffer(boxed) |
210 | var sk = sodium.crypto_sign_ed25519_sk_to_curve25519(toBuffer(keys.private || keys)) |
211 | |
212 | var msg = pb.multibox_open(boxed, sk) |
213 | if(msg) return JSON.parse(''+msg) |
214 | } |
215 | |
216 | |
217 | |
218 | |
219 | |
220 | |
221 |
Built with git-ssb-web