obscr 0.1.1 → 0.2.1
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/CHANGELOG.md +92 -0
- package/README.md +214 -26
- package/bin/index.js +417 -85
- package/bin/utils/crypto.js +69 -30
- package/bin/utils/steg.js +193 -86
- package/bin/utils/utils.js +113 -64
- package/package.json +24 -4
- package/.github/workflows/publish.yml +0 -18
package/bin/utils/utils.js
CHANGED
|
@@ -1,45 +1,59 @@
|
|
|
1
1
|
const { MersenneTwister } = require("./mersenne-twister");
|
|
2
2
|
const SHA512 = require("crypto-js/sha512");
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Generates a hashed order array for data scrambling using password-based seeding
|
|
6
|
+
* @param {string} password - The password to generate the seed from
|
|
7
|
+
* @param {number} arrayLength - The length of the order array to generate
|
|
8
|
+
* @returns {number[]} Array of indices in scrambled order
|
|
9
|
+
*/
|
|
10
|
+
const get_hashed_order = (password, arrayLength) => {
|
|
11
|
+
// O(arrayLength) algorithm using Fisher-Yates shuffle
|
|
12
|
+
const orders = Array.from(Array(arrayLength).keys());
|
|
7
13
|
const result = [];
|
|
8
|
-
let
|
|
9
|
-
const seed = SHA512(password).words.reduce(
|
|
14
|
+
let location;
|
|
15
|
+
const seed = SHA512(password).words.reduce((total, num) => {
|
|
10
16
|
return total + Math.abs(num);
|
|
11
17
|
}, 0);
|
|
12
|
-
const
|
|
13
|
-
for (let i =
|
|
14
|
-
|
|
15
|
-
result.push(orders[
|
|
16
|
-
orders[
|
|
18
|
+
const rng = new MersenneTwister(seed);
|
|
19
|
+
for (let i = arrayLength; i > 0; i--) {
|
|
20
|
+
location = rng.genrand_int32() % i;
|
|
21
|
+
result.push(orders[location]);
|
|
22
|
+
orders[location] = orders[i - 1];
|
|
17
23
|
}
|
|
18
24
|
return result;
|
|
19
25
|
};
|
|
20
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Decodes a UTF-8 byte array to a string
|
|
29
|
+
* @param {number[]} bytes - Array of byte values
|
|
30
|
+
* @returns {string} Decoded UTF-8 string
|
|
31
|
+
*/
|
|
21
32
|
const utf8Decode = (bytes) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
c2,
|
|
27
|
-
c3;
|
|
33
|
+
const chars = [];
|
|
34
|
+
let offset = 0;
|
|
35
|
+
const length = bytes.length;
|
|
36
|
+
let currentByte, secondByte, thirdByte;
|
|
28
37
|
|
|
29
38
|
while (offset < length) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
currentByte = bytes[offset];
|
|
40
|
+
secondByte = bytes[offset + 1];
|
|
41
|
+
thirdByte = bytes[offset + 2];
|
|
33
42
|
|
|
34
|
-
if (128 >
|
|
35
|
-
|
|
43
|
+
if (128 > currentByte) {
|
|
44
|
+
// Single-byte character (ASCII)
|
|
45
|
+
chars.push(String.fromCharCode(currentByte));
|
|
36
46
|
offset += 1;
|
|
37
|
-
} else if (191 <
|
|
38
|
-
|
|
47
|
+
} else if (191 < currentByte && currentByte < 224) {
|
|
48
|
+
// Two-byte character
|
|
49
|
+
chars.push(String.fromCharCode(((currentByte & 31) << 6) | (secondByte & 63)));
|
|
39
50
|
offset += 2;
|
|
40
51
|
} else {
|
|
52
|
+
// Three-byte character
|
|
41
53
|
chars.push(
|
|
42
|
-
String.fromCharCode(
|
|
54
|
+
String.fromCharCode(
|
|
55
|
+
((currentByte & 15) << 12) | ((secondByte & 63) << 6) | (thirdByte & 63)
|
|
56
|
+
)
|
|
43
57
|
);
|
|
44
58
|
offset += 3;
|
|
45
59
|
}
|
|
@@ -48,11 +62,16 @@ const utf8Decode = (bytes) => {
|
|
|
48
62
|
return chars.join("");
|
|
49
63
|
};
|
|
50
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Encodes a string to a UTF-8 byte array
|
|
67
|
+
* @param {string} str - The string to encode
|
|
68
|
+
* @returns {number[]} Array of byte values
|
|
69
|
+
*/
|
|
51
70
|
const utf8Encode = (str) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
const bytes = [];
|
|
72
|
+
let offset = 0;
|
|
73
|
+
let length;
|
|
74
|
+
let char;
|
|
56
75
|
|
|
57
76
|
str = encodeURI(str);
|
|
58
77
|
length = str.length;
|
|
@@ -73,51 +92,81 @@ const utf8Encode = (str) => {
|
|
|
73
92
|
return bytes;
|
|
74
93
|
};
|
|
75
94
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for (
|
|
92
|
-
|
|
93
|
-
merge_bits(
|
|
94
|
-
bitarray.slice((i * 8 + j) * num_copy, (i * 8 + j + 1) * num_copy)
|
|
95
|
-
) * tmp;
|
|
96
|
-
tmp = Math.floor(tmp / 2);
|
|
95
|
+
/**
|
|
96
|
+
* Converts a bit array to a string using majority voting for error correction
|
|
97
|
+
* @param {number[]} bitArray - Array of bits (0s and 1s)
|
|
98
|
+
* @param {number} numCopies - Number of redundant copies per bit for error correction
|
|
99
|
+
* @returns {string} Decoded string
|
|
100
|
+
*/
|
|
101
|
+
const bits_to_str = (bitArray, numCopies) => {
|
|
102
|
+
/**
|
|
103
|
+
* Merges multiple copies of a bit using majority voting
|
|
104
|
+
* @param {number[]} bits - Array of bit copies
|
|
105
|
+
* @returns {number} 0 or 1 based on majority
|
|
106
|
+
*/
|
|
107
|
+
const mergeBits = (bits) => {
|
|
108
|
+
const bitsLength = bits.length;
|
|
109
|
+
let bitsSum = 0;
|
|
110
|
+
for (let i = 0; i < bitsLength; i++) {
|
|
111
|
+
bitsSum += bits[i];
|
|
97
112
|
}
|
|
98
|
-
|
|
99
|
-
|
|
113
|
+
return Math.round(bitsSum / bitsLength);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const messageArray = [];
|
|
117
|
+
let byteValue, powerOfTwo;
|
|
118
|
+
|
|
119
|
+
const messageArrayLength = Math.floor(Math.floor(bitArray.length / numCopies) / 8);
|
|
120
|
+
for (let i = 0; i < messageArrayLength; i++) {
|
|
121
|
+
byteValue = 0;
|
|
122
|
+
powerOfTwo = 128;
|
|
123
|
+
for (let j = 0; j < 8; j++) {
|
|
124
|
+
byteValue +=
|
|
125
|
+
mergeBits(
|
|
126
|
+
bitArray.slice((i * 8 + j) * numCopies, (i * 8 + j + 1) * numCopies)
|
|
127
|
+
) * powerOfTwo;
|
|
128
|
+
powerOfTwo = Math.floor(powerOfTwo / 2);
|
|
129
|
+
}
|
|
130
|
+
if (byteValue === 255) break; // END NOTATION
|
|
131
|
+
messageArray.push(byteValue);
|
|
100
132
|
}
|
|
101
133
|
|
|
102
|
-
return utf8Decode(
|
|
134
|
+
return utf8Decode(messageArray);
|
|
103
135
|
};
|
|
104
136
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Converts a string to a bit array with redundant copies for error correction
|
|
139
|
+
* @param {string} str - The string to convert
|
|
140
|
+
* @param {number} numCopies - Number of redundant copies per bit
|
|
141
|
+
* @returns {number[]} Array of bits with redundancy
|
|
142
|
+
*/
|
|
143
|
+
const str_to_bits = (str, numCopies) => {
|
|
144
|
+
const utf8Array = utf8Encode(str);
|
|
145
|
+
const result = [];
|
|
146
|
+
const utf8Length = utf8Array.length;
|
|
147
|
+
|
|
148
|
+
for (let i = 0; i < utf8Length; i++) {
|
|
149
|
+
for (let powerOfTwo = 128; powerOfTwo > 0; powerOfTwo = Math.floor(powerOfTwo / 2)) {
|
|
150
|
+
if (Math.floor(utf8Array[i] / powerOfTwo)) {
|
|
151
|
+
for (let copy = 0; copy < numCopies; copy++) {
|
|
152
|
+
result.push(1);
|
|
153
|
+
}
|
|
154
|
+
utf8Array[i] -= powerOfTwo;
|
|
155
|
+
} else {
|
|
156
|
+
for (let copy = 0; copy < numCopies; copy++) {
|
|
157
|
+
result.push(0);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
115
160
|
}
|
|
116
161
|
}
|
|
117
|
-
|
|
118
|
-
|
|
162
|
+
|
|
163
|
+
// Add end marker (24 bits of 1s)
|
|
164
|
+
for (let j = 0; j < 24; j++) {
|
|
165
|
+
for (let i = 0; i < numCopies; i++) {
|
|
119
166
|
result.push(1);
|
|
120
167
|
}
|
|
168
|
+
}
|
|
169
|
+
|
|
121
170
|
return result;
|
|
122
171
|
};
|
|
123
172
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "obscr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Encrypt and hide your secure data",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"obscr": "./bin/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "jest --coverage",
|
|
11
|
+
"test:watch": "jest --watch",
|
|
12
|
+
"test:unit": "jest test/unit",
|
|
13
|
+
"test:integration": "jest test/integration"
|
|
11
14
|
},
|
|
12
15
|
"publishConfig": {
|
|
13
16
|
"registry": "https://registry.npmjs.org"
|
|
@@ -23,10 +26,27 @@
|
|
|
23
26
|
"crypto"
|
|
24
27
|
],
|
|
25
28
|
"dependencies": {
|
|
26
|
-
"
|
|
29
|
+
"boxen": "^5.1.2",
|
|
27
30
|
"chalk": "^4.1.2",
|
|
31
|
+
"cli-progress": "^3.12.0",
|
|
28
32
|
"crypto-js": "^4.1.1",
|
|
29
33
|
"inquirer": "^8.0.0",
|
|
34
|
+
"ora": "^5.4.1",
|
|
35
|
+
"pngjs": "^6.0.0",
|
|
30
36
|
"yargs": "^17.5.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"jest": "^29.7.0"
|
|
40
|
+
},
|
|
41
|
+
"jest": {
|
|
42
|
+
"testEnvironment": "node",
|
|
43
|
+
"coverageDirectory": "coverage",
|
|
44
|
+
"collectCoverageFrom": [
|
|
45
|
+
"bin/**/*.js",
|
|
46
|
+
"!bin/index.js"
|
|
47
|
+
],
|
|
48
|
+
"testMatch": [
|
|
49
|
+
"**/test/**/*.test.js"
|
|
50
|
+
]
|
|
31
51
|
}
|
|
32
|
-
}
|
|
52
|
+
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
name: Publish Package to npmjs
|
|
2
|
-
on:
|
|
3
|
-
release:
|
|
4
|
-
types: [created]
|
|
5
|
-
jobs:
|
|
6
|
-
build:
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
steps:
|
|
9
|
-
- uses: actions/checkout@v3
|
|
10
|
-
# Setup .npmrc file to publish to npm
|
|
11
|
-
- uses: actions/setup-node@v3
|
|
12
|
-
with:
|
|
13
|
-
node-version: "16.x"
|
|
14
|
-
registry-url: "https://registry.npmjs.org"
|
|
15
|
-
- run: npm ci
|
|
16
|
-
- run: npm publish
|
|
17
|
-
env:
|
|
18
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|