dstu_core 0.3.0-x86_64-linux → 0.4.0-x86_64-linux
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.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3324edace58d86e8f8ae5b4f139e7b9682b9a931b5fb775b0273791e4cc97980
|
|
4
|
+
data.tar.gz: 6961e94c1ec3c39977b3df52ec91e83f71a4e111489ff577606c62d5cb58fab2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dbb410375a407cd9954d796cd1d6f2eded7343b0e39dbabd564b3e875088efdb12c6ab65289e5348c143c04ff428d3d80718f93dc035b5876d2ddd095cf90db4
|
|
7
|
+
data.tar.gz: 4f1dffd6bad51d324401665fd91d3e8f3bfaea6ff64a3ad8409382a0c4f10fc1c15c019037a814c4d0de00adfbddaf06c2e3634e763c7a657c7816c4f6afa356
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# `crypto_secretstream` idiomatic wrapper (docs/DECISIONS.md D-118, docs/bindings-strategy.md
|
|
4
|
-
# T-160 step 3) - hides chunk/tag/header bookkeeping behind `write`/`each
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# T-160 step 3) - hides chunk/tag/header bookkeeping behind `write`/`each`. The framing itself
|
|
5
|
+
# (record layout, length rules, Final, trailing data) is dstu-core's stream-file codec, exposed as
|
|
6
|
+
# SecretStreamFileEncryptor/SecretStreamFileDecryptor (T-284); this file does only I/O. Modeled on
|
|
7
|
+
# stdlib's own
|
|
7
8
|
# Zlib::GzipWriter/Zlib::GzipReader - the same "wraps an IO, transforms chunks transparently"
|
|
8
9
|
# shape, the closest Ruby-native precedent for this problem (matching Python's file-like object,
|
|
9
10
|
# Node's stream.Transform).
|
|
@@ -23,8 +24,8 @@
|
|
|
23
24
|
# differing encodings and fails even when the bytes match (`"привіт".b == "привіт"` is `false` in
|
|
24
25
|
# Ruby) - this is a caller-side encoding-compare gotcha, not a bug in this wrapper.
|
|
25
26
|
module DstuCore
|
|
26
|
-
#
|
|
27
|
-
#
|
|
27
|
+
# dstu-core's crypto_secretstream::CHUNK_BYTES and wire constants (D-208); the codec enforces
|
|
28
|
+
# them, these stay as public constants.
|
|
28
29
|
SECRETSTREAM_CHUNK_BYTES = 8 * 1024
|
|
29
30
|
SECRETSTREAM_AUTH_TAG_BYTES = 16
|
|
30
31
|
# Matches dstu-core's crypto_secretstream::FORMAT_VERSION (D-208); the shared vectors pin it.
|
|
@@ -40,9 +41,8 @@ module DstuCore
|
|
|
40
41
|
def initialize(key, out)
|
|
41
42
|
@out = out
|
|
42
43
|
@out.binmode if @out.respond_to?(:binmode)
|
|
43
|
-
@
|
|
44
|
-
@out.write(
|
|
45
|
-
@out.write(@push.header)
|
|
44
|
+
@enc = SecretStreamFileEncryptor.new(key)
|
|
45
|
+
@out.write(@enc.prefix)
|
|
46
46
|
@buf = +""
|
|
47
47
|
@closed = false
|
|
48
48
|
end
|
|
@@ -65,18 +65,21 @@ module DstuCore
|
|
|
65
65
|
@closed
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
# Buffers `data`,
|
|
69
|
-
# exactly-8-KiB) chunk is
|
|
70
|
-
# data is coming -
|
|
71
|
-
#
|
|
68
|
+
# Buffers `data`, sealing every full 8 KiB chunk that is certain not to be the last one. The
|
|
69
|
+
# trailing partial (or exactly-8-KiB) chunk is held back until `close`, since only `close`
|
|
70
|
+
# knows no more data is coming - an exact multiple of 8 KiB must end in a full Final record
|
|
71
|
+
# (D-208). Full chunks are cut from `data` by offset, so one large write costs linear time
|
|
72
|
+
# (T-276).
|
|
72
73
|
def write(data)
|
|
73
74
|
raise IOError, "closed stream" if @closed
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@
|
|
76
|
+
data = data.b
|
|
77
|
+
pos = top_up_buffer(data)
|
|
78
|
+
while data.bytesize - pos > SECRETSTREAM_CHUNK_BYTES
|
|
79
|
+
@out.write(@enc.seal_chunk(data.byteslice(pos, SECRETSTREAM_CHUNK_BYTES)))
|
|
80
|
+
pos += SECRETSTREAM_CHUNK_BYTES
|
|
79
81
|
end
|
|
82
|
+
@buf << data.byteslice(pos..)
|
|
80
83
|
data.bytesize
|
|
81
84
|
end
|
|
82
85
|
|
|
@@ -87,19 +90,27 @@ module DstuCore
|
|
|
87
90
|
def close
|
|
88
91
|
return if @closed
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
@out.write(@enc.seal_final(@buf))
|
|
91
94
|
@buf = +""
|
|
92
95
|
@closed = true
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
private
|
|
96
99
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
@
|
|
101
|
-
|
|
102
|
-
@
|
|
100
|
+
# Adds `data` to the held-back chunk if it all fits; otherwise tops that chunk up to a full one
|
|
101
|
+
# and seals it, since more data follows. Returns how many bytes of `data` it consumed.
|
|
102
|
+
def top_up_buffer(data)
|
|
103
|
+
return 0 if @buf.empty?
|
|
104
|
+
|
|
105
|
+
if @buf.bytesize + data.bytesize <= SECRETSTREAM_CHUNK_BYTES
|
|
106
|
+
@buf << data
|
|
107
|
+
return data.bytesize
|
|
108
|
+
end
|
|
109
|
+
taken = SECRETSTREAM_CHUNK_BYTES - @buf.bytesize
|
|
110
|
+
@buf << data.byteslice(0, taken)
|
|
111
|
+
@out.write(@enc.seal_chunk(@buf))
|
|
112
|
+
@buf = +""
|
|
113
|
+
taken
|
|
103
114
|
end
|
|
104
115
|
end
|
|
105
116
|
|
|
@@ -119,14 +130,10 @@ module DstuCore
|
|
|
119
130
|
def initialize(key, inp)
|
|
120
131
|
@inp = inp
|
|
121
132
|
@inp.binmode if @inp.respond_to?(:binmode)
|
|
122
|
-
|
|
123
|
-
if version != SECRETSTREAM_FORMAT_VERSION
|
|
124
|
-
raise DstuCore::Error,
|
|
125
|
-
"unsupported stream format version #{version} (this build reads #{SECRETSTREAM_FORMAT_VERSION})"
|
|
126
|
-
end
|
|
127
|
-
header = read_exact(32, "header")
|
|
128
|
-
@pull = SecretStreamPullState.new(key, header)
|
|
133
|
+
@dec = SecretStreamFileDecryptor.new(key)
|
|
129
134
|
@done = false
|
|
135
|
+
# The version byte and header, read now so a wrong version fails at construction.
|
|
136
|
+
2.times { step }
|
|
130
137
|
end
|
|
131
138
|
|
|
132
139
|
def self.open(key, inp)
|
|
@@ -137,7 +144,10 @@ module DstuCore
|
|
|
137
144
|
def each
|
|
138
145
|
return enum_for(:each) unless block_given?
|
|
139
146
|
|
|
140
|
-
|
|
147
|
+
until @done
|
|
148
|
+
plaintext = step
|
|
149
|
+
yield plaintext unless plaintext.empty?
|
|
150
|
+
end
|
|
141
151
|
end
|
|
142
152
|
|
|
143
153
|
def read_all
|
|
@@ -149,38 +159,20 @@ module DstuCore
|
|
|
149
159
|
|
|
150
160
|
private
|
|
151
161
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
# gigabytes before ever failing). Matches `uacrypt decrypt`'s own
|
|
159
|
-
# `CliError::SecretstreamChunkTooLarge` bound (`crates/uacrypt/src/lib.rs`).
|
|
160
|
-
if chunk_len > SECRETSTREAM_CHUNK_BYTES
|
|
161
|
-
raise DstuCore::Error,
|
|
162
|
-
"secretstream chunk too large: declared #{chunk_len} bytes, max #{SECRETSTREAM_CHUNK_BYTES}"
|
|
163
|
-
end
|
|
164
|
-
if tag_byte != SECRETSTREAM_TAG_FINAL && chunk_len != SECRETSTREAM_CHUNK_BYTES
|
|
165
|
-
raise DstuCore::Error,
|
|
166
|
-
"non-final chunk of #{chunk_len} bytes, must be exactly #{SECRETSTREAM_CHUNK_BYTES} (D-208)"
|
|
167
|
-
end
|
|
168
|
-
ciphertext = read_exact(chunk_len, "chunk ciphertext")
|
|
169
|
-
auth_tag = read_exact(SECRETSTREAM_AUTH_TAG_BYTES, "chunk auth tag")
|
|
170
|
-
tag, plaintext = @pull.pull(tag_byte, ciphertext, auth_tag)
|
|
171
|
-
if tag == SECRETSTREAM_TAG_FINAL
|
|
172
|
-
# Matches `uacrypt decrypt`'s own `CliError::SecretstreamTrailingData` check - reject
|
|
173
|
-
# bytes remaining after `Final` rather than silently ignoring them. Checked before
|
|
174
|
-
# `plaintext` is returned, so a trailing-data stream never yields this last chunk either
|
|
175
|
-
# via `each` or `read_all`.
|
|
176
|
-
raise DstuCore::Error, "trailing data after the secretstream's Final chunk" if @inp.read(1)
|
|
177
|
-
|
|
162
|
+
# Feeds the codec one `next_len` read: the plaintext that read released (possibly empty), or
|
|
163
|
+
# at end of input the Final chunk's (after which the stream is done).
|
|
164
|
+
def step
|
|
165
|
+
want = @dec.next_len
|
|
166
|
+
data = read_up_to(want)
|
|
167
|
+
if data.bytesize < want
|
|
178
168
|
@done = true
|
|
169
|
+
return @dec.finish
|
|
179
170
|
end
|
|
180
|
-
|
|
171
|
+
@dec.feed(data)
|
|
181
172
|
end
|
|
182
173
|
|
|
183
|
-
|
|
174
|
+
# Reads until `size` bytes arrived or `inp` ended; a short result means end of input.
|
|
175
|
+
def read_up_to(size)
|
|
184
176
|
pieces = []
|
|
185
177
|
remaining = size
|
|
186
178
|
while remaining.positive?
|
|
@@ -190,12 +182,7 @@ module DstuCore
|
|
|
190
182
|
pieces << piece
|
|
191
183
|
remaining -= piece.bytesize
|
|
192
184
|
end
|
|
193
|
-
|
|
194
|
-
if data.bytesize != size
|
|
195
|
-
raise DstuCore::Error, "truncated secretstream: expected #{size} bytes for #{what}, got #{data.bytesize}"
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
data
|
|
185
|
+
pieces.join.b
|
|
199
186
|
end
|
|
200
187
|
end
|
|
201
188
|
end
|
data/lib/dstu_core/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dstu_core
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: x86_64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- dstu-core contributors
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Ruby bindings for dstu-core (Ukrainian DSTU cryptographic standards)
|
|
14
14
|
- not independently audited.
|