dstu-core 0.3.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 +174 -81
- 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
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Idiomatic `crypto_secretstream` pipeline (docs/DECISIONS.md D-118, docs/bindings-strategy.md
|
|
5
|
-
* T-50 step 3) - hides chunk/tag/length-prefix bookkeeping behind a `stream.Transform` pair
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* T-50 step 3) - hides chunk/tag/length-prefix bookkeeping behind a `stream.Transform` pair. The
|
|
6
|
+
* framing itself (record layout, length rules, Final, trailing data) is dstu-core's stream-file
|
|
7
|
+
* codec, exposed as SecretStreamFileEncryptor/SecretStreamFileDecryptor (T-284); this module only
|
|
8
|
+
* buffers. Mirrors
|
|
8
9
|
* bindings/python/python/dstu_core/secretstream.py's own design exactly (see D-118's two
|
|
9
10
|
* standing pitfalls this port re-checks below).
|
|
10
11
|
*
|
|
@@ -19,12 +20,9 @@
|
|
|
19
20
|
const { Transform } = require('node:stream');
|
|
20
21
|
const native = require('../native/index.js');
|
|
21
22
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
23
|
+
// dstu-core's crypto_secretstream::CHUNK_BYTES (D-208): the codec seals and checks it; this module
|
|
24
|
+
// only needs it to cut input into chunks.
|
|
24
25
|
const CHUNK_BYTES = 8 * 1024;
|
|
25
|
-
const AUTH_TAG_BYTES = 16;
|
|
26
|
-
// Matches dstu-core's crypto_secretstream::FORMAT_VERSION (D-208); the shared vectors pin it.
|
|
27
|
-
const FORMAT_VERSION = 2;
|
|
28
26
|
|
|
29
27
|
/**
|
|
30
28
|
* Write-only Transform: buffers input and pushes each full 8 KiB chunk downstream as it fills,
|
|
@@ -44,27 +42,28 @@ const FORMAT_VERSION = 2;
|
|
|
44
42
|
class SecretStreamEncryptor extends Transform {
|
|
45
43
|
constructor(key, options) {
|
|
46
44
|
super(options);
|
|
47
|
-
this.
|
|
45
|
+
this._enc = this._startCodec(key);
|
|
48
46
|
this._buf = Buffer.alloc(0);
|
|
49
|
-
this.push(
|
|
47
|
+
this.push(this._enc.prefix);
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
lenBuf.writeUInt32LE(data.length, 0);
|
|
56
|
-
this.push(Buffer.concat([Buffer.from([tag]), lenBuf, ciphertext, authTag]));
|
|
50
|
+
/** The native codec for `key`; the box/passphrase subclasses start it their own way. */
|
|
51
|
+
_startCodec(key) {
|
|
52
|
+
return new native.SecretStreamFileEncryptor(key);
|
|
57
53
|
}
|
|
58
54
|
|
|
59
55
|
_transform(chunk, _encoding, callback) {
|
|
60
56
|
this._buf = Buffer.concat([this._buf, chunk]);
|
|
61
57
|
let err;
|
|
62
58
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
// Hold back the last chunk (even a full one) until _flush: only then is it known to be the
|
|
60
|
+
// Final one (an exact multiple of 8 KiB ends in a full Final record, D-208).
|
|
61
|
+
let pos = 0;
|
|
62
|
+
while (this._buf.length - pos > CHUNK_BYTES) {
|
|
63
|
+
this.push(this._enc.sealChunk(this._buf.subarray(pos, pos + CHUNK_BYTES)));
|
|
64
|
+
pos += CHUNK_BYTES;
|
|
67
65
|
}
|
|
66
|
+
this._buf = this._buf.subarray(pos);
|
|
68
67
|
} catch (e) {
|
|
69
68
|
err = e;
|
|
70
69
|
}
|
|
@@ -79,7 +78,7 @@ class SecretStreamEncryptor extends Transform {
|
|
|
79
78
|
_flush(callback) {
|
|
80
79
|
let err;
|
|
81
80
|
try {
|
|
82
|
-
this.
|
|
81
|
+
this.push(this._enc.sealFinal(this._buf));
|
|
83
82
|
this._buf = Buffer.alloc(0);
|
|
84
83
|
} catch (e) {
|
|
85
84
|
err = e;
|
|
@@ -94,29 +93,42 @@ class SecretStreamEncryptor extends Transform {
|
|
|
94
93
|
* failure or truncation - a dropped/tampered/reordered chunk, or a stream that ends before a
|
|
95
94
|
* Final chunk, both fail closed rather than yielding wrong plaintext.
|
|
96
95
|
*
|
|
97
|
-
* D-118 pitfall check #2:
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* after the Final chunk are rejected too (`CliError::SecretstreamTrailingData`'s equivalent),
|
|
102
|
-
* checked both as more input arrives (`_transform`) and at end-of-stream (`_flush`) so trailing
|
|
103
|
-
* data appended in the same write as the Final chunk, or arriving in a later write, is caught
|
|
104
|
-
* either way - not just the case naive testing would think to check first.
|
|
96
|
+
* D-118 pitfall check #2: the untrusted length field, the 8 KiB rule, trailing data after Final
|
|
97
|
+
* and truncation are all the codec's checks (T-284); this class only collects `nextLen()` bytes
|
|
98
|
+
* at a time. A Final chunk's plaintext is pushed only in `_flush`, once the input is known to end
|
|
99
|
+
* right after it.
|
|
105
100
|
*/
|
|
106
101
|
class SecretStreamDecryptor extends Transform {
|
|
107
102
|
constructor(key, options) {
|
|
108
103
|
super(options);
|
|
109
|
-
this.
|
|
110
|
-
this._pullState = null;
|
|
104
|
+
this._dec = this._startCodec(key);
|
|
111
105
|
this._buf = Buffer.alloc(0);
|
|
112
|
-
|
|
106
|
+
}
|
|
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);
|
|
113
111
|
}
|
|
114
112
|
|
|
115
113
|
_transform(chunk, _encoding, callback) {
|
|
116
114
|
this._buf = Buffer.concat([this._buf, chunk]);
|
|
117
115
|
let err;
|
|
118
116
|
try {
|
|
119
|
-
this.
|
|
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
|
+
}
|
|
125
|
+
let pos = 0;
|
|
126
|
+
for (let want = this._dec.nextLen(); this._buf.length - pos >= want; want = this._dec.nextLen()) {
|
|
127
|
+
const plaintext = this._dec.feed(this._buf.subarray(pos, pos + want));
|
|
128
|
+
pos += want;
|
|
129
|
+
if (plaintext.length > 0) this.push(plaintext);
|
|
130
|
+
}
|
|
131
|
+
this._buf = this._buf.subarray(pos);
|
|
120
132
|
} catch (e) {
|
|
121
133
|
err = e;
|
|
122
134
|
}
|
|
@@ -124,57 +136,12 @@ class SecretStreamDecryptor extends Transform {
|
|
|
124
136
|
process.nextTick(callback, err);
|
|
125
137
|
}
|
|
126
138
|
|
|
127
|
-
_drain() {
|
|
128
|
-
if (!this._pullState) {
|
|
129
|
-
if (this._buf.length >= 1 && this._buf[0] !== FORMAT_VERSION) {
|
|
130
|
-
throw new Error(`unsupported stream format version ${this._buf[0]} (this build reads ${FORMAT_VERSION})`);
|
|
131
|
-
}
|
|
132
|
-
if (this._buf.length < 33) return;
|
|
133
|
-
const header = this._buf.subarray(1, 33);
|
|
134
|
-
this._buf = this._buf.subarray(33);
|
|
135
|
-
this._pullState = new native.SecretStreamPullState(this._key, header);
|
|
136
|
-
}
|
|
137
|
-
for (;;) {
|
|
138
|
-
if (this._done) {
|
|
139
|
-
if (this._buf.length > 0) {
|
|
140
|
-
throw new Error('trailing data after the secretstream Final chunk');
|
|
141
|
-
}
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
if (this._buf.length < 5) return;
|
|
145
|
-
const tagByte = this._buf[0];
|
|
146
|
-
const chunkLen = this._buf.readUInt32LE(1);
|
|
147
|
-
if (chunkLen > CHUNK_BYTES) {
|
|
148
|
-
throw new Error(`secretstream chunk too large: declared ${chunkLen} bytes, max ${CHUNK_BYTES}`);
|
|
149
|
-
}
|
|
150
|
-
if (tagByte !== native.SECRETSTREAM_TAG_FINAL && chunkLen !== CHUNK_BYTES) {
|
|
151
|
-
throw new Error(`non-final chunk of ${chunkLen} bytes, must be exactly ${CHUNK_BYTES} (D-208)`);
|
|
152
|
-
}
|
|
153
|
-
const recordLen = 5 + chunkLen + AUTH_TAG_BYTES;
|
|
154
|
-
if (this._buf.length < recordLen) return;
|
|
155
|
-
const ciphertext = this._buf.subarray(5, 5 + chunkLen);
|
|
156
|
-
const authTag = this._buf.subarray(5 + chunkLen, recordLen);
|
|
157
|
-
this._buf = this._buf.subarray(recordLen);
|
|
158
|
-
const { tag, plaintext } = this._pullState.pull(tagByte, ciphertext, authTag);
|
|
159
|
-
this.push(plaintext);
|
|
160
|
-
if (tag === native.SECRETSTREAM_TAG_FINAL) {
|
|
161
|
-
this._done = true;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
139
|
_flush(callback) {
|
|
167
140
|
let err;
|
|
168
141
|
try {
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (!this._done) {
|
|
173
|
-
throw new Error('truncated secretstream: stream ended before a Final chunk');
|
|
174
|
-
}
|
|
175
|
-
if (this._buf.length > 0) {
|
|
176
|
-
throw new Error('trailing data after the secretstream Final chunk');
|
|
177
|
-
}
|
|
142
|
+
if (this._dec === null) throw new Error(`input too short to contain a ${this._prefixName}`);
|
|
143
|
+
const plaintext = this._dec.finish();
|
|
144
|
+
if (plaintext.length > 0) this.push(plaintext);
|
|
178
145
|
} catch (e) {
|
|
179
146
|
err = e;
|
|
180
147
|
}
|
|
@@ -182,4 +149,130 @@ class SecretStreamDecryptor extends Transform {
|
|
|
182
149
|
}
|
|
183
150
|
}
|
|
184
151
|
|
|
185
|
-
|
|
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
|
}
|