dstu-core 0.4.0 → 0.5.0
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 +4 -3
- package/js/index.js +2 -3
- package/js/secretstream.js +148 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -55,12 +55,13 @@ console.log(dstu.secretboxOpen(key, sealed).toString()); // "a message worth pro
|
|
|
55
55
|
| Module | Functions/classes | Notes |
|
|
56
56
|
|---|---|---|
|
|
57
57
|
| `crypto_secretbox` | `secretboxKeygen`, `secretboxSeal`, `secretboxOpen` | Single-message authenticated encryption. `examples/secretbox.js`. |
|
|
58
|
-
| `crypto_box` | `boxKeygen`, `boxPublicKey`, `boxSeal`, `boxOpen` | Public-key encryption (hybrid via KDF over `hazmat::dstu9041`, `l(p)=256`, D-169). `boxSeal`/`boxOpen`
|
|
59
|
-
| `
|
|
58
|
+
| `crypto_box` | `boxKeygen`, `boxPublicKey`, `boxSeal`, `boxOpen`, `BoxEncryptor`, `BoxDecryptor` | Public-key encryption (hybrid via KDF over `hazmat::dstu9041`, `l(p)=256`, D-169). `boxSeal`/`boxOpen` hold the whole message in memory; the `stream.Transform` `BoxEncryptor`/`BoxDecryptor` write and read the same bytes (format v3) chunk by chunk, as `uacrypt box-seal`/`box-open` do. `examples/box.js`. |
|
|
59
|
+
| `crypto_box384` | `box384Keygen`, `box384PublicKey`, `box384Seal`, `box384Open`, `Box384Encryptor`, `Box384Decryptor` | `l(p)=384`/E384/1 sibling of `crypto_box` (T-297). `examples/box384.js`. |
|
|
60
|
+
| `crypto_box512` | `box512Keygen`, `box512PublicKey`, `box512Seal`, `box512Open`, `Box512Encryptor`, `Box512Decryptor` | `l(p)=512`/E512/1 sibling of `crypto_box` (T-193/T-204). `examples/box512.js`. |
|
|
60
61
|
| `crypto_secretstream` | `secretstreamKeygen`, `SecretStreamPushState`, `SecretStreamPullState`, `SecretStreamEncryptor`, `SecretStreamDecryptor` | Chunked streaming AEAD. The `stream.Transform` `SecretStreamEncryptor`/`SecretStreamDecryptor` wire format matches `uacrypt encrypt`/`decrypt` exactly (D-118). `examples/secretstream-file.js`. |
|
|
61
62
|
| `crypto_sign` | `signKeygen`, `signVerifyingKey`, `signMessage`, `signVerify` | DSTU 4145 `m=163` digital signatures, deterministic nonce (no RNG dependency). `examples/sign.js`. |
|
|
62
63
|
| `crypto_sign257` | `sign257Keygen`, `sign257VerifyingKey`, `sign257Message`, `sign257Verify` | `m=257` sibling of `crypto_sign` (T-199/T-204) — the curve real Diia-issued qualified signatures use. `examples/sign257.js`. |
|
|
63
|
-
| `crypto_pwhash` | `pwhashHashPassword`, `pwhashVerifyPassword`, `PWHASH_INTERACTIVE`/`PWHASH_MODERATE`/`PWHASH_SENSITIVE` | Argon2id (the one deliberately non-DSTU component, D-49/D-50). `examples/password-hashing.js`. |
|
|
64
|
+
| `crypto_pwhash` | `pwhashHashPassword`, `pwhashVerifyPassword`, `pwhashDeriveKey`, `PWHASH_INTERACTIVE`/`PWHASH_MODERATE`/`PWHASH_SENSITIVE`, `PassphraseEncryptor`, `PassphraseDecryptor` | Argon2id (the one deliberately non-DSTU component, D-49/D-50). `pwhashDeriveKey` equals libsodium's `crypto_pwhash`. `PassphraseEncryptor`/`PassphraseDecryptor` write and read the files `uacrypt encrypt --passphrase` makes (D-219/D-240); each runs Argon2id at 256 MiB once, synchronously on the calling thread like `pwhashHashPassword`. `examples/password-hashing.js`. |
|
|
64
65
|
| `crypto_auth` | `authKeygen`, `auth`, `authVerify` | Keyed message authentication (Kupyna-KMAC). `examples/misc.js`. |
|
|
65
66
|
| `crypto_kdf` | `kdfKeygen`, `kdfDeriveSubkey` | Deterministic subkey derivation. `subkeyId` is a plain non-negative `number`, not `BigInt`. `examples/misc.js`. |
|
|
66
67
|
| `crypto_generichash` | `kupyna256`, `kupyna512`, `Kupyna256Hasher`, `Kupyna512Hasher` | One-shot and streaming Kupyna hashing. `examples/misc.js`. |
|
package/js/index.js
CHANGED
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
const native = require('../native/index.js');
|
|
18
|
-
const
|
|
18
|
+
const streams = require('./secretstream.js');
|
|
19
19
|
|
|
20
20
|
module.exports = {
|
|
21
21
|
...native,
|
|
22
|
-
|
|
23
|
-
SecretStreamDecryptor,
|
|
22
|
+
...streams,
|
|
24
23
|
};
|
package/js/secretstream.js
CHANGED
|
@@ -42,11 +42,16 @@ const CHUNK_BYTES = 8 * 1024;
|
|
|
42
42
|
class SecretStreamEncryptor extends Transform {
|
|
43
43
|
constructor(key, options) {
|
|
44
44
|
super(options);
|
|
45
|
-
this._enc =
|
|
45
|
+
this._enc = this._startCodec(key);
|
|
46
46
|
this._buf = Buffer.alloc(0);
|
|
47
47
|
this.push(this._enc.prefix);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/** The native codec for `key`; the box/passphrase subclasses start it their own way. */
|
|
51
|
+
_startCodec(key) {
|
|
52
|
+
return new native.SecretStreamFileEncryptor(key);
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
_transform(chunk, _encoding, callback) {
|
|
51
56
|
this._buf = Buffer.concat([this._buf, chunk]);
|
|
52
57
|
let err;
|
|
@@ -96,14 +101,27 @@ class SecretStreamEncryptor extends Transform {
|
|
|
96
101
|
class SecretStreamDecryptor extends Transform {
|
|
97
102
|
constructor(key, options) {
|
|
98
103
|
super(options);
|
|
99
|
-
this._dec =
|
|
104
|
+
this._dec = this._startCodec(key);
|
|
100
105
|
this._buf = Buffer.alloc(0);
|
|
101
106
|
}
|
|
102
107
|
|
|
108
|
+
/** The native codec for `key`, or null while a subclass still collects its prefix. */
|
|
109
|
+
_startCodec(key) {
|
|
110
|
+
return new native.SecretStreamFileDecryptor(key);
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
_transform(chunk, _encoding, callback) {
|
|
104
114
|
this._buf = Buffer.concat([this._buf, chunk]);
|
|
105
115
|
let err;
|
|
106
116
|
try {
|
|
117
|
+
if (this._dec === null) {
|
|
118
|
+
if (this._buf.length < this._prefixBytes) {
|
|
119
|
+
process.nextTick(callback);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this._dec = this._openPrefixed(this._buf.subarray(0, this._prefixBytes));
|
|
123
|
+
this._buf = this._buf.subarray(this._prefixBytes);
|
|
124
|
+
}
|
|
107
125
|
let pos = 0;
|
|
108
126
|
for (let want = this._dec.nextLen(); this._buf.length - pos >= want; want = this._dec.nextLen()) {
|
|
109
127
|
const plaintext = this._dec.feed(this._buf.subarray(pos, pos + want));
|
|
@@ -121,6 +139,7 @@ class SecretStreamDecryptor extends Transform {
|
|
|
121
139
|
_flush(callback) {
|
|
122
140
|
let err;
|
|
123
141
|
try {
|
|
142
|
+
if (this._dec === null) throw new Error(`input too short to contain a ${this._prefixName}`);
|
|
124
143
|
const plaintext = this._dec.finish();
|
|
125
144
|
if (plaintext.length > 0) this.push(plaintext);
|
|
126
145
|
} catch (e) {
|
|
@@ -130,4 +149,130 @@ class SecretStreamDecryptor extends Transform {
|
|
|
130
149
|
}
|
|
131
150
|
}
|
|
132
151
|
|
|
133
|
-
|
|
152
|
+
function checkLength(buf, bytes, name) {
|
|
153
|
+
if (!Buffer.isBuffer(buf) || buf.length !== bytes) {
|
|
154
|
+
throw new TypeError(`${name} must be a ${bytes}-byte Buffer`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* `crypto_box` sealed to `publicKey` as a stream (format v3, T-265/T-269): the same bytes
|
|
160
|
+
* `boxSeal` returns, written chunk by chunk; `boxOpen`/`BoxDecryptor` read them.
|
|
161
|
+
*/
|
|
162
|
+
class BoxEncryptor extends SecretStreamEncryptor {
|
|
163
|
+
_startCodec(publicKey) {
|
|
164
|
+
return native.SecretStreamFileEncryptor.boxSeal(publicKey);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** `crypto_box384` counterpart of `BoxEncryptor`. */
|
|
169
|
+
class Box384Encryptor extends SecretStreamEncryptor {
|
|
170
|
+
_startCodec(publicKey) {
|
|
171
|
+
return native.SecretStreamFileEncryptor.box384Seal(publicKey);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** `crypto_box512` counterpart of `BoxEncryptor`. */
|
|
176
|
+
class Box512Encryptor extends SecretStreamEncryptor {
|
|
177
|
+
_startCodec(publicKey) {
|
|
178
|
+
return native.SecretStreamFileEncryptor.box512Seal(publicKey);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A passphrase file (D-219/D-240) - the bytes `uacrypt encrypt --passphrase` writes. The
|
|
184
|
+
* constructor runs Argon2id (256 MiB, t=3, about a second) on the calling thread.
|
|
185
|
+
*/
|
|
186
|
+
class PassphraseEncryptor extends SecretStreamEncryptor {
|
|
187
|
+
_startCodec(passphrase) {
|
|
188
|
+
return native.SecretStreamFileEncryptor.passphraseSeal(passphrase);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Collects the bytes in front of the stream file, then opens the codec with them. The secret is
|
|
194
|
+
* held only until then.
|
|
195
|
+
*/
|
|
196
|
+
class PrefixedDecryptor extends SecretStreamDecryptor {
|
|
197
|
+
_startCodec(secret) {
|
|
198
|
+
this._secret = secret;
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
_openPrefixed(prefix) {
|
|
203
|
+
const dec = this._openCodec(this._secret, prefix);
|
|
204
|
+
this._secret = null;
|
|
205
|
+
return dec;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Reads a `crypto_box` stream (or a `boxSeal` blob) with bounded memory. */
|
|
210
|
+
class BoxDecryptor extends PrefixedDecryptor {
|
|
211
|
+
constructor(secretKey, options) {
|
|
212
|
+
checkLength(secretKey, 32, 'secretKey');
|
|
213
|
+
super(secretKey, options);
|
|
214
|
+
this._prefixBytes = native.BOX_OPEN_STREAM_PREFIX_BYTES;
|
|
215
|
+
this._prefixName = 'sealed message';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
_openCodec(secretKey, prefix) {
|
|
219
|
+
return native.SecretStreamFileDecryptor.boxOpen(secretKey, prefix);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** `crypto_box384` counterpart of `BoxDecryptor`. */
|
|
224
|
+
class Box384Decryptor extends PrefixedDecryptor {
|
|
225
|
+
constructor(secretKey, options) {
|
|
226
|
+
checkLength(secretKey, 48, 'secretKey');
|
|
227
|
+
super(secretKey, options);
|
|
228
|
+
this._prefixBytes = native.BOX384_OPEN_STREAM_PREFIX_BYTES;
|
|
229
|
+
this._prefixName = 'sealed message';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
_openCodec(secretKey, prefix) {
|
|
233
|
+
return native.SecretStreamFileDecryptor.box384Open(secretKey, prefix);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** `crypto_box512` counterpart of `BoxDecryptor`. */
|
|
238
|
+
class Box512Decryptor extends PrefixedDecryptor {
|
|
239
|
+
constructor(secretKey, options) {
|
|
240
|
+
checkLength(secretKey, 64, 'secretKey');
|
|
241
|
+
super(secretKey, options);
|
|
242
|
+
this._prefixBytes = native.BOX512_OPEN_STREAM_PREFIX_BYTES;
|
|
243
|
+
this._prefixName = 'sealed message';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
_openCodec(secretKey, prefix) {
|
|
247
|
+
return native.SecretStreamFileDecryptor.box512Open(secretKey, prefix);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Reads a passphrase file. The header is checked before Argon2 runs (on the calling thread, once
|
|
253
|
+
* the header has arrived); a wrong passphrase errors at the first record.
|
|
254
|
+
*/
|
|
255
|
+
class PassphraseDecryptor extends PrefixedDecryptor {
|
|
256
|
+
constructor(passphrase, options) {
|
|
257
|
+
super(passphrase, options);
|
|
258
|
+
this._prefixBytes = native.PASSPHRASE_FILE_HEADER_BYTES;
|
|
259
|
+
this._prefixName = 'passphrase file header';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
_openCodec(passphrase, header) {
|
|
263
|
+
return native.SecretStreamFileDecryptor.passphraseOpen(header, passphrase);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
module.exports = {
|
|
268
|
+
SecretStreamEncryptor,
|
|
269
|
+
SecretStreamDecryptor,
|
|
270
|
+
BoxEncryptor,
|
|
271
|
+
BoxDecryptor,
|
|
272
|
+
Box384Encryptor,
|
|
273
|
+
Box384Decryptor,
|
|
274
|
+
Box512Encryptor,
|
|
275
|
+
Box512Decryptor,
|
|
276
|
+
PassphraseEncryptor,
|
|
277
|
+
PassphraseDecryptor,
|
|
278
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dstu-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Node.js bindings for dstu-core (Ukrainian DSTU cryptographic standards)",
|
|
6
6
|
"main": "js/index.js",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"prepublishOnly": "napi prepublish -t npm --skip-gh-release"
|
|
48
48
|
},
|
|
49
49
|
"optionalDependencies": {
|
|
50
|
-
"dstu-core-linux-x64-gnu": "0.
|
|
51
|
-
"dstu-core-darwin-arm64": "0.
|
|
52
|
-
"dstu-core-win32-x64-msvc": "0.
|
|
53
|
-
"dstu-core-linux-arm64-gnu": "0.
|
|
50
|
+
"dstu-core-linux-x64-gnu": "0.5.0",
|
|
51
|
+
"dstu-core-darwin-arm64": "0.5.0",
|
|
52
|
+
"dstu-core-win32-x64-msvc": "0.5.0",
|
|
53
|
+
"dstu-core-linux-arm64-gnu": "0.5.0"
|
|
54
54
|
}
|
|
55
55
|
}
|