neva256-native 0.1.0__tar.gz

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.
@@ -0,0 +1,180 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.5.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
10
+
11
+ [[package]]
12
+ name = "cfg-if"
13
+ version = "1.0.5"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
16
+
17
+ [[package]]
18
+ name = "heck"
19
+ version = "0.5.0"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
22
+
23
+ [[package]]
24
+ name = "indoc"
25
+ version = "2.0.8"
26
+ source = "registry+https://github.com/rust-lang/crates.io-index"
27
+ checksum = "a37b2691796cffeb8a8cd305ac66e65841559f147f4e63231d0eafa4db5384d1"
28
+ dependencies = [
29
+ "rustversion",
30
+ ]
31
+
32
+ [[package]]
33
+ name = "libc"
34
+ version = "0.2.191"
35
+ source = "registry+https://github.com/rust-lang/crates.io-index"
36
+ checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
37
+
38
+ [[package]]
39
+ name = "memoffset"
40
+ version = "0.9.1"
41
+ source = "registry+https://github.com/rust-lang/crates.io-index"
42
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
43
+ dependencies = [
44
+ "autocfg",
45
+ ]
46
+
47
+ [[package]]
48
+ name = "neva256-native"
49
+ version = "0.1.0"
50
+ dependencies = [
51
+ "pyo3",
52
+ ]
53
+
54
+ [[package]]
55
+ name = "once_cell"
56
+ version = "1.21.4"
57
+ source = "registry+https://github.com/rust-lang/crates.io-index"
58
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
59
+
60
+ [[package]]
61
+ name = "portable-atomic"
62
+ version = "1.15.0"
63
+ source = "registry+https://github.com/rust-lang/crates.io-index"
64
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
65
+
66
+ [[package]]
67
+ name = "proc-macro2"
68
+ version = "1.0.107"
69
+ source = "registry+https://github.com/rust-lang/crates.io-index"
70
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
71
+ dependencies = [
72
+ "unicode-ident",
73
+ ]
74
+
75
+ [[package]]
76
+ name = "pyo3"
77
+ version = "0.23.5"
78
+ source = "registry+https://github.com/rust-lang/crates.io-index"
79
+ checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
80
+ dependencies = [
81
+ "cfg-if",
82
+ "indoc",
83
+ "libc",
84
+ "memoffset",
85
+ "once_cell",
86
+ "portable-atomic",
87
+ "pyo3-build-config",
88
+ "pyo3-ffi",
89
+ "pyo3-macros",
90
+ "unindent",
91
+ ]
92
+
93
+ [[package]]
94
+ name = "pyo3-build-config"
95
+ version = "0.23.5"
96
+ source = "registry+https://github.com/rust-lang/crates.io-index"
97
+ checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
98
+ dependencies = [
99
+ "once_cell",
100
+ "target-lexicon",
101
+ ]
102
+
103
+ [[package]]
104
+ name = "pyo3-ffi"
105
+ version = "0.23.5"
106
+ source = "registry+https://github.com/rust-lang/crates.io-index"
107
+ checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
108
+ dependencies = [
109
+ "libc",
110
+ "pyo3-build-config",
111
+ ]
112
+
113
+ [[package]]
114
+ name = "pyo3-macros"
115
+ version = "0.23.5"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
118
+ dependencies = [
119
+ "proc-macro2",
120
+ "pyo3-macros-backend",
121
+ "quote",
122
+ "syn",
123
+ ]
124
+
125
+ [[package]]
126
+ name = "pyo3-macros-backend"
127
+ version = "0.23.5"
128
+ source = "registry+https://github.com/rust-lang/crates.io-index"
129
+ checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
130
+ dependencies = [
131
+ "heck",
132
+ "proc-macro2",
133
+ "pyo3-build-config",
134
+ "quote",
135
+ "syn",
136
+ ]
137
+
138
+ [[package]]
139
+ name = "quote"
140
+ version = "1.0.47"
141
+ source = "registry+https://github.com/rust-lang/crates.io-index"
142
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
143
+ dependencies = [
144
+ "proc-macro2",
145
+ ]
146
+
147
+ [[package]]
148
+ name = "rustversion"
149
+ version = "1.0.23"
150
+ source = "registry+https://github.com/rust-lang/crates.io-index"
151
+ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
152
+
153
+ [[package]]
154
+ name = "syn"
155
+ version = "2.0.119"
156
+ source = "registry+https://github.com/rust-lang/crates.io-index"
157
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
158
+ dependencies = [
159
+ "proc-macro2",
160
+ "quote",
161
+ "unicode-ident",
162
+ ]
163
+
164
+ [[package]]
165
+ name = "target-lexicon"
166
+ version = "0.12.16"
167
+ source = "registry+https://github.com/rust-lang/crates.io-index"
168
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
169
+
170
+ [[package]]
171
+ name = "unicode-ident"
172
+ version = "1.0.27"
173
+ source = "registry+https://github.com/rust-lang/crates.io-index"
174
+ checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
175
+
176
+ [[package]]
177
+ name = "unindent"
178
+ version = "0.2.4"
179
+ source = "registry+https://github.com/rust-lang/crates.io-index"
180
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
@@ -0,0 +1,20 @@
1
+ [package]
2
+ name = "neva256-native"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "NEVA-256 native accelerator (Rust/PyO3) for the neva256 Python package"
6
+ license = "MIT"
7
+ readme = "README.md"
8
+
9
+ [lib]
10
+ name = "neva256_native"
11
+ crate-type = ["cdylib", "rlib"]
12
+
13
+ [dependencies]
14
+ pyo3 = { version = "0.23", features = ["extension-module"] }
15
+
16
+ [profile.release]
17
+ opt-level = 3
18
+ lto = true
19
+ codegen-units = 1
20
+ panic = "abort"
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: neva256-native
3
+ Version: 0.1.0
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Programming Language :: Rust
6
+ Classifier: Programming Language :: Python :: Implementation :: CPython
7
+ Classifier: Topic :: Security :: Cryptography
8
+ Summary: NEVA-256 native accelerator (Rust/PyO3) — experimental cipher, NOT for production secrets
9
+ Keywords: cipher,experimental,native,rust,pyo3
10
+ Author: EWX-AI
11
+ License-Expression: MIT
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
14
+ Project-URL: Homepage, https://github.com/ewxai/ewxai
15
+
16
+ # neva256-native — NEVA-256 Rust accelerator
17
+
18
+ Native (Rust/PyO3) backend for the [`neva256`](https://pypi.org/project/neva256/)
19
+ Python package. Bit-exact with the TypeScript and pure-Python implementations,
20
+ roughly 1000× faster than pure Python, with the GIL released during compute.
21
+
22
+ > ⚠️ EKSPERIMENTAL — NEVA belum pernah dianalisis kriptografer. Jangan pakai
23
+ > untuk data rahasia sungguhan; gunakan AES-256-GCM/libsodium.
24
+
25
+ ## Instalasi
26
+
27
+ Biasanya tidak perlu dipasang manual — cukup:
28
+
29
+ ```bash
30
+ pip install neva256[fast]
31
+ ```
32
+
33
+ atau:
34
+
35
+ ```bash
36
+ pip install neva256-native
37
+ ```
38
+
39
+ Paket Python `neva256` otomatis memakai backend native ini bila tersedia dan
40
+ jatuh kembali ke pure Python kalau tidak.
41
+
42
+ ## Pemakaian langsung
43
+
44
+ ```python
45
+ from neva256_native import NativeCipher
46
+
47
+ c = NativeCipher(b"\x00" * 32, b"\x01" * 16)
48
+ blob = c.encrypt_bytes(b"halo")
49
+ assert c.decrypt_bytes(blob, True) == b"halo"
50
+ ```
51
+
52
+ ## Build dari sumber
53
+
54
+ Butuh Rust toolchain:
55
+
56
+ ```bash
57
+ pip install maturin
58
+ maturin build --release # wheel untuk platform ini
59
+ maturin develop --release # install ke venv aktif
60
+ ```
61
+
62
+ ## Test kompatibilitas
63
+
64
+ Test parity (native == pure Python) ada di `../tests/test_neva.py`.
65
+
@@ -0,0 +1,49 @@
1
+ # neva256-native — NEVA-256 Rust accelerator
2
+
3
+ Native (Rust/PyO3) backend for the [`neva256`](https://pypi.org/project/neva256/)
4
+ Python package. Bit-exact with the TypeScript and pure-Python implementations,
5
+ roughly 1000× faster than pure Python, with the GIL released during compute.
6
+
7
+ > ⚠️ EKSPERIMENTAL — NEVA belum pernah dianalisis kriptografer. Jangan pakai
8
+ > untuk data rahasia sungguhan; gunakan AES-256-GCM/libsodium.
9
+
10
+ ## Instalasi
11
+
12
+ Biasanya tidak perlu dipasang manual — cukup:
13
+
14
+ ```bash
15
+ pip install neva256[fast]
16
+ ```
17
+
18
+ atau:
19
+
20
+ ```bash
21
+ pip install neva256-native
22
+ ```
23
+
24
+ Paket Python `neva256` otomatis memakai backend native ini bila tersedia dan
25
+ jatuh kembali ke pure Python kalau tidak.
26
+
27
+ ## Pemakaian langsung
28
+
29
+ ```python
30
+ from neva256_native import NativeCipher
31
+
32
+ c = NativeCipher(b"\x00" * 32, b"\x01" * 16)
33
+ blob = c.encrypt_bytes(b"halo")
34
+ assert c.decrypt_bytes(blob, True) == b"halo"
35
+ ```
36
+
37
+ ## Build dari sumber
38
+
39
+ Butuh Rust toolchain:
40
+
41
+ ```bash
42
+ pip install maturin
43
+ maturin build --release # wheel untuk platform ini
44
+ maturin develop --release # install ke venv aktif
45
+ ```
46
+
47
+ ## Test kompatibilitas
48
+
49
+ Test parity (native == pure Python) ada di `../tests/test_neva.py`.
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.5,<2.0"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "neva256-native"
7
+ version = "0.1.0"
8
+ description = "NEVA-256 native accelerator (Rust/PyO3) — experimental cipher, NOT for production secrets"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ authors = [{ name = "EWX-AI" }]
13
+ keywords = ["cipher", "experimental", "native", "rust", "pyo3"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Programming Language :: Rust",
17
+ "Programming Language :: Python :: Implementation :: CPython",
18
+ "Topic :: Security :: Cryptography",
19
+ ]
20
+ dependencies = []
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/ewxai/ewxai"
24
+
25
+ [tool.maturin]
26
+ module-name = "neva256_native"
27
+ features = ["pyo3/extension-module"]
@@ -0,0 +1,280 @@
1
+ //! NEVA-256 native accelerator (Rust/PyO3).
2
+ //!
3
+ //! Bit-exact port of the `neva256` Python package / the TypeScript
4
+ //! implementation in `src/neva/neva.ts`. Provides the same results, ~1000x
5
+ //! faster than the pure-Python fallback.
6
+ //!
7
+ //! EXPERIMENTAL — not for production secrets.
8
+
9
+ use pyo3::create_exception;
10
+ use pyo3::exceptions::{PyException, PyValueError};
11
+ use pyo3::prelude::*;
12
+ use pyo3::types::PyBytes;
13
+
14
+ const KEY_BYTES: usize = 32;
15
+ const NONCE_BYTES: usize = 16;
16
+ const TAG_BYTES: usize = 16;
17
+
18
+ const GOLDEN: u32 = 0x9e3779b9;
19
+ const SEED_SECOND: u32 = 0x85ebca6b;
20
+
21
+ const SALT_VORTEX: u32 = 0x1;
22
+ const SALT_COMET: u32 = 0x7e3;
23
+ const SALT_TAG: u32 = 0xc0ffee;
24
+
25
+ pub const VERSION: &str = "NEVA-256-native/0.1.0";
26
+
27
+ create_exception!(neva256_native, NativeNevaError, PyException);
28
+
29
+ #[inline(always)]
30
+ fn swirl(w: &mut [u32; 8], rnd: u32) {
31
+ for i in 0..8u32 {
32
+ let a = w[i as usize];
33
+ let b = w[((i + 1) & 7) as usize];
34
+ let c = w[((i + 3) & 7) as usize];
35
+ let r = 1 + ((a >> ((rnd + i) & 7)) % 31);
36
+ w[i as usize] = (a ^ b)
37
+ .rotate_left(r & 31)
38
+ .wrapping_add(c)
39
+ .wrapping_add((rnd + i).wrapping_mul(GOLDEN));
40
+ }
41
+ }
42
+
43
+ fn seed_words(salt: u32) -> [u32; 8] {
44
+ [
45
+ 0x6e657661 ^ salt,
46
+ 0x2d766f72 ^ salt.wrapping_mul(GOLDEN),
47
+ 0x74657820,
48
+ 0x636f7265,
49
+ 0x0e3d25c1 ^ salt,
50
+ 0x656c6961,
51
+ 0x24bd08e5 ^ salt.wrapping_mul(0x27d4eb2f),
52
+ 0x1a2b3c4d,
53
+ ]
54
+ }
55
+
56
+ fn absorb_bytes(v: &mut [u32; 8], data: &[u8]) {
57
+ let mut acc: u32 = 0;
58
+ let mut count: u32 = 0;
59
+ for &byte in data {
60
+ acc |= (byte as u32) << (8 * (count & 3));
61
+ count += 1;
62
+ if count & 3 == 0 {
63
+ let lane = ((count >> 2) & 7) as usize;
64
+ v[lane] ^= acc ^ count.wrapping_mul(GOLDEN);
65
+ swirl(v, lane as u32);
66
+ acc = 0;
67
+ }
68
+ }
69
+ if count & 3 != 0 {
70
+ let lane = (count & 7) as usize;
71
+ v[lane] ^= acc ^ (count + 1).wrapping_mul(GOLDEN);
72
+ swirl(v, lane as u32);
73
+ }
74
+ }
75
+
76
+ fn schedule(key: &[u8], nonce: &[u8], salt: u32) -> [u32; 8] {
77
+ let mut v = seed_words(salt);
78
+ swirl(&mut v, salt & 7);
79
+ v[0] ^= (salt + 1).wrapping_mul(GOLDEN);
80
+ absorb_bytes(&mut v, key);
81
+ swirl(&mut v, 1);
82
+ swirl(&mut v, 2);
83
+ absorb_bytes(&mut v, nonce);
84
+ for i in 0..8u32 {
85
+ swirl(&mut v, 3 + (i & 7));
86
+ }
87
+ v
88
+ }
89
+
90
+ #[inline(always)]
91
+ fn stream_byte(core: &[u32; 8], reg: u32, pos: u32) -> u8 {
92
+ let mut w = *core;
93
+ w[0] ^= reg ^ (pos + 1).wrapping_mul(GOLDEN);
94
+ w[1] ^= (reg >> 16) ^ (pos + 1).wrapping_mul(SEED_SECOND);
95
+ w[2] = w[2].wrapping_add(pos);
96
+ w[7] ^= reg ^ (reg << 13);
97
+ swirl(&mut w, 0);
98
+ swirl(&mut w, 3);
99
+ swirl(&mut w, 6);
100
+ swirl(&mut w, 9);
101
+ ((w[((pos * 5) & 7) as usize] >> ((pos & 3) * 8)) & 0xff) as u8
102
+ }
103
+
104
+ fn vortex_layer(data: &[u8], core: &[u32; 8], decoding: bool) -> Vec<u8> {
105
+ let mut out = vec![0u8; data.len()];
106
+ let mut reg: u32 = 0;
107
+ for i in 0..data.len() {
108
+ let sb = stream_byte(core, reg, i as u32);
109
+ let c = data[i] ^ sb;
110
+ out[i] = c;
111
+ let feed = if decoding { data[i] } else { c };
112
+ reg = (reg >> 8) | ((feed as u32) << 24);
113
+ }
114
+ out
115
+ }
116
+
117
+ fn make_permutation(n: usize, seeds: &[u32; 4]) -> Vec<u32> {
118
+ let mut rng = *seeds;
119
+ if (rng[0] | rng[1] | rng[2] | rng[3]) == 0 {
120
+ rng[0] = GOLDEN;
121
+ }
122
+ let mut p: Vec<u32> = (0..n as u32).collect();
123
+ for i in (1..n).rev() {
124
+ // xorshift128
125
+ let t = rng[3];
126
+ let w = rng[0];
127
+ rng[3] = rng[2];
128
+ rng[2] = rng[1];
129
+ rng[1] = w;
130
+ let mut t2 = t ^ (t << 11);
131
+ t2 ^= t2 >> 8;
132
+ rng[0] = t2 ^ w ^ (w >> 19);
133
+ let j = (rng[0] as usize) % (i + 1);
134
+ p.swap(i, j);
135
+ }
136
+ p
137
+ }
138
+
139
+ fn compute_tag(tag_state: &[u32; 8], data: &[u8], length: usize) -> [u8; TAG_BYTES] {
140
+ let mut v = *tag_state;
141
+ absorb_bytes(&mut v, data);
142
+ let len = length as u32;
143
+ let len_digest = [
144
+ (len & 0xff) as u8,
145
+ ((len >> 8) & 0xff) as u8,
146
+ ((len >> 16) & 0xff) as u8,
147
+ ((len >> 24) & 0xff) as u8,
148
+ 0x5a,
149
+ ];
150
+ absorb_bytes(&mut v, &len_digest);
151
+ for i in 0..8u32 {
152
+ swirl(&mut v, (i + 1) & 7);
153
+ }
154
+ let mut out = [0u8; TAG_BYTES];
155
+ for i in 0..4usize {
156
+ out[i * 4] = (v[i] & 0xff) as u8;
157
+ out[i * 4 + 1] = ((v[i] >> 8) & 0xff) as u8;
158
+ out[i * 4 + 2] = ((v[i] >> 16) & 0xff) as u8;
159
+ out[i * 4 + 3] = ((v[i] >> 24) & 0xff) as u8;
160
+ }
161
+ out
162
+ }
163
+
164
+ #[pyclass]
165
+ struct NativeCipher {
166
+ vortex: [u32; 8],
167
+ comet: [u32; 4],
168
+ tag: [u32; 8],
169
+ }
170
+
171
+ impl NativeCipher {
172
+ fn encrypt_impl(&self, data: &[u8]) -> Vec<u8> {
173
+ let n = data.len();
174
+ let masked = vortex_layer(data, &self.vortex, false);
175
+ let payload: Vec<u8> = if n > 1 {
176
+ let p = make_permutation(n, &self.comet);
177
+ let mut out = vec![0u8; n];
178
+ for i in 0..n {
179
+ out[i] = masked[p[i] as usize];
180
+ }
181
+ out
182
+ } else {
183
+ masked
184
+ };
185
+ let mut result = payload.clone();
186
+ result.extend_from_slice(&compute_tag(&self.tag, &payload, n));
187
+ result
188
+ }
189
+
190
+ fn decrypt_impl(&self, blob: &[u8], verify: bool) -> Result<Vec<u8>, String> {
191
+ if blob.len() < TAG_BYTES {
192
+ return Err("ciphertext too short: missing divergence tag".to_string());
193
+ }
194
+ let n = blob.len() - TAG_BYTES;
195
+ let payload = &blob[..n];
196
+ let tag = &blob[n..];
197
+ if verify {
198
+ let expected = compute_tag(&self.tag, payload, n);
199
+ if expected.as_slice() != tag {
200
+ return Err("integrity check failed: ciphertext was modified".to_string());
201
+ }
202
+ }
203
+ let masked: Vec<u8> = if n > 1 {
204
+ let p = make_permutation(n, &self.comet);
205
+ let mut out = vec![0u8; n];
206
+ for i in 0..n {
207
+ out[p[i] as usize] = payload[i];
208
+ }
209
+ out
210
+ } else {
211
+ payload.to_vec()
212
+ };
213
+ Ok(vortex_layer(&masked, &self.vortex, true))
214
+ }
215
+ }
216
+
217
+ #[pymethods]
218
+ impl NativeCipher {
219
+ #[new]
220
+ fn new(key: &[u8], nonce: &[u8]) -> PyResult<Self> {
221
+ if key.len() != KEY_BYTES {
222
+ return Err(PyValueError::new_err(format!(
223
+ "key must be exactly {} bytes (got {})",
224
+ KEY_BYTES,
225
+ key.len()
226
+ )));
227
+ }
228
+ if nonce.len() != NONCE_BYTES {
229
+ return Err(PyValueError::new_err(format!(
230
+ "nonce must be exactly {} bytes (got {})",
231
+ NONCE_BYTES,
232
+ nonce.len()
233
+ )));
234
+ }
235
+ let vortex = schedule(key, nonce, SALT_VORTEX);
236
+ let comet = schedule(key, nonce, SALT_COMET);
237
+ let tag = schedule(key, nonce, SALT_TAG);
238
+ Ok(Self {
239
+ vortex,
240
+ comet: [comet[0], comet[1], comet[2], comet[3]],
241
+ tag,
242
+ })
243
+ }
244
+
245
+ /// Encrypt bytes -> ciphertext || 16-byte tag (GIL released during compute).
246
+ fn encrypt_bytes<'py>(&self, py: Python<'py>, data: &[u8]) -> Bound<'py, PyBytes> {
247
+ let out = py.allow_threads(|| self.encrypt_impl(data));
248
+ PyBytes::new(py, &out)
249
+ }
250
+
251
+ /// Decrypt bytes produced by `encrypt_bytes`.
252
+ fn decrypt_bytes<'py>(
253
+ &self,
254
+ py: Python<'py>,
255
+ blob: &[u8],
256
+ verify: bool,
257
+ ) -> PyResult<Bound<'py, PyBytes>> {
258
+ match py.allow_threads(|| self.decrypt_impl(blob, verify)) {
259
+ Ok(out) => Ok(PyBytes::new(py, &out)),
260
+ Err(message) => Err(NativeNevaError::new_err(message)),
261
+ }
262
+ }
263
+ }
264
+
265
+ #[pyfunction]
266
+ fn version() -> &'static str {
267
+ VERSION
268
+ }
269
+
270
+ #[pymodule]
271
+ fn neva256_native(m: &Bound<'_, PyModule>) -> PyResult<()> {
272
+ m.add_class::<NativeCipher>()?;
273
+ m.add_function(wrap_pyfunction!(version, m)?)?;
274
+ m.add("NativeNevaError", m.py().get_type::<NativeNevaError>())?;
275
+ m.add("KEY_BYTES", KEY_BYTES)?;
276
+ m.add("NONCE_BYTES", NONCE_BYTES)?;
277
+ m.add("TAG_BYTES", TAG_BYTES)?;
278
+ m.add("__version__", env!("CARGO_PKG_VERSION"))?;
279
+ Ok(())
280
+ }