@drifted/raven 0.0.6 → 0.0.11

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
@@ -8,10 +8,12 @@ const JSONDataFile = require(path.join(__dirname, '..'));
8
8
 
9
9
 
10
10
  var parser = new CarrierPigeon({strict: true});
11
- parser.commands('usage', 'show', 'secret', 'init', 'generate', 'expose', 'conceal', 'encrypt', 'decrypt');
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});
@@ -165,3 +167,11 @@ if (command == 'usage') {
165
167
  }
166
168
 
167
169
 
170
+
171
+ if (command == 'version') {
172
+ var data = fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString();
173
+ var json = JSON.parse(data);
174
+ console.log(json.version)
175
+ }
176
+
177
+
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)+"=";
129
142
  }
130
143
 
131
- encrypt(text) {
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'));
166
+ }
167
+
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,13 @@ 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
+ fs.readFile(self.file, {}, async (err, data) => {
203
+ //self.type = isAscii(data);
204
+ //console.log('type', self.type);
167
205
  self.data = data;
168
206
 
169
207
  resolve(data);
@@ -171,6 +209,7 @@ class RavenDataFile {
171
209
  })
172
210
  }
173
211
 
212
+
174
213
  save() {
175
214
  var self = this;
176
215
  return new Promise(async(resolve) => {
@@ -187,32 +226,66 @@ class RavenDataFile {
187
226
  })
188
227
  }
189
228
 
229
+
190
230
  conceal() {
191
231
  var self = this;
192
232
  return new Promise(async(resolve) => {
193
- self.read().then((data) => {
194
- self.data = self.encrypt(data);
233
+ const iv = crypto.randomBytes(ivLength);
234
+ const cipher = self.cipher({iv: iv, secret: self.secret, algorithm: self.algorithm});
235
+
236
+ //console.log(self);
237
+
238
+ self.read().then(async (buffer) => {
239
+ var readable = Readable.from(buffer)
240
+ const output = new Bufferable();
241
+ readable.pipe(cipher).pipe(output);
195
242
 
196
- self.save().then((response) => {
197
- resolve(response);
198
- })
199
- })
243
+ output.on('finish', async() => {
244
+ const authTag = cipher.getAuthTag().toString('hex');
245
+ var encrypted = output.buffer;
246
+
247
+
248
+ fs.truncate(self.file, 0, () => {
249
+ const stream = fs.createWriteStream(self.file, { flags: 'a' });
250
+ stream.write(`${iv.toString('hex')}:${authTag}:`);
251
+ stream.write(encrypted.toString('base64'));
252
+ stream.end();
253
+
254
+ stream.on('finish', () => {
255
+ resolve({success: true});
256
+ });
257
+ })
258
+ });
259
+ })
200
260
  })
201
261
  }
202
262
 
263
+
203
264
  expose() {
204
265
  var self = this;
205
266
  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
- })
267
+ self.read().then((buffer) => {
268
+ const [ivHex, authTagHex, encryptedText] = buffer.toString().split(':');
269
+
270
+ var readable = Readable.from(Buffer.from(encryptedText, 'base64'))
271
+ const output = new Bufferable();
272
+ const decipher = self.decipher({iv:ivHex, secret: self.secret, algorithm: self.algorithm});
273
+ decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));
274
+
275
+ readable.pipe(decipher).pipe(output);
276
+
277
+ output.on('finish', () => {
278
+ self.data = output.buffer;
279
+
280
+ self.save().then(() => {
281
+ resolve({success: true});
282
+ })
283
+ });
212
284
  })
213
285
  })
214
286
  }
215
287
 
288
+
216
289
  }
217
290
 
218
291
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drifted/raven",
3
- "version": "0.0.6",
3
+ "version": "0.0.11",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/driftless-group/raven",
6
6
  "bin": {
@@ -15,14 +15,14 @@
15
15
  "type": "commonjs",
16
16
  "main": "index.js",
17
17
  "scripts": {
18
- "test": "mocha -w",
19
- "encrypt": "./bin/file.js encrypt",
20
- "decrypt": "./bin/file.js decrypt"
18
+ "test": "mocha -w"
21
19
  },
22
20
  "devDependencies": {
23
21
  "@drifted/qa": "^0.0.5"
24
22
  },
25
23
  "dependencies": {
26
- "carrier-pigeon": "^0.1.11"
24
+ "carrier-pigeon": "^0.1.11",
25
+ "file-type": "^22.0.0",
26
+ "read-chunk": "^5.0.0"
27
27
  }
28
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,25 @@ 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
+
26
+ if (fs.existsSync(encrypted)) {
27
+ await remove(encrypted)
28
+ }
29
+
30
+ await copy(file, encrypted);
31
+ })
32
+
33
+ after(async() => {
34
+ var encrypted = path.join(__dirname, 'data','1984.pdf.encrypted');
35
+
36
+ if (fs.existsSync(encrypted)) {
37
+ await remove(encrypted)
38
+ }
39
+ })
40
+
22
41
  describe('human', function() {
23
42
  it('secret', function(done) {
24
43
  run('secret').then((stdout) => {
@@ -28,7 +47,7 @@ describe('raven', function() {
28
47
  assert.notEqual(secret, undefined);
29
48
 
30
49
  done();
31
- }).catch(console.log)
50
+ }).catch(doneMessage(done))
32
51
  })
33
52
 
34
53
  it('init', function(done) {
@@ -100,6 +119,8 @@ describe('raven', function() {
100
119
  }).catch(doneMessage(done));
101
120
  })
102
121
 
122
+
123
+
103
124
  it('repeatedly concealing/exposing', function(done) {
104
125
  var secret = RavenDataFile.secret();
105
126
  var file = path.join(__dirname, 'theraven.txt');
@@ -125,6 +146,31 @@ describe('raven', function() {
125
146
  }).catch(doneMessage(done));
126
147
  }).catch(doneMessage(done));
127
148
  })
149
+
150
+
151
+
152
+ it('conceal/expose a pdf', function(done) {
153
+ var secret = RavenDataFile.secret();
154
+ var original = path.join(__dirname, 'data', '1984.pdf');
155
+ var encrypted = path.join(__dirname, 'data', '1984.pdf.encrypted');
156
+
157
+ run('conceal', '-f', encrypted, '-s', secret).then(() => {
158
+ compare(original, encrypted).then((response) => {
159
+ assert.equal(response.equal, false);
160
+
161
+ run('expose', '-f', encrypted, '-s', secret).then(() => {
162
+ compare(original, encrypted).then((response) => {
163
+ //console.log(response)
164
+ assert.equal(response.equal, true);
165
+ done();
166
+
167
+ }).catch(doneMessage(done));
168
+ }).catch(doneMessage(done));
169
+ }).catch(doneMessage(done));
170
+ }).catch(doneMessage(done));
171
+ })
172
+
128
173
  })
129
174
 
175
+
130
176
  })
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,48 @@ 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 = true;
77
+ return resolve(response)
78
+ } else {
79
+ const hash1 = crypto.createHash('sha256').update(fs.readFileSync(file1)).digest('hex');
80
+ const hash2 = crypto.createHash('sha256').update(fs.readFileSync(file2)).digest('hex');
81
+
82
+ if (hash1 !== hash2) {
83
+ response.equal = false;
84
+ resolve(response);
85
+ } else {
86
+ response.equal = true;
87
+ resolve(response);
88
+ }
89
+ }
90
+ })
91
+ }
92
+ module.exports.compare = compare;
93
+
94
+
95
+
96
+
@@ -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
 
File without changes