UNPKG

8.31 kBJavaScriptView Raw
1var Collection, Definition, Document, Envelope, HDKey, bs, crypto, defer, ecc, exports, tv4, _;
2
3_ = require('lodash');
4
5bs = require('bs58check');
6
7ecc = require('ecc-tools');
8
9tv4 = require('tv4');
10
11defer = require('when');
12
13crypto = require('crypto');
14
15HDKey = require('hdkey');
16
17Envelope = require('ecc-envelope');
18
19Collection = require('./collection');
20
21Definition = require('./definition');
22
23Document = (function() {
24 function Document(custodian, key) {
25 var _ref, _ref1, _ref2;
26 this.custodian = custodian;
27 if (!(this instanceof Document)) {
28 return new Document(this.custodian, key);
29 }
30 if (typeof key === 'string') {
31 key = bs.decode(key);
32 }
33 this.extended = false;
34 if (key.length === 78) {
35 this.extended = true;
36 this.hdkey = HDKey.fromExtendedKey(bs.encode(key));
37 this.privateKey = this.hdkey.privateKey;
38 this.publicKey = this.hdkey.publicKey;
39 this.children = new Collection(this.custodian, Document, (_ref = this.hdkey.privateExtendedKey) != null ? _ref : this.hdkey.publicExtendedKey);
40 } else if (key.length === 33) {
41 this.publicKey = key;
42 } else if (key.length === 32) {
43 this.privateKey = key;
44 this.publicKey = ecc.publicKey(this.privateKey, true);
45 }
46 if (this.publicKey) {
47 this.pub = bs.encode(this.publicKey);
48 }
49 if (this.privateKey) {
50 this.prv = bs.encode(this.privateKey);
51 }
52 this.xpub = (_ref1 = this.hdkey) != null ? _ref1.publicExtendedKey : void 0;
53 this.xprv = (_ref2 = this.hdkey) != null ? _ref2.privateExtendedKey : void 0;
54 this.readonly = this.privateKey == null;
55 this._headers = {
56 from: bs.encode(this.publicKey)
57 };
58 this._links = {};
59 this.fetch()["else"](false);
60 }
61
62 Document.prototype.fetch = function() {
63 return this._fetch = this.custodian.document.get(this.publicKey).then((function(_this) {
64 return function(envelope) {
65 if (!envelope) {
66 return false;
67 }
68 envelope = Envelope({
69 decode: envelope
70 });
71 return envelope.open().then(function(envelope) {
72 _this._meta = envelope.data.meta;
73 _this._links = envelope.data.links;
74 _this._headers = envelope.data.headers;
75 return envelope;
76 });
77 };
78 })(this));
79 };
80
81 Document.prototype.link = function(key, data) {
82 if (data != null) {
83 return this.custodian.data.put(data).then((function(_this) {
84 return function(hash) {
85 return _this._links[key] = bs.encode(new Buffer(hash));
86 };
87 })(this));
88 } else {
89 return this.custodian.data.get(this._links[key]);
90 }
91 };
92
93 Document.prototype.definition = function(definition) {
94 if (definition != null) {
95 if (this.readonly) {
96 return defer(false);
97 }
98 definition = Definition(this.custodian, definition);
99 return definition.save().then((function(_this) {
100 return function(hash) {
101 return _this._links.definition = bs.encode(new Buffer(hash));
102 };
103 })(this)).then((function(_this) {
104 return function() {
105 return definition.children();
106 };
107 })(this)).then((function(_this) {
108 return function(children) {
109 return _this.children._definitions = children;
110 };
111 })(this)).then((function(_this) {
112 return function() {
113 return _this._links.definition;
114 };
115 })(this));
116 } else {
117 return defer(Definition(this.custodian, this._links.definition));
118 }
119 };
120
121 Document.prototype.data = function(data, alg) {
122 if (alg == null) {
123 alg = 'aes';
124 }
125 if (data != null) {
126 return defer(data).then((function(_this) {
127 return function(data) {
128 if (_this.readonly) {
129 return false;
130 }
131 return _this.encrypt(data, alg);
132 };
133 })(this));
134 } else {
135 return this.decrypt();
136 }
137 };
138
139 Document.prototype.encrypt = function(data, alg) {
140 if (alg == null) {
141 alg = 'aes';
142 }
143 if (this.readonly) {
144 return defer(false);
145 }
146 return this.definition().then(function(definition) {
147 return definition.get('permissions');
148 }).then((function(_this) {
149 return function(permissions) {
150 var iv, key;
151 if (permissions === 'public') {
152 return _this.link('data', data);
153 } else if (permissions === 'hardened' || permissions === 'private') {
154 if (!_this.extended) {
155 return defer(false);
156 }
157 iv = crypto.randomBytes(16);
158 if (permissions === 'hardened') {
159 key = ecc.sha256(_this.xpub);
160 }
161 if (permissions === 'private') {
162 key = ecc.sha256(_this.prv);
163 }
164 return ecc.cipher(data, key, iv, 'aes').then(function(ciphertext) {
165 iv = bs.encode(iv);
166 ciphertext = bs.encode(ciphertext);
167 return _this.link('data', {
168 iv: iv,
169 alg: alg,
170 ciphertext: ciphertext
171 });
172 });
173 }
174 };
175 })(this));
176 };
177
178 Document.prototype.decrypt = function() {
179 return this.definition().then(function(definition) {
180 return definition.get('permissions');
181 }).then((function(_this) {
182 return function(permissions) {
183 return _this.link('data').then(function(cipher) {
184 var ciphertext, iv, key;
185 if (permissions === 'public') {
186 return cipher;
187 }
188 if (permissions === 'hardened') {
189 key = ecc.sha256(_this.xpub);
190 }
191 if (permissions === 'private') {
192 key = ecc.sha256(_this.prv);
193 }
194 ciphertext = bs.decode(cipher.ciphertext);
195 iv = bs.decode(cipher.iv);
196 return ecc.decipher(ciphertext, key, iv, cipher.alg);
197 });
198 };
199 })(this));
200 };
201
202 Document.prototype.validate = function() {
203 return this.definition().then(function(definition) {
204 return definition.get('schema');
205 }).then((function(_this) {
206 return function(schema) {
207 return _this.data().then(function(data) {
208 var validation;
209 if (data == null) {
210 defer.reject(Error("No data"));
211 }
212 validation = tv4.validateMultiple(data, schema, false, true);
213 _this.errors = validation.errors;
214 if (validation.valid) {
215 return data;
216 } else {
217 return defer.reject(Error("Data does not match schema: " + _this.errors + " s" + (JSON.stringify(schema)) + " d" + (JSON.stringify(data))));
218 }
219 });
220 };
221 })(this));
222 };
223
224 Document.prototype.meta = function() {
225 return this.definition().then(function(definition) {
226 return definition.get('meta');
227 }).then((function(_this) {
228 return function(meta) {
229 return _this.data().then(function(data) {
230 return _this._meta = _(meta).mapValues(function(path) {
231 return _.get(data, path);
232 }).omitBy(_.isUndefined).value();
233 });
234 };
235 })(this));
236 };
237
238 Document.prototype.summary = function() {
239 return this.meta().then((function(_this) {
240 return function(meta) {
241 return {
242 meta: meta,
243 document: _this
244 };
245 };
246 })(this));
247 };
248
249 Document.prototype.details = function() {
250 return this._fetch.then((function(_this) {
251 return function() {
252 return _this.data();
253 };
254 })(this)).then((function(_this) {
255 return function(data) {
256 return {
257 data: data,
258 document: _this
259 };
260 };
261 })(this));
262 };
263
264 Document.prototype.save = function() {
265 if (this.readonly) {
266 return defer(false);
267 }
268 return this.validate().then((function(_this) {
269 return function() {
270 return _this.meta();
271 };
272 })(this)).then((function(_this) {
273 return function(meta) {
274 _this._envelope = Envelope({
275 send: {
276 meta: _this._meta,
277 links: _this._links,
278 headers: _this._headers
279 },
280 from: _this.privateKey
281 });
282 return _this.custodian.document.put(_this._envelope.encode());
283 };
284 })(this));
285 };
286
287 return Document;
288
289})();
290
291exports = module.exports = Document;