laicrypto 0.1.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.
- checksums.yaml +7 -0
- data/README.md +338 -0
- data/lib/laicrypto/version.rb +3 -0
- data/lib/laicrypto.rb +163 -0
- metadata +50 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 542a40d2e82129821225c13236509b3432870e6a32fd12b079800377c5a76a42
|
|
4
|
+
data.tar.gz: f0240e59391207efcad435482237b2af8be2acb8c5d07f35037838d8ad4c6545
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 55d0e02c98ad5824b0f2c89b808fcf5ed51476f6a058b891324773aa92ef2553da1ddd7536735e5fc872c7b4eae66026035681d3e11b7ac1bd81ff16eb1880a0
|
|
7
|
+
data.tar.gz: ad07b4cb86dcca560db066527eaef153a2de18ae67acd4c7709a69644db395b407c41d2a7bdd65d66247455b9a78d3181819c5535c9222ac8de62d1e2808f534
|
data/README.md
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# pqcrypto
|
|
2
|
+
|
|
3
|
+
<img src="https://github.com/4211421036/pqcrypto/blob/main/logo.png" />
|
|
4
|
+
|
|
5
|
+
**Post-Quantum Lemniscate-AGM Isogeny (LAI) Encryption**
|
|
6
|
+
|
|
7
|
+
A Python package providing a reference implementation of the Lemniscate-AGM Isogeny (LAI) encryption scheme. LAI is a promising post-quantum cryptosystem based on isogenies of elliptic curves over lemniscate lattices, offering resistance against quantum-capable adversaries.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Project Overview
|
|
12
|
+
|
|
13
|
+
This library implements the core mathematical primitives and high-level API of the LAI scheme, including:
|
|
14
|
+
|
|
15
|
+
* **Key Generation**: Derivation of a private scalar and corresponding public point via binary exponentiation of the LAI transformation.
|
|
16
|
+
* **Encryption**: Secure encryption of integer messages modulo a prime.
|
|
17
|
+
* **Decryption**: Accurate recovery of plaintext via inverse transform.
|
|
18
|
+
|
|
19
|
+
The code is annotated with direct correspondence to the mathematical definitions and pseudocode, making it suitable for research, educational use, and further development.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Mathematical Formulation
|
|
24
|
+
|
|
25
|
+
### 1. Hash-Based Seed Function
|
|
26
|
+
|
|
27
|
+
Define:
|
|
28
|
+
|
|
29
|
+
$$
|
|
30
|
+
H(x, y, s) \;=\; \mathrm{SHA256}\bigl(x \,\|\, y \,\|\, s\bigr) \bmod p
|
|
31
|
+
$$
|
|
32
|
+
|
|
33
|
+
where \$x,y,s \in \mathbb{Z}\_p\$ and \$|\$ denotes byte-string concatenation.
|
|
34
|
+
|
|
35
|
+
### 2. Modular Square Root (Tonelli–Shanks)
|
|
36
|
+
|
|
37
|
+
Compute \$z = \sqrt{a} \bmod p\$ for prime \$p\$:
|
|
38
|
+
|
|
39
|
+
* If \$p \equiv 3 \pmod{4}\$:
|
|
40
|
+
$z \;=\; a^{\frac{p+1}{4}} \bmod p$
|
|
41
|
+
* Otherwise, use the full Tonelli–Shanks algorithm for general primes.
|
|
42
|
+
|
|
43
|
+
### 3. LAI Transformation \$T\$
|
|
44
|
+
|
|
45
|
+
Given a point \$(x,y) \in \mathbb{F}\_p^2\$, parameter \$a\$, and seed index \$s\$, define:
|
|
46
|
+
|
|
47
|
+
$$
|
|
48
|
+
\begin{aligned}
|
|
49
|
+
h &= H(x,y,s), \[4pt]
|
|
50
|
+
x' &= \frac{x + a + h}{2} \bmod p, \[4pt]
|
|
51
|
+
y' &= \sqrt{x \, y + h} \bmod p.
|
|
52
|
+
\end{aligned}
|
|
53
|
+
$$
|
|
54
|
+
|
|
55
|
+
Thus,
|
|
56
|
+
|
|
57
|
+
$T\bigl((x,y), s; a, p\bigr) = (\,x', y').$
|
|
58
|
+
|
|
59
|
+
### 4. Binary Exponentiation of \$T\$
|
|
60
|
+
|
|
61
|
+
To compute \$T^k(P\_0)\$ efficiently, use exponentiation by squaring:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
function pow_T(P, k):
|
|
65
|
+
result ← P
|
|
66
|
+
base ← P
|
|
67
|
+
s ← 1
|
|
68
|
+
while k > 0:
|
|
69
|
+
if (k mod 2) == 1:
|
|
70
|
+
result ← T(result, s)
|
|
71
|
+
base ← T(base, s)
|
|
72
|
+
k ← k >> 1
|
|
73
|
+
s ← s + 1
|
|
74
|
+
return result
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 5. API Algorithms
|
|
78
|
+
|
|
79
|
+
**Key Generation**
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
function keygen(p, a, P0):
|
|
83
|
+
k ← random integer in [1, p−1]
|
|
84
|
+
Q ← pow_T(P0, k)
|
|
85
|
+
return (k, Q)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Encryption**
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
function encrypt(m, Q, p, a, P0):
|
|
92
|
+
r ← random integer in [1, p−1]
|
|
93
|
+
C1 ← pow_T(P0, r)
|
|
94
|
+
Sr ← pow_T(Q, r)
|
|
95
|
+
M ← (m mod p, 0)
|
|
96
|
+
C2 ← ( (M.x + Sr.x) mod p,
|
|
97
|
+
(M.y + Sr.y) mod p )
|
|
98
|
+
return (C1, C2)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Decryption**
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
function decrypt(C1, C2, k, a, p):
|
|
105
|
+
S ← pow_T(C1, k)
|
|
106
|
+
M.x ← (C2.x − S.x) mod p
|
|
107
|
+
return M.x
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Features
|
|
113
|
+
|
|
114
|
+
1. **Pure Python** implementation: no external dependencies for core routines (uses `hashlib` & `secrets`).
|
|
115
|
+
2. **Mathematically Annotated**: formulas and pseudocode directly reference the original scheme.
|
|
116
|
+
3. **Modular Design**: separation of primitives (`H`, `sqrt_mod`, `T`) and high-level API (`keygen`, `encrypt`, `decrypt`).
|
|
117
|
+
4. **General & Optimized**: Tonelli–Shanks for any prime, plus branch for \$p\equiv3\pmod4\$.
|
|
118
|
+
5. **Automated Testing**: `pytest` suite for end-to-end verification.
|
|
119
|
+
6. **CI/CD Ready**: PyPI publication via GitHub Actions.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Installation
|
|
124
|
+
|
|
125
|
+
### From PyPI
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install pqcrypto
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### From NPM
|
|
132
|
+
```bash
|
|
133
|
+
npm install pqlaicrypto
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### From Ruby GEMFILE
|
|
137
|
+
```bash
|
|
138
|
+
gem build laicrypto.gemspec
|
|
139
|
+
gem install ./laicrypto-0.1.0.gem
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### From Source
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
git clone https://github.com/4211421036/pqcrypto.git
|
|
146
|
+
cd pqcrypto
|
|
147
|
+
pip install .
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Usage Example
|
|
153
|
+
|
|
154
|
+
Python
|
|
155
|
+
```python
|
|
156
|
+
import math
|
|
157
|
+
|
|
158
|
+
from pqcrypto import keygen, encrypt, decrypt
|
|
159
|
+
|
|
160
|
+
p = 10007
|
|
161
|
+
a = 5
|
|
162
|
+
P0 = (1, 0)
|
|
163
|
+
|
|
164
|
+
def max_block_size(p: int) -> int:
|
|
165
|
+
bit_len = p.bit_length()
|
|
166
|
+
return (bit_len - 1) // 8
|
|
167
|
+
|
|
168
|
+
def text_to_int_blocks(text: str, p: int) -> list[int]:
|
|
169
|
+
raw_bytes = text.encode("utf-8")
|
|
170
|
+
B = max_block_size(p)
|
|
171
|
+
if B < 1:
|
|
172
|
+
raise ValueError("Prime p terlalu kecil untuk menyimpan satu byte pun.")
|
|
173
|
+
|
|
174
|
+
blocks = []
|
|
175
|
+
# Hitung jumlah blok
|
|
176
|
+
n_blocks = math.ceil(len(raw_bytes) / B)
|
|
177
|
+
for i in range(n_blocks):
|
|
178
|
+
start = i * B
|
|
179
|
+
end = start + B
|
|
180
|
+
chunk = raw_bytes[start:end]
|
|
181
|
+
m_int = int.from_bytes(chunk, byteorder="big")
|
|
182
|
+
if m_int >= p:
|
|
183
|
+
raise ValueError("Blok integer melebihi modulus p.")
|
|
184
|
+
blocks.append(m_int)
|
|
185
|
+
|
|
186
|
+
return blocks
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def int_blocks_to_text(blocks: list[int], p: int) -> str:
|
|
190
|
+
all_bytes = bytearray()
|
|
191
|
+
for m_int in blocks:
|
|
192
|
+
if not (0 <= m_int < p):
|
|
193
|
+
raise ValueError(f"Integer block {m_int} di luar range [0, p).")
|
|
194
|
+
if m_int == 0:
|
|
195
|
+
chunk_bytes = b"\x00"
|
|
196
|
+
else:
|
|
197
|
+
byte_len = math.ceil(m_int.bit_length() / 8)
|
|
198
|
+
chunk_bytes = m_int.to_bytes(byte_len, byteorder="big")
|
|
199
|
+
all_bytes.extend(chunk_bytes)
|
|
200
|
+
|
|
201
|
+
return all_bytes.decode("utf-8", errors="strict")
|
|
202
|
+
|
|
203
|
+
def encrypt_text(
|
|
204
|
+
text: str,
|
|
205
|
+
k: int,
|
|
206
|
+
public_Q: tuple[int, int],
|
|
207
|
+
p: int,
|
|
208
|
+
a: int,
|
|
209
|
+
P0: tuple[int, int],
|
|
210
|
+
) -> list[dict]:
|
|
211
|
+
int_blocks = text_to_int_blocks(text, p)
|
|
212
|
+
ciphertext = []
|
|
213
|
+
|
|
214
|
+
for m_int in int_blocks:
|
|
215
|
+
# encrypt() sudah otomatis retry jika T^r gagal
|
|
216
|
+
C1, C2, r = encrypt(m_int, public_Q, k, p, a, P0)
|
|
217
|
+
ciphertext.append({
|
|
218
|
+
"C1": (C1[0], C1[1]),
|
|
219
|
+
"C2": (C2[0], C2[1]),
|
|
220
|
+
"r": r,
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
return ciphertext
|
|
224
|
+
|
|
225
|
+
def decrypt_text(
|
|
226
|
+
ciphertext: list[dict],
|
|
227
|
+
k: int,
|
|
228
|
+
p: int,
|
|
229
|
+
a: int,
|
|
230
|
+
) -> str:
|
|
231
|
+
int_blocks = []
|
|
232
|
+
for block in ciphertext:
|
|
233
|
+
x1, y1 = block["C1"]
|
|
234
|
+
x2, y2 = block["C2"]
|
|
235
|
+
r = block["r"]
|
|
236
|
+
m_int = decrypt((x1, y1), (x2, y2), k, r, a, p)
|
|
237
|
+
int_blocks.append(m_int)
|
|
238
|
+
|
|
239
|
+
return int_blocks_to_text(int_blocks, p)
|
|
240
|
+
|
|
241
|
+
if __name__ == "__main__":
|
|
242
|
+
# 6.1. Generate keypair
|
|
243
|
+
private_k, public_Q = keygen(p, a, P0)
|
|
244
|
+
print("=== Key Generation ===")
|
|
245
|
+
print("Private k :", private_k)
|
|
246
|
+
print("Public Q :", public_Q)
|
|
247
|
+
print()
|
|
248
|
+
|
|
249
|
+
original_text = """
|
|
250
|
+
function hello(name) {
|
|
251
|
+
console.log("Hello, " + name + "!");
|
|
252
|
+
}
|
|
253
|
+
hello("LAI User");
|
|
254
|
+
""".strip()
|
|
255
|
+
|
|
256
|
+
print("=== Original Text ===")
|
|
257
|
+
print(original_text)
|
|
258
|
+
print()
|
|
259
|
+
|
|
260
|
+
ciphertext = encrypt_text(original_text, private_k, public_Q, p, a, P0)
|
|
261
|
+
print("=== Ciphertext (serialized) ===")
|
|
262
|
+
for i, blk in enumerate(ciphertext):
|
|
263
|
+
print(f"Block {i+1}: C1={blk['C1']}, C2={blk['C2']}, r={blk['r']}")
|
|
264
|
+
|
|
265
|
+
print()
|
|
266
|
+
recovered_text = decrypt_text(ciphertext, private_k, p, a)
|
|
267
|
+
print("=== Decrypted Text ===")
|
|
268
|
+
print(recovered_text)
|
|
269
|
+
print()
|
|
270
|
+
|
|
271
|
+
assert recovered_text == original_text, "Decryption mismatch!"
|
|
272
|
+
print("Round-trip successful! Teks tepat sama dengan semula.")
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
JS
|
|
277
|
+
```js
|
|
278
|
+
const {
|
|
279
|
+
keygen,
|
|
280
|
+
encrypt,
|
|
281
|
+
decrypt
|
|
282
|
+
} = require('pqlaicrypto');
|
|
283
|
+
|
|
284
|
+
const p = 23n;
|
|
285
|
+
const a = 5n;
|
|
286
|
+
const P0 = [3n, 10n];
|
|
287
|
+
|
|
288
|
+
const { k, Q } = keygen(p, a, P0);
|
|
289
|
+
|
|
290
|
+
const m = 7n;
|
|
291
|
+
const { C1, C2, r } = encrypt(m, Q, k, p, a, P0);
|
|
292
|
+
|
|
293
|
+
const decrypted = decrypt(C1, C2, k, r, a, p);
|
|
294
|
+
console.log('Pesan asli:', decrypted.toString());
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Ruby
|
|
298
|
+
|
|
299
|
+
```rb
|
|
300
|
+
irb
|
|
301
|
+
> require "laicrypto"
|
|
302
|
+
> laicrypto.keygen(23, 5, [3,10])
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## API Reference
|
|
308
|
+
|
|
309
|
+
| Function | Description |
|
|
310
|
+
| ------------------------------------ | --------------------------------------- |
|
|
311
|
+
| `H(x, y, s, p) -> int` | Hash-based seed modulo \$p\$. |
|
|
312
|
+
| `sqrt_mod(a, p) -> int` | Modular square root via Tonelli–Shanks. |
|
|
313
|
+
| `T(point, s, a, p) -> (int, int)` | One LAI transform step. |
|
|
314
|
+
| `keygen(p, a, P0) -> (k, Q)` | Generate private key and public point. |
|
|
315
|
+
| `encrypt(m, Q, p, a, P0) -> (C1,C2)` | Encrypt integer message. |
|
|
316
|
+
| `decrypt(C1, C2, k, a, p) -> int` | Decrypt ciphertext to integer. |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Testing
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
pytest --disable-warnings -q
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Contributing & Development
|
|
329
|
+
|
|
330
|
+
1. Fork the repo
|
|
331
|
+
2. Create branch: `git checkout -b feature/xyz`
|
|
332
|
+
3. Implement changes with corresponding tests
|
|
333
|
+
4. Run tests: `pytest`
|
|
334
|
+
5. Submit Pull Request
|
|
335
|
+
|
|
336
|
+
Please follow PEP 8 and include unit tests for new functionality.
|
|
337
|
+
|
|
338
|
+
---
|
data/lib/laicrypto.rb
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
require_relative "laicrypto/version"
|
|
3
|
+
|
|
4
|
+
module Laicrypto
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
|
|
7
|
+
# H(x, y, s, p) = SHA256(x || y || s) mod p
|
|
8
|
+
def self.H(x, y, s, p)
|
|
9
|
+
data = "#{x}|#{y}|#{s}"
|
|
10
|
+
digest = Digest::SHA256.digest(data)
|
|
11
|
+
digest_bn = digest.unpack1("H*").to_i(16)
|
|
12
|
+
digest_bn % p
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# mod_pow(base, exp, mod): base^exp mod mod (exponentiation by squaring)
|
|
16
|
+
def self.mod_pow(base, exp, mod)
|
|
17
|
+
result = 1
|
|
18
|
+
b = base % mod
|
|
19
|
+
e = exp
|
|
20
|
+
|
|
21
|
+
while e > 0
|
|
22
|
+
result = (result * b) % mod if (e & 1) != 0
|
|
23
|
+
b = (b * b) % mod
|
|
24
|
+
e >>= 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
result
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# sqrt_mod(a, p): Tonelli–Shanks to find sqrt modulo p (p prime). Return nil if no root.
|
|
31
|
+
def self.sqrt_mod(a, p)
|
|
32
|
+
a = a % p
|
|
33
|
+
return 0 if a == 0
|
|
34
|
+
|
|
35
|
+
# Legendre symbol: a^((p-1)//2) mod p
|
|
36
|
+
ls = mod_pow(a, (p - 1) / 2, p)
|
|
37
|
+
return nil if ls == p - 1
|
|
38
|
+
|
|
39
|
+
# Shortcut if p % 4 == 3
|
|
40
|
+
if p % 4 == 3
|
|
41
|
+
return mod_pow(a, (p + 1) / 4, p)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Tonelli–Shanks for p ≡ 1 (mod 4)
|
|
45
|
+
q = p - 1
|
|
46
|
+
s = 0
|
|
47
|
+
while (q & 1) == 0
|
|
48
|
+
q >>= 1
|
|
49
|
+
s += 1
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Find z: a quadratic non-residue
|
|
53
|
+
z = 2
|
|
54
|
+
while mod_pow(z, (p - 1) / 2, p) != p - 1
|
|
55
|
+
z += 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
m = s
|
|
59
|
+
c = mod_pow(z, q, p)
|
|
60
|
+
t = mod_pow(a, q, p)
|
|
61
|
+
r = mod_pow(a, (q + 1) / 2, p)
|
|
62
|
+
|
|
63
|
+
loop do
|
|
64
|
+
return r if t == 1
|
|
65
|
+
# Find smallest i: t^(2^i) ≡ 1 (mod p)
|
|
66
|
+
t2i = t
|
|
67
|
+
i = 0
|
|
68
|
+
(1...m).each do |j|
|
|
69
|
+
t2i = (t2i * t2i) % p
|
|
70
|
+
if t2i == 1
|
|
71
|
+
i = j
|
|
72
|
+
break
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
# Compute next values
|
|
76
|
+
b = mod_pow(c, 1 << (m - i - 1), p)
|
|
77
|
+
m = i
|
|
78
|
+
c = (b * b) % p
|
|
79
|
+
t = (t * c) % p
|
|
80
|
+
r = (r * b) % p
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# T(point, s, a, p): transformation
|
|
85
|
+
# x' = (x + a + H(x,y,s)) * inv2 mod p
|
|
86
|
+
# y' = sqrt_mod(x*y + H(x,y,s), p)
|
|
87
|
+
# If sqrt_mod returns nil, increment s (up to 10 tries)
|
|
88
|
+
def self.T(point, s, a, p)
|
|
89
|
+
x, y = point
|
|
90
|
+
inv2 = mod_pow(2, p - 2, p) # modular inverse of 2 mod p
|
|
91
|
+
trials = 0
|
|
92
|
+
s_current = s
|
|
93
|
+
|
|
94
|
+
while trials < 10
|
|
95
|
+
h = H(x, y, s_current, p)
|
|
96
|
+
x_candidate = ((x + a + h) * inv2) % p
|
|
97
|
+
y_sq = (x * y + h) % p
|
|
98
|
+
y_candidate = sqrt_mod(y_sq, p)
|
|
99
|
+
return [x_candidate, y_candidate] unless y_candidate.nil?
|
|
100
|
+
|
|
101
|
+
s_current += 1
|
|
102
|
+
trials += 1
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
raise Error, "T: Gagal menemukan sqrt untuk y^2=#{y_sq} mod #{p} setelah #{trials} percobaan."
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# _pow_T_range(P, start_s, exp, a, p): apply T iteratively exp times, starting seed = start_s
|
|
109
|
+
def self._pow_T_range(P, start_s, exp, a, p)
|
|
110
|
+
result = P.dup
|
|
111
|
+
curr_s = start_s
|
|
112
|
+
exp.times do
|
|
113
|
+
result = T(result, curr_s, a, p)
|
|
114
|
+
curr_s += 1
|
|
115
|
+
end
|
|
116
|
+
result
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# keygen(p, a, P0) → { k: Integer, Q: [x,y] }
|
|
120
|
+
def self.keygen(p, a, P0)
|
|
121
|
+
loop do
|
|
122
|
+
k_candidate = rand(1...p)
|
|
123
|
+
begin
|
|
124
|
+
Q = _pow_T_range(P0, 1, k_candidate, a, p)
|
|
125
|
+
return { k: k_candidate, Q: Q }
|
|
126
|
+
rescue Error
|
|
127
|
+
next
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# encrypt(m, public_Q, k, p, a, P0) → { C1: [x,y], C2: [x,y], r: Integer }
|
|
133
|
+
def self.encrypt(m, public_Q, k, p, a, P0)
|
|
134
|
+
loop do
|
|
135
|
+
r = rand(1...p)
|
|
136
|
+
# Compute C1 = T^r(P0) with seeds 1..r
|
|
137
|
+
begin
|
|
138
|
+
C1 = _pow_T_range(P0, 1, r, a, p)
|
|
139
|
+
rescue Error
|
|
140
|
+
next
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Compute Sr = T^r(public_Q) with seeds (k+1)..(k+r)
|
|
144
|
+
begin
|
|
145
|
+
Sr = _pow_T_range(public_Q, k + 1, r, a, p)
|
|
146
|
+
rescue Error
|
|
147
|
+
next
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
M0 = m % p
|
|
151
|
+
M = [ M0 < 0 ? M0 + p : M0, 0 ]
|
|
152
|
+
C2 = [ (M[0] + Sr[0]) % p, (M[1] + Sr[1]) % p ]
|
|
153
|
+
return { C1: C1, C2: C2, r: r }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# decrypt(C1, C2, k, r, a, p) → Integer (original message)
|
|
158
|
+
def self.decrypt(C1, C2, k, r, a, p)
|
|
159
|
+
S = _pow_T_range(C1, r + 1, k, a, p)
|
|
160
|
+
M0 = (C2[0] - S[0]) % p
|
|
161
|
+
M0 < 0 ? M0 + p : M0
|
|
162
|
+
end
|
|
163
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: laicrypto
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- GALIH RIDHO UTOMO
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-05-31 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: 'LAI is a promising post-quantum cryptosystem based on isogenies of elliptic
|
|
14
|
+
curves over lemniscate lattices, offering resistance against quantum-capable adversaries.
|
|
15
|
+
|
|
16
|
+
'
|
|
17
|
+
email:
|
|
18
|
+
- g4lihru@students.unnes.ac.id
|
|
19
|
+
executables: []
|
|
20
|
+
extensions: []
|
|
21
|
+
extra_rdoc_files: []
|
|
22
|
+
files:
|
|
23
|
+
- README.md
|
|
24
|
+
- lib/laicrypto.rb
|
|
25
|
+
- lib/laicrypto/version.rb
|
|
26
|
+
homepage: https://github.com/4211421036/pqcrypto
|
|
27
|
+
licenses:
|
|
28
|
+
- MIT
|
|
29
|
+
metadata: {}
|
|
30
|
+
post_install_message:
|
|
31
|
+
rdoc_options: []
|
|
32
|
+
require_paths:
|
|
33
|
+
- lib
|
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
requirements: []
|
|
45
|
+
rubygems_version: 3.3.27
|
|
46
|
+
signing_key:
|
|
47
|
+
specification_version: 4
|
|
48
|
+
summary: LAI is a promising post-quantum cryptosystem based on isogenies of elliptic
|
|
49
|
+
curves.
|
|
50
|
+
test_files: []
|