@drifted/raven 0.0.8 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,5 +53,17 @@
53
53
 
54
54
  theraven.txt decrypted
55
55
  secret: O9JorHTlz2Wk+oaGq1XpLZzYxwblzlYd3+No7l8eupQ=
56
+
57
+ # if you use a password it will create a secret to use that is the same every time.
58
+ > raven conceal -f theraven.txt -p 'some clever password'
59
+
60
+ theraven.txt encrypted
61
+ secret: e6d71b7a7ddd3afbdc9e91007d2c1f63c8c4c563e64=
62
+
63
+ > raven expose -f theraven.txt -p 'some clever password'
64
+
65
+ theraven.txt decrypted
66
+ secret: e6d71b7a7ddd3afbdc9e91007d2c1f63c8c4c563e64=
67
+
56
68
 
57
69
  ```
package/bin/cli.js CHANGED
@@ -10,8 +10,10 @@ const JSONDataFile = require(path.join(__dirname, '..'));
10
10
  var parser = new CarrierPigeon({strict: true});
11
11
  parser.commands('version', 'usage', 'show', 'secret', 'init', 'generate', 'expose', 'conceal', 'encrypt', 'decrypt');
12
12
  parser.option('file', {type: 'file'});
13
+ parser.option('password', {});
13
14
  parser.option('secret', {});
14
15
  parser.option('data', {});
16
+ parser.option('type', {default: 'text'});
15
17
  parser.option('json', {default: false});
16
18
  parser.option('location', {type: 'file', default: process.cwd()});
17
19
  parser.option('verbose', {default: false});
package/bin/console.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require('path');
4
+ const CarrierPigeon = require('carrier-pigeon');
5
+ const repl = require('node:repl');
6
+
7
+ var RavenDataFile = require(path.join(__dirname, '..'));
8
+
9
+
10
+ var parser = new CarrierPigeon({strict: false});
11
+ parser.option('env', { default: (process.env.NODE_ENV ? process.env.NODE_ENV : "development"), env: "NODE_ENV" })
12
+ parser.option('verbose', { default: false, env: "VERBOSE" })
13
+
14
+ var options = parser.parse(process.argv);
15
+ //require('@drifted/qa/db');
16
+
17
+
18
+
19
+ const r = repl.start('> ');
20
+ Object.defineProperty(r.context, 'RavenDataFile', {
21
+ configurable: false,
22
+ enumerable: true,
23
+ value: RavenDataFile
24
+ });
25
+
26
+
package/bufferable.js ADDED
@@ -0,0 +1,18 @@
1
+ const { Writable } = require('node:stream');
2
+
3
+ class Bufferable extends Writable {
4
+ constructor() {
5
+ super();
6
+ this.chunks = [];
7
+ }
8
+ _write(chunk, encoding, callback) {
9
+ this.chunks.push(chunk);
10
+ callback();
11
+ }
12
+ get buffer() {
13
+ return Buffer.concat(this.chunks);
14
+ }
15
+ }
16
+
17
+
18
+ module.exports = Bufferable;
package/index.js CHANGED
@@ -1,12 +1,18 @@
1
1
  const path = require('path');
2
2
  const crypto = require('crypto');
3
3
  const fs = require('fs');
4
-
4
+ const { fileTypeFromBuffer } = require('file-type');
5
5
  const ivLength = 16;
6
+ const { Readable, pipeline, buffer } = require('stream');
7
+ const Bufferable = require(path.join(__dirname, 'bufferable'));
8
+ const { isUtf8, isAscii } = require('node:buffer');
6
9
 
7
10
  class RavenDataFile {
11
+
12
+
8
13
  constructor(options={}) {
9
14
  Object.assign(this, options)
15
+
10
16
  if (this.data != undefined && typeof this.data == 'string') {
11
17
  try {
12
18
  this.data = JSON.parse(this.data);
@@ -19,6 +25,10 @@ class RavenDataFile {
19
25
  this.algorithm = 'aes-256-gcm';
20
26
  }
21
27
 
28
+ if (this.password != undefined && this.secret == undefined) {
29
+ this.secret = RavenDataFile.hashPassword(this.password);
30
+ }
31
+
22
32
  if (this.secret == undefined && RavenDataFile.hasFile()) {
23
33
  this.populateSecretFromFile();
24
34
  }
@@ -26,18 +36,10 @@ class RavenDataFile {
26
36
  if (this.secret == undefined) {
27
37
  this.secret = crypto.randomBytes(32).toString('base64');
28
38
  }
29
- }
30
-
31
- populateSecretFromFile() {
32
- var data = fs.readFileSync(RavenDataFile.secretFile()).toString();
33
- var json = JSON.parse(data);
34
- this.secret = json.secret;
35
- }
36
39
 
37
- shortPath() {
38
- return this.file.replace(process.cwd()+"/", "")
39
40
  }
40
41
 
42
+
41
43
  static eval(options={}) {
42
44
  var iterations = 0;
43
45
 
@@ -63,7 +65,7 @@ class RavenDataFile {
63
65
  }
64
66
 
65
67
  try {
66
- file.data = JSON.parse(file.data.trim());
68
+ file.data = JSON.parse(file.data.toString());
67
69
  } catch(error) {
68
70
  console.log(error);
69
71
  }
@@ -72,6 +74,7 @@ class RavenDataFile {
72
74
  })
73
75
  }
74
76
 
77
+
75
78
  static toEnv(options={}) {
76
79
  var self = this;
77
80
 
@@ -83,19 +86,23 @@ class RavenDataFile {
83
86
  })
84
87
  }
85
88
 
89
+
86
90
  static show() {
87
91
  return fs.readFileSync(this.secretFile()).toString();
88
92
  }
89
93
 
94
+
90
95
  static secretFile() {
91
96
  return path.join(process.cwd(), 'config', 'secret.json');
92
97
  }
93
98
 
99
+
94
100
  static hasFile() {
95
101
  var exists = fs.existsSync(this.secretFile());
96
102
  return exists;
97
103
  }
98
104
 
105
+
99
106
  static generate() {
100
107
  var self = this;
101
108
  return new Promise((resolve) => {
@@ -107,6 +114,7 @@ class RavenDataFile {
107
114
  })
108
115
  }
109
116
 
117
+
110
118
  static init() {
111
119
  var self = this;
112
120
  return new Promise((resolve) => {
@@ -123,16 +131,45 @@ class RavenDataFile {
123
131
  });
124
132
  })
125
133
  }
134
+
126
135
 
127
136
  static secret() {
128
137
  return crypto.randomBytes(32).toString('base64');
138
+ }
139
+
140
+ static hashPassword(password) {
141
+ return crypto.createHash('sha256').update(password).digest('hex').slice(0, 43)+"=";
142
+ }
143
+
144
+ populateSecretFromFile() {
145
+ var data = fs.readFileSync(RavenDataFile.secretFile()).toString();
146
+ var json = JSON.parse(data);
147
+ this.secret = json.secret;
148
+ }
149
+
150
+
151
+ shortPath() {
152
+ return this.file.replace(process.cwd()+"/", "")
153
+ }
154
+
155
+ cipher(options={}) {
156
+ //console.log(options);
157
+ return crypto.createCipheriv(options.algorithm,
158
+ Buffer.from(options.secret, 'base64'),
159
+ options.iv);
160
+ }
161
+
162
+ decipher(options={}) {
163
+ return crypto.createDecipheriv(options.algorithm,
164
+ Buffer.from(options.secret, 'base64'),
165
+ Buffer.from(options.iv, 'hex'));
129
166
  }
130
167
 
131
- encrypt(text) {
168
+ encrypt(buffer) {
132
169
  const iv = crypto.randomBytes(ivLength);
133
- const cipher = crypto.createCipheriv(this.algorithm, Buffer.from(this.secret, 'base64'), iv);
170
+ const cipher = this.cipher({iv: iv, secret: this.secret, algorithm: this.algorithm});
134
171
 
135
- let encrypted = cipher.update(text, 'utf8', 'hex');
172
+ var encrypted = cipher.update(buffer, 'utf8', 'hex');
136
173
  encrypted += cipher.final('hex');
137
174
 
138
175
  const authTag = cipher.getAuthTag().toString('hex');
@@ -141,20 +178,16 @@ class RavenDataFile {
141
178
  return `${iv.toString('hex')}:${authTag}:${encrypted}`;
142
179
  }
143
180
 
144
- decrypt(encryptedData) {
145
- const [ivHex, authTagHex, encryptedText] = encryptedData.split(':');
146
181
 
147
- const decipher = crypto.createDecipheriv(
148
- this.algorithm,
149
- Buffer.from(this.secret, 'base64'),
150
- Buffer.from(ivHex, 'hex')
151
- );
182
+ decrypt(encryptedData) {
183
+ const [ivHex, authTagHex, encryptedText] = encryptedData.toString().split(':');
184
+ const decipher = this.decipher({iv: ivHex, secret: this.secret, algorithm: this.algorithm});
152
185
 
153
186
  decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));
154
-
155
- let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
156
- decrypted += decipher.final('utf8');
157
187
 
188
+ var decrypted = decipher.update(encryptedText, 'hex', 'utf8');
189
+ decrypted += decipher.final('utf8');
190
+
158
191
  return decrypted;
159
192
  }
160
193
 
@@ -162,8 +195,12 @@ class RavenDataFile {
162
195
  read() {
163
196
  var self = this;
164
197
 
165
- return new Promise(async(resolve) => {
166
- fs.readFile(self.file, 'utf8', async (err, data) => {
198
+ return new Promise(async(resolve, reject) => {
199
+ if (fs.existsSync(self.file) == false) {
200
+ return reject(new Error('file doesnt exist.'))
201
+ }
202
+
203
+ fs.readFile(self.file, {}, async (err, data) => {
167
204
  self.data = data;
168
205
 
169
206
  resolve(data);
@@ -171,6 +208,7 @@ class RavenDataFile {
171
208
  })
172
209
  }
173
210
 
211
+
174
212
  save() {
175
213
  var self = this;
176
214
  return new Promise(async(resolve) => {
@@ -187,32 +225,64 @@ class RavenDataFile {
187
225
  })
188
226
  }
189
227
 
228
+
190
229
  conceal() {
191
230
  var self = this;
192
231
  return new Promise(async(resolve) => {
193
- self.read().then((data) => {
194
- self.data = self.encrypt(data);
232
+ const iv = crypto.randomBytes(ivLength);
233
+ const cipher = self.cipher({iv: iv, secret: self.secret, algorithm: self.algorithm});
234
+
235
+ self.read().then(async (buffer) => {
236
+ var readable = Readable.from(buffer)
237
+ const output = new Bufferable();
238
+ readable.pipe(cipher).pipe(output);
195
239
 
196
- self.save().then((response) => {
197
- resolve(response);
198
- })
199
- })
240
+ output.on('finish', async() => {
241
+ const authTag = cipher.getAuthTag().toString('hex');
242
+ var encrypted = output.buffer;
243
+
244
+
245
+ fs.truncate(self.file, 0, () => {
246
+ const stream = fs.createWriteStream(self.file, { flags: 'a' });
247
+ stream.write(`${iv.toString('hex')}:${authTag}:`);
248
+ stream.write(encrypted.toString('base64'));
249
+ stream.end();
250
+
251
+ stream.on('finish', () => {
252
+ resolve({success: true});
253
+ });
254
+ })
255
+ });
256
+ })
200
257
  })
201
258
  }
202
259
 
260
+
203
261
  expose() {
204
262
  var self = this;
205
263
  return new Promise(async(resolve) => {
206
- self.read().then((data) => {
207
- self.data = self.decrypt(data);
208
-
209
- self.save().then((response) => {
210
- resolve(response);
211
- })
264
+ self.read().then((buffer) => {
265
+ const [ivHex, authTagHex, encryptedText] = buffer.toString().split(':');
266
+
267
+ var readable = Readable.from(Buffer.from(encryptedText, 'base64'))
268
+ const output = new Bufferable();
269
+ const decipher = self.decipher({iv:ivHex, secret: self.secret, algorithm: self.algorithm});
270
+ decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));
271
+
272
+ readable.pipe(decipher).pipe(output);
273
+
274
+ output.on('finish', () => {
275
+ self.data = output.buffer;
276
+
277
+ self.save().then(() => {
278
+ resolve({success: true});
279
+ })
280
+ });
212
281
  })
213
282
  })
214
283
  }
215
284
 
285
+
216
286
  }
217
287
 
218
288
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drifted/raven",
3
- "version": "0.0.8",
3
+ "version": "0.0.12",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/driftless-group/raven",
6
6
  "bin": {
@@ -21,6 +21,8 @@
21
21
  "@drifted/qa": "^0.0.5"
22
22
  },
23
23
  "dependencies": {
24
- "carrier-pigeon": "^0.1.11"
24
+ "carrier-pigeon": "^0.1.11",
25
+ "file-type": "^22.0.0",
26
+ "read-chunk": "^5.0.0"
25
27
  }
26
28
  }
package/test/cli.test.js CHANGED
@@ -10,7 +10,7 @@ const {
10
10
  } = require('@drifted/qa');
11
11
 
12
12
  const {
13
- run, ensure, remove
13
+ compare, run, ensure, remove, copy
14
14
  } = require(path.join(__dirname, 'helpers'));
15
15
 
16
16
  var defaults = {
@@ -19,6 +19,23 @@ var defaults = {
19
19
 
20
20
  describe('raven', function() {
21
21
 
22
+ before(async() => {
23
+ var file = path.join(__dirname, 'data', '1984.pdf');
24
+ var encrypted = path.join(__dirname, 'data','1984.pdf.encrypted');
25
+ if (fs.existsSync(encrypted)) {
26
+ await remove(encrypted)
27
+ }
28
+ await copy(file, encrypted);
29
+ })
30
+
31
+ after(async() => {
32
+ var encrypted = path.join(__dirname, 'data','1984.pdf.encrypted');
33
+
34
+ if (fs.existsSync(encrypted)) {
35
+ await remove(encrypted)
36
+ }
37
+ })
38
+
22
39
  describe('human', function() {
23
40
  it('secret', function(done) {
24
41
  run('secret').then((stdout) => {
@@ -28,7 +45,7 @@ describe('raven', function() {
28
45
  assert.notEqual(secret, undefined);
29
46
 
30
47
  done();
31
- }).catch(console.log)
48
+ }).catch(doneMessage(done))
32
49
  })
33
50
 
34
51
  it('init', function(done) {
@@ -100,6 +117,8 @@ describe('raven', function() {
100
117
  }).catch(doneMessage(done));
101
118
  })
102
119
 
120
+
121
+
103
122
  it('repeatedly concealing/exposing', function(done) {
104
123
  var secret = RavenDataFile.secret();
105
124
  var file = path.join(__dirname, 'theraven.txt');
@@ -125,6 +144,32 @@ describe('raven', function() {
125
144
  }).catch(doneMessage(done));
126
145
  }).catch(doneMessage(done));
127
146
  })
147
+
148
+
149
+
150
+ it('conceal/expose a pdf', function(done) {
151
+ var secret = RavenDataFile.secret();
152
+ var original = path.join(__dirname, 'data', '1984.pdf');
153
+ var encrypted = path.join(__dirname, 'data', '1984.pdf.encrypted');
154
+
155
+ run('conceal', '-f', encrypted, '-s', secret).then(() => {
156
+ compare(original, encrypted).then((response) => {
157
+ //console.log(response);
158
+ assert.equal(response.equal, false);
159
+
160
+ run('expose', '-f', encrypted, '-s', secret).then(() => {
161
+ compare(original, encrypted).then((response) => {
162
+ //console.log(response)
163
+ assert.equal(response.equal, true);
164
+ done();
165
+
166
+ }).catch(doneMessage(done));
167
+ }).catch(doneMessage(done));
168
+ }).catch(doneMessage(done));
169
+ }).catch(doneMessage(done));
170
+ })
171
+
128
172
  })
129
173
 
174
+
130
175
  })
package/test/helpers.js CHANGED
@@ -3,6 +3,8 @@ const assert = require('assert');
3
3
  var { exec } = require('child_process');
4
4
  var { mkdir } = require('fs');
5
5
  const fs = require('fs');
6
+ const crypto = require('crypto');
7
+
6
8
 
7
9
 
8
10
  function run(...specifics) {
@@ -47,3 +49,50 @@ function remove(pathname) {
47
49
  })
48
50
  }
49
51
  module.exports.remove = remove;
52
+
53
+
54
+
55
+ function copy(original, newfile) {
56
+ return new Promise((resolve, reject) => {
57
+ fs.copyFile(original, newfile, (err) => {
58
+ if (err) {
59
+ return reject(err);
60
+ }
61
+ resolve();
62
+
63
+ });
64
+ })
65
+ }
66
+ module.exports.copy = copy;
67
+
68
+
69
+
70
+
71
+ function compare(file1, file2) {
72
+ var response = {equal: false};
73
+
74
+ return new Promise((resolve, reject) => {
75
+ if (fs.statSync(file1).size !== fs.statSync(file2).size) {
76
+ response.equal = false;
77
+ response.reason = 'size';
78
+ return resolve(response)
79
+ } else {
80
+ const hash1 = crypto.createHash('sha256').update(fs.readFileSync(file1)).digest('hex');
81
+ const hash2 = crypto.createHash('sha256').update(fs.readFileSync(file2)).digest('hex');
82
+
83
+ if (hash1 !== hash2) {
84
+ response.reason = 'hash';
85
+ response.equal = false;
86
+ resolve(response);
87
+ } else {
88
+ response.equal = true;
89
+ resolve(response);
90
+ }
91
+ }
92
+ })
93
+ }
94
+ module.exports.compare = compare;
95
+
96
+
97
+
98
+
@@ -16,16 +16,9 @@ const {
16
16
  run, ensure, remove
17
17
  } = require(path.join(__dirname, 'helpers'));
18
18
 
19
-
20
-
21
19
  process.chdir(path.join(__dirname, 'workspace'));
22
20
 
23
- console.log(__dirname);
24
-
25
- //console.log(process.env)
26
-
27
21
  describe('raven', function() {
28
-
29
22
 
30
23
  it('eval', function(done) {
31
24
 
@@ -40,19 +33,6 @@ describe('raven', function() {
40
33
 
41
34
  })
42
35
 
43
- it('write', function(done) {
44
- done();
45
- })
46
-
47
- it('encrypt/decrypt', function(done) {
48
- var jdf = new RavenDataFile({secret: defaults.secret, data: {test: true}});
49
- var result = jdf.encrypt(JSON.stringify(jdf.data));
50
- result = JSON.parse(jdf.decrypt(result));
51
- assert.equal(result.test, true);
52
-
53
- done();
54
- })
55
-
56
36
 
57
37
 
58
38