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.
- neva256_native-0.1.0/Cargo.lock +180 -0
- neva256_native-0.1.0/Cargo.toml +20 -0
- neva256_native-0.1.0/PKG-INFO +65 -0
- neva256_native-0.1.0/README.md +49 -0
- neva256_native-0.1.0/pyproject.toml +27 -0
- neva256_native-0.1.0/src/lib.rs +280 -0
|
@@ -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
|
+
}
|