asymmetric-encryption


Nameasymmetric-encryption JSON
Version 0.1.6 PyPI version JSON
download
home_pagehttps://github.com/RoyNisimov/AsymmetricEncryption
SummaryA simple asymmetric encryption module
upload_time2024-03-21 12:43:26
maintainerNone
docs_urlNone
authorRoy Nisimov, jacebalaron (Daniel gaisenberg)
requires_pythonNone
licenseMIT
keywords python cipher asymmetric encryptions signing verifying protocols encryption decryption
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # WARNING
This is probably hazards because I don't know best practices.
I write this only for fun and learning.
Do not use it on real things.
Checkout Pycryptodome [here](https://github.com/Legrandin/pycryptodome) and [here](https://pycryptodome.readthedocs.io/en/latest/)


# Asymmetric-Encryption
Asymmetric encryption uses two keys, one public, one private.
You can encrypt with the public key and only decrypt with the private key.
You can also sign with them.

# Installation
```
pip install asymmetric-encryption
```
[In PyPI](https://pypi.org/project/asymmetric-encryption/)



# Table of contents

---
If you don't know what you're doing, use [Unhazardous](#unhazardous)

| Algorithm                                        | Code                             | Math behind it                   |
|--------------------------------------------------|----------------------------------|----------------------------------|
| [RSA](#rsa)                                      | [Code](#rsa-code)                | [Math](#rsa-math)                |
| [ElGamal](#elgamal)                              | [Code](#elgamal-code)            | [Math](#elgamal-math)            |
| [DSA](#dsa)                                      | [Code](#dsa-code)                | [Math](#dsa-math)                |
| [ECC](#ecc)                                      | [Code](#ecc-code)                | [Math](#ecc-math)                |
| [DLIES](#dlies)                                  | [Code](#dlies-code)              | [Math](#dlies-math)              |
| [LWE](#learning-with-errors)                     | [Code](#lwe-code)                | [Math](#lwe-math)                |
| [PKCS7](#pkcs7)                                  | [Code](#pkcs7-code)              | [Math](#pkcs7-math)              |
| [OAEP](#oaep)                                    | [Code](#oaep-code)               | [Math](#oaep-math)               |
| [DH](#diffie-hellman)                            | [Code](#dh-code)                 | [Math](#dh-math)                 |
| [YAK](#yak)                                      | [Code](#yak-code)                | [Math](#yak-math)                |
| [MQV](#mqv)                                      | [Code](#mqv-code)                | [Math](#mqv-math)                |
| [SSS](#sss)                                      | [Code](#sss-code)                | [Math](#sss-math)                |
| [Fiat–Shamir](#fiat-shamir-zero-knowledge-proof) | [Code](#fiat-shamir-code)        | [Math](#fiat-shamir-math)        |
| [OT1O2](#oblivious-transfer)                     | [Code](#oblivious-transfer-code) | [Math](#oblivious-transfer-math) |
| [TPP](#three-pass-protocol)                      | [Code](#tpp-code)                | [Math](#tpp-math)                |
| [POK](#proof-of-knowledge)                       | [Code](#pok-code)                | [Math](#pok-math)                |
---


# Math symbols
- Pow : **
- Modulo / Mod : %
- XOR : ^
- Append : ||
- x**-1 % n : means the [Modular multiplicative inverse](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse),
    means that if d = e**-1 % n -> e*d % n == 1


# Not my code
- [Prime Number Generator](https://www.geeksforgeeks.org/how-to-generate-large-prime-numbers-for-rsa-algorithm/)
- [Repeating key xor \ OTP (one-time-pad)](https://www.geeksforgeeks.org/encrypt-using-xor-cipher-with-repeating-key/)
- [Shamir's Secret Sharing](#https://www.geeksforgeeks.org/implementing-shamirs-secret-sharing-scheme-in-python/)
- [Unedited ECC](https://github.com/cgossi/fundamental_cryptography_with_python/blob/main/implementing_p_256_ecdhe.py)
---

# RSA
RSA stands for Rivest–Shamir–Adleman, the three people who invented it (Ron Rivest, Adi Shamir, and Leonard Adleman).

RSA is considered one of the best asymmetric crypto systems.
Used for authentication and Diffie-Hellman exchanges.

Problems:
If m >= n then the encryption wouldn't work

- [code here](#rsa-code)
- [math here](#rsa-math)
## RSA Math
```
==============================================
                   Generate
----------------------------------------------
let p and q be prime numbers
let n = p * q
let tot(n) = (p - 1) * (q - 1)
let e be prime number such that 2 < e < tot_n -1 and gcd(e, tot_n) == 1
let d = e**-1 % tot_n
        |
        ⌄
It took me ages to figure out what that means.
Bassicly if you do e * d and then mod it by tot_n you get 1.
e and d must be inteager primes.
For example:
p = 5 // Prime should be a lot larger this is for example. 
q = 7 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
n = 35 // p * q, 5 * 7 = 35
tot_n = 24 // (p - 1) * (q - 1), (5 - 1) * (7 - 1) = 4 * 6 = 24
e = 5 // greatest common denemenator between tot_n and e is 1
(e * d) % tot_n = 1
d = 5 // 5 * 5 = 25, 25 % 24 is 1


Public: e, n
Private: p, q, tot_n, d
==============================================
                   Encryption
----------------------------------------------
c = m**e % n
==============================================
                   Decryption
----------------------------------------------
m = c**d % n
==============================================
                   Signature
----------------------------------------------
s = m**d % n
==============================================
                    Verify 
----------------------------------------------
v = s**e % n
m == v
==============================================
```
## RSA Code
**WARNING:** This is the bare bones RSA with OAEP (If you pad it with OAEP). In the signing the msg is turning into a hash (sha256)

```python
from AsymmetricEncryptions.PublicPrivateKey import RSA
from AsymmetricEncryptions.Protocols import OAEP

message: bytes = b"RSA test"

# pad
message = OAEP.oaep_pad(message)
print(message)
# Key generation
priv, pub = RSA.generate_key_pair(1024)
print(priv)
print(pub)
# Encryption (Assume we don't have the private key)
cipher = RSA(pub)
encrypted_msg: bytes = cipher.encrypt(message)

# decryption (we must have the private key (d))
cipher = RSA(priv)
msg: bytes = cipher.decrypt(encrypted_msg)
# make sure to use OAEP.oaep_unpad on msg

# Test
print(OAEP.oaep_unpad(msg))
print(message)
print(msg)
print(msg == message)  # True

# Sign
cipher = RSA(priv)
s: bytes = cipher.sign(msg)
cipher.verify(s, msg)
# Verify (Will throw and error if it isn't auth)
```
**WARNING:** The exportation process is dumping it to JSON, then XOR it with the pwd.
The HMAC is then put before it.


You can export and load keys like this:

```
from AsymmetricEncryptions import RSA, RSAKey

priv, pub = RSA.generate_key_pair(1024)
priv.export(file_name="file_name.txt", pwd=b"test")
RSAKey.load(file_name="file_name.txt", pwd=b"test")
# load will throw an assertion error if the HMACs aren't the same
```
# ElGamal
ElGamal was invented in 1985 by [Taher Elgamal](https://en.wikipedia.org/wiki/Taher_Elgamal).
Read more [here](https://en.wikipedia.org/wiki/ElGamal_encryption)



- [code here](#elgamal-code)
- [math here](#elgamal-math)
## ElGamal Math
```
                        The math of ElGamal
------------------------------------------------------------------------

                          Key Generation
------------------------------------------------------------------------
Let p = large prime number
Let g = 1 < g < p-1
Let x = 1 < x < p-1
Let y = g**x % p

Public = {p,g,y}
Private = {x}

                            Encryption
------------------------------------------------------------------------

m = message < p
Let b = 2 < b < p-1
C1 = g**b % p
C2 = (m * y**b) % p

                            Decryption
------------------------------------------------------------------------

XM = C1**x % p
m = (C2 * XM**(p-2)) % p

                             Signing
------------------------------------------------------------------------
m = message
k = 0 < k < p
s1 = g**k % p
phi = p - 1
mod_inv = k ** -1 % phi // pow(k, -1, phi) or mod_inv*k % phi == 1
s2 = (mod_inv * (m - x * s1)) % phi

Send {m, s1, s2}
Keep k private

                             Verifying
------------------------------------------------------------------------
V = y**s1 * s1**s2 % p
W = g**m % p
If V == W then the message was signed by the private key



                              Example
------------------------------------------------------------------------

Let p = 23
Let g = 6
Let x = 8
Let y = 6**8 % 23 = 18

m = 4
Let b = 3
C1 = 6**3 % 23 = 9
C2 = (4 * 18**3) % 23 = 6

XM = 9**8 % 23 = 13
m = (6 * 13**21) % 23 = 4

Sign 
m = 5
k = 3
s1 = g**k % m = 9
phi_n = p-1 = 22
inv = k**-1 % phi_n = 15
s2 = (inv * (m - x * s1)) % phi_n = 7

Verify
V = (18**9 * 9**7) % 23 = 2
W = 6**5 % 23 = 2

The message is authentic
```
## ElGamal Code
**WARNING:** This is the bare bones ElGamal with OAEP (If you pad it with OAEP). Also, the signature is turning the msg into a hash (sha256)

```python
from AsymmetricEncryptions.PublicPrivateKey.ElGamal import ElGamal
from AsymmetricEncryptions.Protocols import OAEP

message: bytes = b"ElGamal test"

# pad
message = OAEP.oaep_pad(message)
print(message)
# Key generation
priv, pub = ElGamal.generate_key_pair(2048)
print(priv)
print(pub)
# Encryption (Assume we don't have the private key)
cipher = ElGamal(pub)
encrypted_msg = cipher.encrypt(message)

# decryption (we must have the private key (d))
cipher = ElGamal(priv)
msg: bytes = cipher.decrypt(encrypted_msg)
# make sure to use OAEP.oaep_unpad on msg

# Test
print(OAEP.oaep_unpad(msg))
print(message)
print(msg)
print(msg == message)  # True

# Sign
cipher = ElGamal(priv)
signature = cipher.sign(msg)
cipher.verify(signature)
# Verify (Will throw and error if it isn't auth)
```
**WARNING:** The exportation process is dumping it to JSON, then XOR it with the pwd.
The HMAC is then put before it.


You can export and load keys like this:

```
from AsymmetricEncryptions import ElGamalKey, ElGamal

priv, pub = ElGamal.generate_key_pair(1024)
priv.export(file_name="file_name.txt", pwd=b"test")
ElGamalKey.load(file_name="file_name.txt", pwd=b"test")
# load will throw an assertion error if the HMACs aren't the same
```

# DSA
The Digital Signature Algorithm (DSA) is a public-key cryptosystem and Federal Information Processing Standard for digital signatures, based on the mathematical concept of modular exponentiation and the discrete logarithm problem. DSA is a variant of the Schnorr and ElGamal signature schemes.

All the math was taken straight from Wikipedia. Read more [here](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm)

**DSA is a signature only algorithm, no encryption and key exchange**

- [code here](#dsa-code)
- [math here](#dsa-math)
## DSA Math
```
                           The math of DSA
------------------------------------------------------------------------
The math is very complex.
Good luck.
        
                            Key generation
------------------------------------------------------------------------
Key generation has two phases. 
The first phase is a choice of algorithm parameters which may be shared between different users of the system.
The second phase computes a single key pair for one user.

First phase (paramaters)
------------------------------
1. Choose an aproved hash function (I chose Sha256) H that outputs |H| bits (256)
2. Choose a key length L (1024 without any change).
(2048 or 3072 is considered safer for life long until 2030)
3. Choose modulus N such that N < L and 0 < N <= |H|
Tricky part:
4. Choose N-bit prime q
5. Choose L-bit prime p such that (p -1) % q == 0
End of tricky part.
6. Choose 0 < h < p - 2
7. Compute g = h**((p-1)//q) % p
{p, q, g} may be shared.

Second phase (Actuall key gen)
-------------------------------
1. Choose private key x such that 0 < x < q -1
2. Compute public key y, y = g**x % p

                            Signing
------------------------------------------------------------------------
m = message as an int
1. Choose an integer k randomly from {1... q-1}
2. Compute r = (g**k % p) % q
3. Compute s = ((k**-1 % q) * (H(m) + x * r)) % q
4. If s == 0 or r == 0: start over with a different k
Sig = (r, s)
                            Verifying
------------------------------------------------------------------------
m = message as an int
1. Verify that 0 < r < q, 0 < s < q
2. Compute W = s**-1 % q
3. Compute U1 = (H(m) * w) % q
4. Compute U2 = (r * w) % q
5. Compute V = (((g**u1 % p) * (y**u2 % p)) % p) % q
The signature is valid if and only if V == r
------------------------------------------------------------------------
                                
                                
                                Example
------------------------------------------------------------------------
// key param gen
p, q = 11, 5 // (p -1) % q == 0
h = 8
g = 8**2 % 11 = 9
{11, 5, 9}
// make a key
x = 7
y = 9**7 % 11 = 4

// sign
m = 3
H(M) = 7
k = 2
r = (9**2 % 11) % 5 = 4
s = ((2**-1 % 5) * (7 + 7 * 4)) % 5 = (3 * 56) % 5 = 3
{r = 4, s = 3}

// verify
m = 3
H(M) = 7
W = 3**-1 % 5 = 2
U1 = (7 * 2) % 5 = 4
U2 = (4 * 2) % 5 = 3
V = (((5 ** 4 % 11) *  (4 ** 3 % 11)) % 11) % 5 = 4

V == r -> the message is authentic



```


## DSA Code
**WARNING:** I made this with some questionable decisions, this algorithm is complex, please use [PyCryptodome implementation](https://pycryptodome.readthedocs.io/en/latest/src/public_key/dsa.html) or use [RSA](#rsa) instead.

```python
from AsymmetricEncryptions.PublicPrivateKey.DSA import DSA

message: bytes = b"DSA test"

# Key generation, Will take longer if the nBit is large and is not 1024, 2048, or 3072
# An extra bool (use_precalculated) is equal to true, this is to save time, you can turn it off though.
priv, pub = DSA.generate_key_pair(1024)
print(priv)
print(pub)

# Sign
cipher = DSA(priv)
sig = cipher.sign(message)
cipher.verify(sig, message)
# Verify (Will throw and error if it isn't auth)
```
**WARNING:** The exportation process is dumping it to JSON, then XOR it with the pwd.
The HMAC is then put before it.


You can export and load keys like this:

```python
from AsymmetricEncryptions.PublicPrivateKey.DSA import DSA, DSAKey

priv, pub = DSA.generate_key_pair()
priv.export(file_name="file_name.txt", pwd=b"test")
DSAKey.load(file_name="file_name.txt", pwd=b"test")
# load will throw an assertion error if the HMACs aren't the same
```


# ECC
Elliptic Curve Cryptography.

ECC in an approach to asymmetric cryptography with the hardest math concepts.

**Note:** ECC protocols like ECDH aren't in the .Protocols module but in the ECC module.

[Computerphile](https://www.youtube.com/watch?v=NF1pwjL9-DE)

## ECC Math
[Wiki](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication)

We first define the curve using this equation y^2 = x^3 + ax + b .

Now we pick a prime modulus P and a generator point G.

```

Key Gen
-----------

x = random int
y = G * x
Private: {x}
Public: {y}

ECDH
-----------
Ax, AY
Bx, By

Alice shared key:
Ax * By
Bob shared key:
Bx * AY


Encryption
-------------
You can't just encrypt with ECC but you can make an hybrid scheme (ECIES).

r = random
R = G * r
S = Y * r
sk = KDF(S)
c = Encrypt(msg, sk)
send c, R

Decryption
------------
s = R * x
sk = KDF(s)
msg = Decrypt(c, sk)

```




Point addition is done like this (P + Q = R)

![Image](https://github.com/RoyNisimov/AsymmetricEncryption/assets/95145981/3a79c131-7a8f-4b63-97d9-14b6a3699412)

Multiplying a point by itself it's addition but drawing a tangent.

![Image](https://media.springernature.com/lw685/springer-static/image/chp%3A10.1007%2F978-981-16-7018-3_46/MediaObjects/511640_1_En_46_Fig2_HTML.png)

[Signatures](https://www.youtube.com/watch?v=r9hJiDrtukI)


## ECC code

```python
from AsymmetricEncryptions.PublicPrivateKey.ECC import ECKey, ECDH, ECSchnorr, ECIES, EllipticCurveNISTP256

curve = EllipticCurveNISTP256.get_curve()
# key pair gen
key_pair = ECKey.new(curve)
priv = key_pair.private_key  # int
pub = key_pair.public_key  # ECPoint

# ECDH

keyA = ECKey.new(curve)
ecdh = ECDH(keyA)
A = keyA.public_key

keyB = ECKey.new(curve)
B = keyB.public_key

shared_key_alice = ecdh.Stage1(B)

shared_key_bob = ECDH.Stage2(keyB, A)

print(shared_key_alice)
print(shared_key_bob)

assert shared_key_alice == shared_key_bob

# ECIES
keyPair = ECKey.new(EllipticCurveNISTP256.get_curve())
msg = b"test"
c = ECIES.encrypt(msg, keyPair.public_key)
print(c)
d = ECIES.decrypt(c, keyPair)
print(d)
assert d == msg

# Schnorr signing
key = ECKey.new(curve)
signer = ECSchnorr(key)
msg = b"test"
signature = signer.sign(msg)
verify = ECSchnorr.verify(signature, msg, key.public_key)
print(verify)

# export
key = ECKey.new(curve)
key.export("key.key", b"password") # there's also an encryption function variable (XOR right now)
new_key = ECKey.load("key.key", b"password")
assert new_key == key
```

# DLIES
Discrete Logarithm Integrated Encryption Scheme is an encryption scheme.

It's basically [Diffie-Hellman](#diffie-hellman) with encryption.

# DLIES Math
``` 
# key gen
let n be a large prime
let g < n - 1
let x < n - 1 
let y = g**x % n
Private: {x}, Public: {n, g, y}
# encryption
{E(m, S) -> symmetric encryption function, m}
let r be a random value
let R = g**r % n
let S = y**r % n
let E = E(m, S)
ciphertxt: (E, R)

# decryption
{D(m, S) -> corresponding decryption function, (E, R)}
S = R**x % n
m = D(E, S)

```

# DLIES Code
```python
from AsymmetricEncryptions.PublicPrivateKey import DLIESKey, DLIES

key = DLIESKey.new(1024)
msg = b"test"

c = DLIES.encrypt(key.public, msg)
d = DLIES.decrypt(key, c)

print(c)
print(d)
# export
priv, pub = DLIES.generate_key_pair(2048)
priv.export("key.key", b"password") # there's also an encryption function variable (XOR right now)
new_key = DLIESKey.load("key.key", b"password")
assert new_key == priv
```



# Learning With Errors
LWE is a [post quantum cryptography algorithm](https://en.wikipedia.org/wiki/Post-quantum_cryptography)

[Video](https://www.youtube.com/watch?v=K026C5YaB3A)

# LWE Math
[Video](https://www.youtube.com/watch?v=MBdKvBA5vrw)

```
First define a vector S this will be your private key then we need to define A.
A is a group of random vectors that contain random numbers up to a prime number q.
Then we define E (the errors in Learning With Errors).

E contains numbers between 1-4 including 1 and 4. 

A helper function is this: 
def helper(list, s):
    return sum([(s[i] * list[i]) % q for i in range(len(list))])

Now we can use our variables to define B using this formula: B[i] = helper(A, S) + E[i] (mod q)

So for example A={{80, 5, 6}, {86, 19, 17}...}, S={5, 2, 4}, Q=97 E={3,3,4...}

So B1 will be equal to (80 * 5) + (5 * 2) + (6 * 4) + 3 (mod 97) = 49.

To encrypt a bit (m in [0, 1]) we define u and v 


def sum_u(l) -> list[int]:
    result_arr: list[int] = [0] * len(l[0])
    for i in range(len(l)):
        for c in range(len(result_arr)):
            result_arr[c] += l[i][c]
    return result_arr
    
The sum_u function sums everything in a list[list] into a list[]
For example:
l = [[10, 20], [50, 30], [10, 20]]
So the answer of sum_u(l) is [70, 70]

u = sum_u(A samples)
v = (sum(B samples) - Q÷2 * M) % q

To decrypt we take v and u to this formula dec = v - helper(u, s) (mod q)
If dec is less than q ÷ 2 then M=0
If dec is more then q ÷ 2 then M=1

This isn't gurrenteed to work 100% of the time!
```
Watch [this video](https://www.youtube.com/watch?v=K026C5YaB3A) for a much better explanation.

# LWE Code
```python
from AsymmetricEncryptions.PublicPrivateKey.LWE import LWEKey, LWE

if __name__ == '__main__':
    # generation
    key_pair = LWEKey.new(128)
    # encryption
    m = b"test"
    cipher = LWE(key_pair)
    ciphertxt = LWE.encrypt_message(key_pair.public, m)
    plaintext = cipher.decrypt_message(ciphertxt)
    print(plaintext)
    assert plaintext == m
    
    # exportation
    key_pair.export("test.txt", b"super secret")
    new_key = LWEKey.load("test.txt", b"super secret")
    assert new_key == key_pair
```

# Protocols

- [Diffie-Hellman](#diffie-hellman)
- [OAEP](#oaep)
- [SSS](#sss)
- [Zero Knowledge](#fiat-shamir-zero-knowledge-proof)
- [Oblivious Transfer](#oblivious-transfer)

# Diffie-Hellman
Diffie-Hellman (commonly referred to as DH) is an essential [key exchange protocol](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange).
DH is a mathematical method of securely exchanging cryptographic keys over a public channel and was one of the first public-key protocols as conceived by Ralph Merkle and named after Whitfield Diffie and Martin Hellman. DH is one of the earliest practical examples of public key exchange implemented within the field of cryptography. Published in 1976 by Diffie and Hellman, this is the earliest publicly known work that proposed the idea of a private key and a corresponding public key.

[Computerphile (without math)](https://www.youtube.com/watch?v=NmM9HA2MQGI)

[Computerphile (with math)](https://www.youtube.com/watch?v=Yjrfm_oRO0w)

**WARNING:** This protocol here doesn't involve authentication, in real DH every party signs their messages before sending.


## DH Math
``` 
Alice and Bob publicly agree on p (large prime) and g (g < p)


|        Alice       |        Public       |        Bob       |     
----------------------------------------------------------------
|      {a, g, p}     |     g, p            |      {b, g, p}   |
|                    |                     |                  |
|     A = g**a % p   |   A ----------->    |     k = A**b % p |
|                    |                     |                  |
|   k = A**b % p     |     <----------- B  |     B = g**b % p |
|                    |                     |                  |
----------------------------------------------------------------
k is the shared symmetric secret.
g**a**b % p == g**(a * b) % p

```

## DH Code

```python
from AsymmetricEncryptions.PublicPrivateKey.RSA import RSA
from AsymmetricEncryptions.Protocols import DiffieHellman

Apriv, Apub = RSA.generate_key_pair(1024)
Bpriv, Bpub = RSA.generate_key_pair(1024)
DH = DiffieHellman.new(Apriv, 1024)
# Alice
A = DH.Stage1()
gp = DH.get_gp()
# send A and gp to Bob

# Bob
B, Bob_shared = DiffieHellman.Stage2(gp, Bpriv, A)
print(Bob_shared)
# send B to Alice

# Alice
Alice_shared = DH.Stage3(B)
print(Alice_shared)
print(Alice_shared == Bob_shared)

```

# Yak
[Wiki](https://en.wikipedia.org/wiki/YAK_(cryptography))

The YAK is a public-key authenticated key-agreement protocol, proposed by Feng Hao in 2010. It is claimed to be the simplest authenticated key exchange protocol among the related schemes, including MQV, HMQV, Station-to-Station protocol, SSL/TLS etc. The authentication is based on public key pairs. As with other protocols, YAK normally requires a Public Key Infrastructure to distribute authentic public keys to the communicating parties. The security of YAK is disputed (see below and the talk page).

## Yak Math
See [Wiki description](https://en.wikipedia.org/wiki/YAK_(cryptography))
## Yak Code

```python
from AsymmetricEncryptions.Protocols.YAK import YAK
from AsymmetricEncryptions.PublicPrivateKey import DLIESKey


yakAlice = YAK.new(2048)
gq = yakAlice.get_gq()
yakBob = YAK(*gq)
g, q = gq

AlicePriv = DLIESKey.build(g, q)
AlicePub = AlicePriv.public
BobPriv = DLIESKey.build(g, q)
BobPub = BobPriv.public

x, AliceSend = yakAlice.stage_1()
y, BobSend = yakBob.stage_1()

sharedAlice = yakAlice.stage_2(x, AlicePriv, BobSend, BobPub)
sharedBob = yakBob.stage_2(y, BobPriv, AliceSend, AlicePub)
print(sharedAlice)
print(sharedBob)
assert sharedAlice == sharedBob
```

# MQV
[Wiki](https://en.wikipedia.org/wiki/MQV)

MQV (Menezes–Qu–Vanstone) is an authenticated protocol for key agreement based on the Diffie–Hellman scheme. Like other authenticated Diffie–Hellman schemes, MQV provides protection against an active attacker. The protocol can be modified to work in an arbitrary finite group, and, in particular, elliptic curve groups, where it is known as elliptic curve MQV (ECMQV).

## MQV Math
See [Wiki description](https://en.wikipedia.org/wiki/MQV)
See [Video](https://www.youtube.com/watch?v=JKlTdY07IY4)
## MQV Code

```python
from AsymmetricEncryptions.PublicPrivateKey.ECC import ECMQV, ECKey, EllipticCurveNISTP256

a = ECKey.new(EllipticCurveNISTP256.get_curve())
b = ECKey.new(EllipticCurveNISTP256.get_curve())

x = ECMQV.Stage1n2(EllipticCurveNISTP256.get_curve())
y = ECMQV.Stage1n2(EllipticCurveNISTP256.get_curve())

Sa = ECMQV.Stage3n4(a, x)
Sb = ECMQV.Stage3n4(b, y)

keyA = ECMQV.Stage5(b.get_public_key(), Sa, y.get_public_key())
keyB = ECMQV.Stage5(a.get_public_key(), Sb, x.get_public_key())

print(keyA)
print(keyB)
assert keyA == keyB
```




# PKCS7
Padding for symmetric algorithms

## PKCS7 Math
[Video](https://www.youtube.com/watch?v=3OTMLUEPZUc)

## PKCS7 Code

```python
from AsymmetricEncryptions.Protocols import PKCS7

msg = b"PKCS7"
block_size = 32
padded = PKCS7(block_size).pad(msg)
print(padded)
unpadded = PKCS7(block_size).unpad(padded)
print(unpadded)
print(unpadded == msg)  # True if the msg is small 
```


# OAEP
```
O-ptimal
A-symmetric
E-ncryption
P-adding
```
[Here](https://www.youtube.com/watch?v=ZwPGE5GgG_E) and [Here](https://www.youtube.com/watch?v=bU4so01qMP4)
## OAEP Math
![image](https://miro.medium.com/v2/resize:fit:1358/1*ppgvPx2BA-Il2w9aZhRrWw.png)
```
G(x) |
     | -> hash functions outputing g and h bits
H(H) |

Pad
================================
r -> random nonce of g bits
m = m || 0**(g-len(m))
x = m ^ G(r)
y = H(X) ^ r
p =  x || y
remember that x and y are g and h bits long
--------------------------------
Unpad
================================
x = p[:g]
y = p[g:]
r = H(X) ^ y
m = x ^ G(r)
```

## OAEP Code

```python
from AsymmetricEncryptions.Protocols import OAEP

msg = b"OAEP"
padded = OAEP.oaep_pad(msg)
print(padded)
unpadded = OAEP.oaep_unpad(padded)
print(unpadded)
print(unpadded == msg)  # True if the msg is small 
```

# SSS

SSS stands for Shamir's Secret Sharing.

Adi Shamir, an Israeli scientist, first formulated the scheme in 1979.

BTW the S in RSA is also Adi Shamir.

[This video here](https://www.youtube.com/watch?v=iFY5SyY3IMQ) explains SSS very well.

Please check out [Pycryptodome's SSS](https://pycryptodome.readthedocs.io/en/latest/src/protocol/ss.html).

SSS is used to secure a secret in a distributed form, most often to secure encryption keys. The secret is split into multiple shares, which individually do not give any information about the secret.

To reconstruct a secret secured by SSS, a number of shares is needed, called the threshold. No information about the secret can be gained from any number of shares below the threshold (a property called perfect secrecy). In this sense, SSS is a generalisation of the one-time pad (which can be viewed as SSS with a two-share threshold and two shares in total).

## SSS Math

``` 
Shamir's Secret Sharing
---------------------------
n -> the number of shares
t (k in the video above) -> the number of people needed to find the answer

Take your secret as m.
Place m on a graph such that the point is (0, m).

Generate a polynomial with a0 being m. Like this for example (3 degree polynomial):
y = a0 + a1*x + a2*x**2 + a3*x**3


Generate n points on the polynomial and share them. (x != 0 of course)
-----------------------------
To find m back you need to calculate the formula again, which you can do that with t shares.
And then plug 0 into the formula and you get m.

Better m finding method
------------------------
Since we only need to calculate a0 there is and easier formula:

a0 = 0
foreach xj, yj in the points:
    prod = 1
    foreach xi, yi in the points:
        if xi == xj: continue
        prod *= xi/(xi-xj)
    prod *= yj
    a0 + prod
    
Example:
------------------------------
n = 4
t = 2

m = 3
a1 = 4 
y = 4x + 3
random point on the line number 1: x = 4, y = 16
random point on the line number 2: x = 6, y = 27
random point on the line number 3: x = 2, y = 11
random point on the line number 4: x = 1, y = 7


|
|
|
|
|                               (6, 27)
|
|               (4, 16)
|
|          (2, 11)                         
|   
|       (1, 7)
|
(0; 3)
|
|
0------------------------------------------

Given only one of those points it's impossible to find m.
However, given t points you can always find m again.

let's say that we got both points (1, 7) and (6, 27).
slope = (27 - 7) / (6 - 1) = 20 / 5 = 4
Using the formula y = m(x - x1) + y1 we get:
y = 4x - 4 + 7, y = 4x + 3.
Pluge x = 0 and we get our answer: m = 3.

```

## SSS Code

```python
from AsymmetricEncryptions.Protocols import SSS
import secrets

# (3,5) sharing scheme
t, n = 3, 5
secret = b"test"
print(f'Original Secret: {secret}')
# Phase I: Generation of shares
shares = SSS.generate_shares(n, t, secret)
print(f'Shares: {", ".join(str(share) for share in shares)}')
# Phase II: Secret Reconstruction
# Picking t shares randomly for
# reconstruction
pool = secrets.SystemRandom().sample(shares, t)
print(f'Combining shares: {", ".join(str(share) for share in pool)}')
print(f'Reconstructed secret: {SSS.reconstruct_secret(pool)}')
```
```
SSS.generate_shares -> returns a list of cordinets as a tuple: list[tuple[int, int]]
secret: the secret
n: number of shares to generate
t: the number of shares needed to reconstruct the secret

SSS.reconstruct_secret(shares) -> returns the secret
shares: the shares needed to reconstruct the secret


```

# Fiat Shamir Zero Knowledge Proof

From Wikipedia, the free encyclopedia
In cryptography, the Fiat–Shamir heuristic is a technique for taking an interactive proof of knowledge and creating a digital signature based on it. This way, some fact (for example, knowledge of a certain secret number) can be publicly proven without revealing underlying information. The technique is due to Amos Fiat and Adi Shamir (1986). For the method to work, the original interactive proof must have the property of being public-coin, i.e. verifier's random coins are made public throughout the proof protocol.

The heuristic was originally presented without a proof of security; later, Pointcheval and Stern proved its security against chosen message attacks in the random oracle model, that is, assuming random oracles exist. This result was generalized to the quantum-accessible random oracle (QROM) by Don, Fehr, Majenz and Schaffner, and concurrently by Liu and Zhandry. In the case that random oracles do not exist, the Fiat–Shamir heuristic has been proven insecure by Shafi Goldwasser and Yael Tauman Kalai. The Fiat–Shamir heuristic thus demonstrates a major application of random oracles. More generally, the Fiat–Shamir heuristic may also be viewed as converting a public-coin interactive proof of knowledge into a non-interactive proof of knowledge. If the interactive proof is used as an identification tool, then the non-interactive version can be used directly as a digital signature by using the message as part of the input to the random oracle.

## Fiat Shamir Math
``` 

---------------------------------------------------
Lets say Alice want to prove Bob that she know a password without reveling any information about it.
Alice and Bob agree on a generator g and a prime n such that g < n

Alice:
x = Hash(Password)
y = g**x % n
v < n
t = g**v % n

Alice sends to Bob: {y, t}

Bob:
Bob generates a number c such that c < n
Bob sends Alice: {c}

Alice:
r = v - c * x
Alice sends Bob: {r}

Bob:
z = (g**r * y**c) % n
verified if z == t
---------------------------------------------------
Example:
g = 35
n = 53

Alice:
x = 17
y = 35**17 % 53 = 20
v = 7
t = 35**7 % 53 = 34
{y = 20, t = 34}

Bob:
c = 15
{c = 15}

Alice:
r = 7 - 15 * 17 = -248
{r = -248}

Bob:
z = ((35**-248) * (20**15)) % 53 = 34
t == z = 34 -> verified
```
## Fiat Shamir Code

```python
from AsymmetricEncryptions.Protocols import FiatShamirZeroKnowledgeProof

if __name__ == '__main__':
    m = b'test'
    fs = FiatShamirZeroKnowledgeProof.new()
    y = fs.AliceStage1(m)
    # send y to bob
    v, t = fs.AliceStage2()
    # keep v send t
    c = fs.BobStage1()
    # Bob sends c as a challenge
    r = fs.AliceStage3(v, c, m)
    # Alice sends r to bob
    ver = fs.BobStage2(y, r, c, t)
    print(ver)
```

# Oblivious Transfer
[Wiki](https://en.wikipedia.org/wiki/Oblivious_transfer), [Computerphile](https://www.youtube.com/watch?v=wE5cl8J27Is)

In a 1–2 oblivious transfer protocol, Alice the sender has two messages m0 and m1, and wants to ensure that the receiver only learns one. Bob, the receiver, has a bit b and wishes to receive mb without Alice learning b. The protocol of Even, Goldreich, and Lempel (which the authors attribute partially to Silvio Micali) is general, but can be instantiated using RSA encryption as follows.

## Oblivious Transfer Math
``` 
# Alice
1. Alice has two messages: {m0, m1} and wants to send exactly one of them to Bob. Bob does not want Alice to know which one he receives.
2. Alice generates an RSA key pair {e, n, d: keep private}
3. She also generates two random values, {x0, x1} and sends them to Bob along with her public RSA key.
# Bob gets e, n, x0, x1
4. Bob picks b to be 0 or 1 (b is what message he will get) and selects xb
5. Bob generate a random value k (k < n) and computes:
v = (xb - k**e) % n
Bob sends to Alice: {v} and keeps {b, k} a secret.
# Alice gets: {v}
6. Alice computes:
k0 = (v - x0)**d % n
k1 = (v - x1)**d % n
7. 
mp0 = (m0 + k0) % n
mp1 = (m1 + k1) % n
Alice sends Bob: {mp0, mp1}
# Bob
mb = (mpb - k) % n
mb == the message that bob chose.


```

## Oblivious Transfer Code

```python
from AsymmetricEncryptions.Protocols import ObliviousTransfer

# Alice
otProt = ObliviousTransfer(b"test A", b"test B")
sendBob = otProt.Stage1and2and3()
# Bob
b = int(input("Choice 0 or 1: ")) % 2
AlicePubKey = sendBob[0]
sendAlice, keepPrivate = ObliviousTransfer.Stage4and5(sendBob, b)
# Alice
sendBob = otProt.Stage6and7(sendAlice)
# Bob
m = ObliviousTransfer.Stage8(sendBob, keepPrivate, b, AlicePubKey)
print(m)
```

# Three Pass Protocol
The first three-pass protocol was the Shamir three-pass protocol developed circa in 1980. It is also called the Shamir No-Key Protocol because the sender and the receiver do not exchange any keys, however the protocol requires the sender and receiver to have two private keys for encrypting and decrypting messages.

**WARNING:** the protocol needs external authentication, please authenticate.

[Spanning Tree's excellent video](https://www.youtube.com/watch?v=I6Unxb-PFhs)

## TPP Math
``` 
let keyA be a key to a symmetric encryption function EA and decryption function DA
let keyB be a key to a symmetric encryption function EB and decryption function DB

c1 = EA(m, keyA)
# Pass to Bob
c2 = EB(c1, keyB)
# Pass to Alice
c3 = DA(c2, keyA)
# Pass to Bob
m = DB(c2, keyB)

```

## TPP Code
**WARNING:** An added encryption and decryption function needs to be added. XOR IS UNSAFE IN TPP! 
```python
from AsymmetricEncryptions.Protocols.ThreePass import ThreePassProtocol


if __name__ == '__main__':
    
    def encryption_function(msg: bytes, key: bytes) -> bytes:
        ct = msg
        ... # Actually encrypt
        return ct # send 
    
    def decryption_function(ct: bytes, key: bytes) -> bytes:
        msg = ct
        ... # Actually decrypt
        return msg
    
    msg = b"message"
    alice = b"Alice's key"

    bob = b"Bob's key"

    TPP = ThreePassProtocol(alice)
    c1 = TPP.stage1(msg, encryption_function)
    print(c1)
    c2 = ThreePassProtocol.stage2(bob, c1, encryption_function)
    print(c2)
    c3 = TPP.stage3(c2, decryption_function)
    print(c3)
    m = ThreePassProtocol.stage4(c3, bob, decryption_function)
    print(m)
    assert m == msg
```

# Proof of Knowledge
Proving that you have a knowledge of a value m.

This is using Schnorr non-interactive

## POK Math
``` 
Construct a new discret log key (n -> prime, g -> generator, x -> m, y = g**x % n).
r -> random value
t = g ** r % n
c = Hash(g | y | t)
s = r + c * x
send {t, s, new_key.public}
# verify
c = Hash(g | y | t)
if g**s % n == (t * y**c) % n: the proof is true

```


## POK Code
```python
from AsymmetricEncryptions.Protocols.SchnorrPOK import POK
from AsymmetricEncryptions.PublicPrivateKey import DLIES

priv, pub = DLIES.generate_key_pair(1024)
m = b"test"
prover = POK(priv)
proof = prover.prove(m)
print(POK.verify(proof))
```

# Feistel
A feistel network is a symmetric cipher builder.

[Computerphile](https://www.youtube.com/watch?v=FGhj3CGxl8I)

## Feistel Code

```python
from AsymmetricEncryptions.Protocols import Feistel
# Here is a built-in Feistel encryption function
def func(m, k):
    ...
    # Encrypt or hash
    return m
msg = b"lol"
key = b"test key"
f = Feistel(func)
c = f.encrypt(msg, key)
m = f.decrypt(c, key)
print(c)
print(m)
```


```python
from AsymmetricEncryptions.General import Feistel_sha256
# Here is a built-in Feistel encryption function

msg = b"lol"
key = b"test key"
f = Feistel_sha256.FeistelSha256.get_feistel()
c = f.encrypt(msg, key)
m = f.decrypt(c, key)
print(c)
print(m)

```



# Unhazardous

# Contents in UH

| Algorithm              | Code                     |
|------------------------|--------------------------|
| [RSA](#uh-rsa)         | [Code](#uh-rsa-code)     |
| [ElGamal](#uh-elgamal) | [Code](#uh-elgamal-code) |
---

# UH RSA
Turns RSA into a KEM. with signatures.

## UH RSA Code
**WARNING:** This uses XOR (Aka OTP), please use a different function like AES.
Modify the function by putting `encryption_function=` for encryption and `decryption_function=` for decryption.
The signature of both functions have to be `(msg or ciphertxt: bytes, key: bytes) -> msg or ciphertxt: bytes`
For the padding: `padding_block_size=`

```python
from AsymmetricEncryptions.Unhazardous.PublicKey.UHRSA import UHRSA
from AsymmetricEncryptions.PublicPrivateKey.RSA import RSA

AlicesPriv, AlicesPub = RSA.generate_key_pair(2048)  # Alice creates a key pair
BobsPriv, BobsPub = RSA.generate_key_pair(2048)  # Bob creates a key pair
# Alice wants to send bob a message
msg = b"Long Message" * 200
cipherAlice = UHRSA(AlicesPriv)  # Alice inits a UHRSA cipher object
ciphertext = cipherAlice.encrypt(BobsPub, msg)  # Alice encrypts using Bob's public key, also creating a signature.
print(ciphertext)
# Bob gets the ciphertext and Alices public key
cipherBob = UHRSA(BobsPriv)  # Bob makes his own UHRSA cipher object
pt = cipherBob.decrypt(ciphertext, AlicesPub)  # Bob verifies the signature and finds the message
print(pt)
assert pt == msg
```


# UH ElGamal
Turns ElGamal into a KEM, with signatures.

## UH ElGamal Code
**WARNING:** This uses XOR (Aka OTP), please use a different function like AES.
Modify the function by putting `encryption_function=` for encryption and `decryption_function=` for decryption.
The signature of both functions have to be `(msg or ciphertxt: bytes, key: bytes) -> msg or ciphertxt: bytes`
For the padding: `padding_block_size=`

```python
from AsymmetricEncryptions.Unhazardous.PublicKey.UHElGamal import UHElGamal
from AsymmetricEncryptions.PublicPrivateKey.ElGamal import ElGamal

AlicesPriv, AlicesPub = ElGamal.generate_key_pair(2048) # Alice's keys
BobsPriv, BobsPub = ElGamal.generate_key_pair(2048) # Bob's keys
msg = b"test" # Message
cipherAlice = UHElGamal(AlicesPriv)
ct = cipherAlice.encrypt(BobsPub, msg) # Encrypts and signs
cipherBob = UHElGamal(BobsPriv)
pt = cipherBob.decrypt(ct, AlicesPub) # Decrypts and verify
assert pt == msg
print(ct)
print(pt)
```


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/RoyNisimov/AsymmetricEncryption",
    "name": "asymmetric-encryption",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "python, cipher, asymmetric encryptions, signing, verifying, protocols, encryption, decryption",
    "author": "Roy Nisimov, jacebalaron (Daniel gaisenberg)",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/28/77/b80c14245ebf1c5169289ef0b864c4ed87ed1c0cdd525827e78fa5c4db6c/asymmetric-encryption-0.1.6.tar.gz",
    "platform": null,
    "description": "# WARNING\r\nThis is probably hazards because I don't know best practices.\r\nI write this only for fun and learning.\r\nDo not use it on real things.\r\nCheckout Pycryptodome [here](https://github.com/Legrandin/pycryptodome) and [here](https://pycryptodome.readthedocs.io/en/latest/)\r\n\r\n\r\n# Asymmetric-Encryption\r\nAsymmetric encryption uses two keys, one public, one private.\r\nYou can encrypt with the public key and only decrypt with the private key.\r\nYou can also sign with them.\r\n\r\n# Installation\r\n```\r\npip install asymmetric-encryption\r\n```\r\n[In PyPI](https://pypi.org/project/asymmetric-encryption/)\r\n\r\n\r\n\r\n# Table of contents\r\n\r\n---\r\nIf you don't know what you're doing, use [Unhazardous](#unhazardous)\r\n\r\n| Algorithm                                        | Code                             | Math behind it                   |\r\n|--------------------------------------------------|----------------------------------|----------------------------------|\r\n| [RSA](#rsa)                                      | [Code](#rsa-code)                | [Math](#rsa-math)                |\r\n| [ElGamal](#elgamal)                              | [Code](#elgamal-code)            | [Math](#elgamal-math)            |\r\n| [DSA](#dsa)                                      | [Code](#dsa-code)                | [Math](#dsa-math)                |\r\n| [ECC](#ecc)                                      | [Code](#ecc-code)                | [Math](#ecc-math)                |\r\n| [DLIES](#dlies)                                  | [Code](#dlies-code)              | [Math](#dlies-math)              |\r\n| [LWE](#learning-with-errors)                     | [Code](#lwe-code)                | [Math](#lwe-math)                |\r\n| [PKCS7](#pkcs7)                                  | [Code](#pkcs7-code)              | [Math](#pkcs7-math)              |\r\n| [OAEP](#oaep)                                    | [Code](#oaep-code)               | [Math](#oaep-math)               |\r\n| [DH](#diffie-hellman)                            | [Code](#dh-code)                 | [Math](#dh-math)                 |\r\n| [YAK](#yak)                                      | [Code](#yak-code)                | [Math](#yak-math)                |\r\n| [MQV](#mqv)                                      | [Code](#mqv-code)                | [Math](#mqv-math)                |\r\n| [SSS](#sss)                                      | [Code](#sss-code)                | [Math](#sss-math)                |\r\n| [Fiat\u00e2\u20ac\u201cShamir](#fiat-shamir-zero-knowledge-proof) | [Code](#fiat-shamir-code)        | [Math](#fiat-shamir-math)        |\r\n| [OT1O2](#oblivious-transfer)                     | [Code](#oblivious-transfer-code) | [Math](#oblivious-transfer-math) |\r\n| [TPP](#three-pass-protocol)                      | [Code](#tpp-code)                | [Math](#tpp-math)                |\r\n| [POK](#proof-of-knowledge)                       | [Code](#pok-code)                | [Math](#pok-math)                |\r\n---\r\n\r\n\r\n# Math symbols\r\n- Pow : **\r\n- Modulo / Mod : %\r\n- XOR : ^\r\n- Append : ||\r\n- x**-1 % n : means the [Modular multiplicative inverse](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse),\r\n    means that if d = e**-1 % n -> e*d % n == 1\r\n\r\n\r\n# Not my code\r\n- [Prime Number Generator](https://www.geeksforgeeks.org/how-to-generate-large-prime-numbers-for-rsa-algorithm/)\r\n- [Repeating key xor \\ OTP (one-time-pad)](https://www.geeksforgeeks.org/encrypt-using-xor-cipher-with-repeating-key/)\r\n- [Shamir's Secret Sharing](#https://www.geeksforgeeks.org/implementing-shamirs-secret-sharing-scheme-in-python/)\r\n- [Unedited ECC](https://github.com/cgossi/fundamental_cryptography_with_python/blob/main/implementing_p_256_ecdhe.py)\r\n---\r\n\r\n# RSA\r\nRSA stands for Rivest\u00e2\u20ac\u201cShamir\u00e2\u20ac\u201cAdleman, the three people who invented it (Ron Rivest, Adi Shamir, and Leonard Adleman).\r\n\r\nRSA is considered one of the best asymmetric crypto systems.\r\nUsed for authentication and Diffie-Hellman exchanges.\r\n\r\nProblems:\r\nIf m >= n then the encryption wouldn't work\r\n\r\n- [code here](#rsa-code)\r\n- [math here](#rsa-math)\r\n## RSA Math\r\n```\r\n==============================================\r\n                   Generate\r\n----------------------------------------------\r\nlet p and q be prime numbers\r\nlet n = p * q\r\nlet tot(n) = (p - 1) * (q - 1)\r\nlet e be prime number such that 2 < e < tot_n -1 and gcd(e, tot_n) == 1\r\nlet d = e**-1 % tot_n\r\n        |\r\n        \u00e2\u0152\u201e\r\nIt took me ages to figure out what that means.\r\nBassicly if you do e * d and then mod it by tot_n you get 1.\r\ne and d must be inteager primes.\r\nFor example:\r\np = 5 // Prime should be a lot larger this is for example. \r\nq = 7 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\nn = 35 // p * q, 5 * 7 = 35\r\ntot_n = 24 // (p - 1) * (q - 1), (5 - 1) * (7 - 1) = 4 * 6 = 24\r\ne = 5 // greatest common denemenator between tot_n and e is 1\r\n(e * d) % tot_n = 1\r\nd = 5 // 5 * 5 = 25, 25 % 24 is 1\r\n\r\n\r\nPublic: e, n\r\nPrivate: p, q, tot_n, d\r\n==============================================\r\n                   Encryption\r\n----------------------------------------------\r\nc = m**e % n\r\n==============================================\r\n                   Decryption\r\n----------------------------------------------\r\nm = c**d % n\r\n==============================================\r\n                   Signature\r\n----------------------------------------------\r\ns = m**d % n\r\n==============================================\r\n                    Verify \r\n----------------------------------------------\r\nv = s**e % n\r\nm == v\r\n==============================================\r\n```\r\n## RSA Code\r\n**WARNING:** This is the bare bones RSA with OAEP (If you pad it with OAEP). In the signing the msg is turning into a hash (sha256)\r\n\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey import RSA\r\nfrom AsymmetricEncryptions.Protocols import OAEP\r\n\r\nmessage: bytes = b\"RSA test\"\r\n\r\n# pad\r\nmessage = OAEP.oaep_pad(message)\r\nprint(message)\r\n# Key generation\r\npriv, pub = RSA.generate_key_pair(1024)\r\nprint(priv)\r\nprint(pub)\r\n# Encryption (Assume we don't have the private key)\r\ncipher = RSA(pub)\r\nencrypted_msg: bytes = cipher.encrypt(message)\r\n\r\n# decryption (we must have the private key (d))\r\ncipher = RSA(priv)\r\nmsg: bytes = cipher.decrypt(encrypted_msg)\r\n# make sure to use OAEP.oaep_unpad on msg\r\n\r\n# Test\r\nprint(OAEP.oaep_unpad(msg))\r\nprint(message)\r\nprint(msg)\r\nprint(msg == message)  # True\r\n\r\n# Sign\r\ncipher = RSA(priv)\r\ns: bytes = cipher.sign(msg)\r\ncipher.verify(s, msg)\r\n# Verify (Will throw and error if it isn't auth)\r\n```\r\n**WARNING:** The exportation process is dumping it to JSON, then XOR it with the pwd.\r\nThe HMAC is then put before it.\r\n\r\n\r\nYou can export and load keys like this:\r\n\r\n```\r\nfrom AsymmetricEncryptions import RSA, RSAKey\r\n\r\npriv, pub = RSA.generate_key_pair(1024)\r\npriv.export(file_name=\"file_name.txt\", pwd=b\"test\")\r\nRSAKey.load(file_name=\"file_name.txt\", pwd=b\"test\")\r\n# load will throw an assertion error if the HMACs aren't the same\r\n```\r\n# ElGamal\r\nElGamal was invented in 1985 by [Taher Elgamal](https://en.wikipedia.org/wiki/Taher_Elgamal).\r\nRead more [here](https://en.wikipedia.org/wiki/ElGamal_encryption)\r\n\r\n\r\n\r\n- [code here](#elgamal-code)\r\n- [math here](#elgamal-math)\r\n## ElGamal Math\r\n```\r\n                        The math of ElGamal\r\n------------------------------------------------------------------------\r\n\r\n                          Key Generation\r\n------------------------------------------------------------------------\r\nLet p = large prime number\r\nLet g = 1 < g < p-1\r\nLet x = 1 < x < p-1\r\nLet y = g**x % p\r\n\r\nPublic = {p,g,y}\r\nPrivate = {x}\r\n\r\n                            Encryption\r\n------------------------------------------------------------------------\r\n\r\nm = message < p\r\nLet b = 2 < b < p-1\r\nC1 = g**b % p\r\nC2 = (m * y**b) % p\r\n\r\n                            Decryption\r\n------------------------------------------------------------------------\r\n\r\nXM = C1**x % p\r\nm = (C2 * XM**(p-2)) % p\r\n\r\n                             Signing\r\n------------------------------------------------------------------------\r\nm = message\r\nk = 0 < k < p\r\ns1 = g**k % p\r\nphi = p - 1\r\nmod_inv = k ** -1 % phi // pow(k, -1, phi) or mod_inv*k % phi == 1\r\ns2 = (mod_inv * (m - x * s1)) % phi\r\n\r\nSend {m, s1, s2}\r\nKeep k private\r\n\r\n                             Verifying\r\n------------------------------------------------------------------------\r\nV = y**s1 * s1**s2 % p\r\nW = g**m % p\r\nIf V == W then the message was signed by the private key\r\n\r\n\r\n\r\n                              Example\r\n------------------------------------------------------------------------\r\n\r\nLet p = 23\r\nLet g = 6\r\nLet x = 8\r\nLet y = 6**8 % 23 = 18\r\n\r\nm = 4\r\nLet b = 3\r\nC1 = 6**3 % 23 = 9\r\nC2 = (4 * 18**3) % 23 = 6\r\n\r\nXM = 9**8 % 23 = 13\r\nm = (6 * 13**21) % 23 = 4\r\n\r\nSign \r\nm = 5\r\nk = 3\r\ns1 = g**k % m = 9\r\nphi_n = p-1 = 22\r\ninv = k**-1 % phi_n = 15\r\ns2 = (inv * (m - x * s1)) % phi_n = 7\r\n\r\nVerify\r\nV = (18**9 * 9**7) % 23 = 2\r\nW = 6**5 % 23 = 2\r\n\r\nThe message is authentic\r\n```\r\n## ElGamal Code\r\n**WARNING:** This is the bare bones ElGamal with OAEP (If you pad it with OAEP). Also, the signature is turning the msg into a hash (sha256)\r\n\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey.ElGamal import ElGamal\r\nfrom AsymmetricEncryptions.Protocols import OAEP\r\n\r\nmessage: bytes = b\"ElGamal test\"\r\n\r\n# pad\r\nmessage = OAEP.oaep_pad(message)\r\nprint(message)\r\n# Key generation\r\npriv, pub = ElGamal.generate_key_pair(2048)\r\nprint(priv)\r\nprint(pub)\r\n# Encryption (Assume we don't have the private key)\r\ncipher = ElGamal(pub)\r\nencrypted_msg = cipher.encrypt(message)\r\n\r\n# decryption (we must have the private key (d))\r\ncipher = ElGamal(priv)\r\nmsg: bytes = cipher.decrypt(encrypted_msg)\r\n# make sure to use OAEP.oaep_unpad on msg\r\n\r\n# Test\r\nprint(OAEP.oaep_unpad(msg))\r\nprint(message)\r\nprint(msg)\r\nprint(msg == message)  # True\r\n\r\n# Sign\r\ncipher = ElGamal(priv)\r\nsignature = cipher.sign(msg)\r\ncipher.verify(signature)\r\n# Verify (Will throw and error if it isn't auth)\r\n```\r\n**WARNING:** The exportation process is dumping it to JSON, then XOR it with the pwd.\r\nThe HMAC is then put before it.\r\n\r\n\r\nYou can export and load keys like this:\r\n\r\n```\r\nfrom AsymmetricEncryptions import ElGamalKey, ElGamal\r\n\r\npriv, pub = ElGamal.generate_key_pair(1024)\r\npriv.export(file_name=\"file_name.txt\", pwd=b\"test\")\r\nElGamalKey.load(file_name=\"file_name.txt\", pwd=b\"test\")\r\n# load will throw an assertion error if the HMACs aren't the same\r\n```\r\n\r\n# DSA\r\nThe Digital Signature Algorithm (DSA) is a public-key cryptosystem and Federal Information Processing Standard for digital signatures, based on the mathematical concept of modular exponentiation and the discrete logarithm problem. DSA is a variant of the Schnorr and ElGamal signature schemes.\r\n\r\nAll the math was taken straight from Wikipedia. Read more [here](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm)\r\n\r\n**DSA is a signature only algorithm, no encryption and key exchange**\r\n\r\n- [code here](#dsa-code)\r\n- [math here](#dsa-math)\r\n## DSA Math\r\n```\r\n                           The math of DSA\r\n------------------------------------------------------------------------\r\nThe math is very complex.\r\nGood luck.\r\n        \r\n                            Key generation\r\n------------------------------------------------------------------------\r\nKey generation has two phases. \r\nThe first phase is a choice of algorithm parameters which may be shared between different users of the system.\r\nThe second phase computes a single key pair for one user.\r\n\r\nFirst phase (paramaters)\r\n------------------------------\r\n1. Choose an aproved hash function (I chose Sha256) H that outputs |H| bits (256)\r\n2. Choose a key length L (1024 without any change).\r\n(2048 or 3072 is considered safer for life long until 2030)\r\n3. Choose modulus N such that N < L and 0 < N <= |H|\r\nTricky part:\r\n4. Choose N-bit prime q\r\n5. Choose L-bit prime p such that (p -1) % q == 0\r\nEnd of tricky part.\r\n6. Choose 0 < h < p - 2\r\n7. Compute g = h**((p-1)//q) % p\r\n{p, q, g} may be shared.\r\n\r\nSecond phase (Actuall key gen)\r\n-------------------------------\r\n1. Choose private key x such that 0 < x < q -1\r\n2. Compute public key y, y = g**x % p\r\n\r\n                            Signing\r\n------------------------------------------------------------------------\r\nm = message as an int\r\n1. Choose an integer k randomly from {1... q-1}\r\n2. Compute r = (g**k % p) % q\r\n3. Compute s = ((k**-1 % q) * (H(m) + x * r)) % q\r\n4. If s == 0 or r == 0: start over with a different k\r\nSig = (r, s)\r\n                            Verifying\r\n------------------------------------------------------------------------\r\nm = message as an int\r\n1. Verify that 0 < r < q, 0 < s < q\r\n2. Compute W = s**-1 % q\r\n3. Compute U1 = (H(m) * w) % q\r\n4. Compute U2 = (r * w) % q\r\n5. Compute V = (((g**u1 % p) * (y**u2 % p)) % p) % q\r\nThe signature is valid if and only if V == r\r\n------------------------------------------------------------------------\r\n                                \r\n                                \r\n                                Example\r\n------------------------------------------------------------------------\r\n// key param gen\r\np, q = 11, 5 // (p -1) % q == 0\r\nh = 8\r\ng = 8**2 % 11 = 9\r\n{11, 5, 9}\r\n// make a key\r\nx = 7\r\ny = 9**7 % 11 = 4\r\n\r\n// sign\r\nm = 3\r\nH(M) = 7\r\nk = 2\r\nr = (9**2 % 11) % 5 = 4\r\ns = ((2**-1 % 5) * (7 + 7 * 4)) % 5 = (3 * 56) % 5 = 3\r\n{r = 4, s = 3}\r\n\r\n// verify\r\nm = 3\r\nH(M) = 7\r\nW = 3**-1 % 5 = 2\r\nU1 = (7 * 2) % 5 = 4\r\nU2 = (4 * 2) % 5 = 3\r\nV = (((5 ** 4 % 11) *  (4 ** 3 % 11)) % 11) % 5 = 4\r\n\r\nV == r -> the message is authentic\r\n\r\n\r\n\r\n```\r\n\r\n\r\n## DSA Code\r\n**WARNING:** I made this with some questionable decisions, this algorithm is complex, please use [PyCryptodome implementation](https://pycryptodome.readthedocs.io/en/latest/src/public_key/dsa.html) or use [RSA](#rsa) instead.\r\n\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey.DSA import DSA\r\n\r\nmessage: bytes = b\"DSA test\"\r\n\r\n# Key generation, Will take longer if the nBit is large and is not 1024, 2048, or 3072\r\n# An extra bool (use_precalculated) is equal to true, this is to save time, you can turn it off though.\r\npriv, pub = DSA.generate_key_pair(1024)\r\nprint(priv)\r\nprint(pub)\r\n\r\n# Sign\r\ncipher = DSA(priv)\r\nsig = cipher.sign(message)\r\ncipher.verify(sig, message)\r\n# Verify (Will throw and error if it isn't auth)\r\n```\r\n**WARNING:** The exportation process is dumping it to JSON, then XOR it with the pwd.\r\nThe HMAC is then put before it.\r\n\r\n\r\nYou can export and load keys like this:\r\n\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey.DSA import DSA, DSAKey\r\n\r\npriv, pub = DSA.generate_key_pair()\r\npriv.export(file_name=\"file_name.txt\", pwd=b\"test\")\r\nDSAKey.load(file_name=\"file_name.txt\", pwd=b\"test\")\r\n# load will throw an assertion error if the HMACs aren't the same\r\n```\r\n\r\n\r\n# ECC\r\nElliptic Curve Cryptography.\r\n\r\nECC in an approach to asymmetric cryptography with the hardest math concepts.\r\n\r\n**Note:** ECC protocols like ECDH aren't in the .Protocols module but in the ECC module.\r\n\r\n[Computerphile](https://www.youtube.com/watch?v=NF1pwjL9-DE)\r\n\r\n## ECC Math\r\n[Wiki](https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication)\r\n\r\nWe first define the curve using this equation y^2 = x^3 + ax + b .\r\n\r\nNow we pick a prime modulus P and a generator point G.\r\n\r\n```\r\n\r\nKey Gen\r\n-----------\r\n\r\nx = random int\r\ny = G * x\r\nPrivate: {x}\r\nPublic: {y}\r\n\r\nECDH\r\n-----------\r\nAx, AY\r\nBx, By\r\n\r\nAlice shared key:\r\nAx * By\r\nBob shared key:\r\nBx * AY\r\n\r\n\r\nEncryption\r\n-------------\r\nYou can't just encrypt with ECC but you can make an hybrid scheme (ECIES).\r\n\r\nr = random\r\nR = G * r\r\nS = Y * r\r\nsk = KDF(S)\r\nc = Encrypt(msg, sk)\r\nsend c, R\r\n\r\nDecryption\r\n------------\r\ns = R * x\r\nsk = KDF(s)\r\nmsg = Decrypt(c, sk)\r\n\r\n```\r\n\r\n\r\n\r\n\r\nPoint addition is done like this (P + Q = R)\r\n\r\n![Image](https://github.com/RoyNisimov/AsymmetricEncryption/assets/95145981/3a79c131-7a8f-4b63-97d9-14b6a3699412)\r\n\r\nMultiplying a point by itself it's addition but drawing a tangent.\r\n\r\n![Image](https://media.springernature.com/lw685/springer-static/image/chp%3A10.1007%2F978-981-16-7018-3_46/MediaObjects/511640_1_En_46_Fig2_HTML.png)\r\n\r\n[Signatures](https://www.youtube.com/watch?v=r9hJiDrtukI)\r\n\r\n\r\n## ECC code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey.ECC import ECKey, ECDH, ECSchnorr, ECIES, EllipticCurveNISTP256\r\n\r\ncurve = EllipticCurveNISTP256.get_curve()\r\n# key pair gen\r\nkey_pair = ECKey.new(curve)\r\npriv = key_pair.private_key  # int\r\npub = key_pair.public_key  # ECPoint\r\n\r\n# ECDH\r\n\r\nkeyA = ECKey.new(curve)\r\necdh = ECDH(keyA)\r\nA = keyA.public_key\r\n\r\nkeyB = ECKey.new(curve)\r\nB = keyB.public_key\r\n\r\nshared_key_alice = ecdh.Stage1(B)\r\n\r\nshared_key_bob = ECDH.Stage2(keyB, A)\r\n\r\nprint(shared_key_alice)\r\nprint(shared_key_bob)\r\n\r\nassert shared_key_alice == shared_key_bob\r\n\r\n# ECIES\r\nkeyPair = ECKey.new(EllipticCurveNISTP256.get_curve())\r\nmsg = b\"test\"\r\nc = ECIES.encrypt(msg, keyPair.public_key)\r\nprint(c)\r\nd = ECIES.decrypt(c, keyPair)\r\nprint(d)\r\nassert d == msg\r\n\r\n# Schnorr signing\r\nkey = ECKey.new(curve)\r\nsigner = ECSchnorr(key)\r\nmsg = b\"test\"\r\nsignature = signer.sign(msg)\r\nverify = ECSchnorr.verify(signature, msg, key.public_key)\r\nprint(verify)\r\n\r\n# export\r\nkey = ECKey.new(curve)\r\nkey.export(\"key.key\", b\"password\") # there's also an encryption function variable (XOR right now)\r\nnew_key = ECKey.load(\"key.key\", b\"password\")\r\nassert new_key == key\r\n```\r\n\r\n# DLIES\r\nDiscrete Logarithm Integrated Encryption Scheme is an encryption scheme.\r\n\r\nIt's basically [Diffie-Hellman](#diffie-hellman) with encryption.\r\n\r\n# DLIES Math\r\n``` \r\n# key gen\r\nlet n be a large prime\r\nlet g < n - 1\r\nlet x < n - 1 \r\nlet y = g**x % n\r\nPrivate: {x}, Public: {n, g, y}\r\n# encryption\r\n{E(m, S) -> symmetric encryption function, m}\r\nlet r be a random value\r\nlet R = g**r % n\r\nlet S = y**r % n\r\nlet E = E(m, S)\r\nciphertxt: (E, R)\r\n\r\n# decryption\r\n{D(m, S) -> corresponding decryption function, (E, R)}\r\nS = R**x % n\r\nm = D(E, S)\r\n\r\n```\r\n\r\n# DLIES Code\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey import DLIESKey, DLIES\r\n\r\nkey = DLIESKey.new(1024)\r\nmsg = b\"test\"\r\n\r\nc = DLIES.encrypt(key.public, msg)\r\nd = DLIES.decrypt(key, c)\r\n\r\nprint(c)\r\nprint(d)\r\n# export\r\npriv, pub = DLIES.generate_key_pair(2048)\r\npriv.export(\"key.key\", b\"password\") # there's also an encryption function variable (XOR right now)\r\nnew_key = DLIESKey.load(\"key.key\", b\"password\")\r\nassert new_key == priv\r\n```\r\n\r\n\r\n\r\n# Learning With Errors\r\nLWE is a [post quantum cryptography algorithm](https://en.wikipedia.org/wiki/Post-quantum_cryptography)\r\n\r\n[Video](https://www.youtube.com/watch?v=K026C5YaB3A)\r\n\r\n# LWE Math\r\n[Video](https://www.youtube.com/watch?v=MBdKvBA5vrw)\r\n\r\n```\r\nFirst define a vector S this will be your private key then we need to define A.\r\nA is a group of random vectors that contain random numbers up to a prime number q.\r\nThen we define E (the errors in Learning With Errors).\r\n\r\nE contains numbers between 1-4 including 1 and 4. \r\n\r\nA helper function is this: \r\ndef helper(list, s):\r\n    return sum([(s[i] * list[i]) % q for i in range(len(list))])\r\n\r\nNow we can use our variables to define B using this formula: B[i] = helper(A, S) + E[i] (mod q)\r\n\r\nSo for example A={{80, 5, 6}, {86, 19, 17}...}, S={5, 2, 4}, Q=97 E={3,3,4...}\r\n\r\nSo B1 will be equal to (80 * 5) + (5 * 2) + (6 * 4) + 3 (mod 97) = 49.\r\n\r\nTo encrypt a bit (m in [0, 1]) we define u and v \r\n\r\n\r\ndef sum_u(l) -> list[int]:\r\n    result_arr: list[int] = [0] * len(l[0])\r\n    for i in range(len(l)):\r\n        for c in range(len(result_arr)):\r\n            result_arr[c] += l[i][c]\r\n    return result_arr\r\n    \r\nThe sum_u function sums everything in a list[list] into a list[]\r\nFor example:\r\nl = [[10, 20], [50, 30], [10, 20]]\r\nSo the answer of sum_u(l) is [70, 70]\r\n\r\nu = sum_u(A samples)\r\nv = (sum(B samples) - Q\u00c3\u00b72 * M) % q\r\n\r\nTo decrypt we take v and u to this formula dec = v - helper(u, s) (mod q)\r\nIf dec is less than q \u00c3\u00b7 2 then M=0\r\nIf dec is more then q \u00c3\u00b7 2 then M=1\r\n\r\nThis isn't gurrenteed to work 100% of the time!\r\n```\r\nWatch [this video](https://www.youtube.com/watch?v=K026C5YaB3A) for a much better explanation.\r\n\r\n# LWE Code\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey.LWE import LWEKey, LWE\r\n\r\nif __name__ == '__main__':\r\n    # generation\r\n    key_pair = LWEKey.new(128)\r\n    # encryption\r\n    m = b\"test\"\r\n    cipher = LWE(key_pair)\r\n    ciphertxt = LWE.encrypt_message(key_pair.public, m)\r\n    plaintext = cipher.decrypt_message(ciphertxt)\r\n    print(plaintext)\r\n    assert plaintext == m\r\n    \r\n    # exportation\r\n    key_pair.export(\"test.txt\", b\"super secret\")\r\n    new_key = LWEKey.load(\"test.txt\", b\"super secret\")\r\n    assert new_key == key_pair\r\n```\r\n\r\n# Protocols\r\n\r\n- [Diffie-Hellman](#diffie-hellman)\r\n- [OAEP](#oaep)\r\n- [SSS](#sss)\r\n- [Zero Knowledge](#fiat-shamir-zero-knowledge-proof)\r\n- [Oblivious Transfer](#oblivious-transfer)\r\n\r\n# Diffie-Hellman\r\nDiffie-Hellman (commonly referred to as DH) is an essential [key exchange protocol](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange).\r\nDH is a mathematical method of securely exchanging cryptographic keys over a public channel and was one of the first public-key protocols as conceived by Ralph Merkle and named after Whitfield Diffie and Martin Hellman. DH is one of the earliest practical examples of public key exchange implemented within the field of cryptography. Published in 1976 by Diffie and Hellman, this is the earliest publicly known work that proposed the idea of a private key and a corresponding public key.\r\n\r\n[Computerphile (without math)](https://www.youtube.com/watch?v=NmM9HA2MQGI)\r\n\r\n[Computerphile (with math)](https://www.youtube.com/watch?v=Yjrfm_oRO0w)\r\n\r\n**WARNING:** This protocol here doesn't involve authentication, in real DH every party signs their messages before sending.\r\n\r\n\r\n## DH Math\r\n``` \r\nAlice and Bob publicly agree on p (large prime) and g (g < p)\r\n\r\n\r\n|        Alice       |        Public       |        Bob       |     \r\n----------------------------------------------------------------\r\n|      {a, g, p}     |     g, p            |      {b, g, p}   |\r\n|                    |                     |                  |\r\n|     A = g**a % p   |   A ----------->    |     k = A**b % p |\r\n|                    |                     |                  |\r\n|   k = A**b % p     |     <----------- B  |     B = g**b % p |\r\n|                    |                     |                  |\r\n----------------------------------------------------------------\r\nk is the shared symmetric secret.\r\ng**a**b % p == g**(a * b) % p\r\n\r\n```\r\n\r\n## DH Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey.RSA import RSA\r\nfrom AsymmetricEncryptions.Protocols import DiffieHellman\r\n\r\nApriv, Apub = RSA.generate_key_pair(1024)\r\nBpriv, Bpub = RSA.generate_key_pair(1024)\r\nDH = DiffieHellman.new(Apriv, 1024)\r\n# Alice\r\nA = DH.Stage1()\r\ngp = DH.get_gp()\r\n# send A and gp to Bob\r\n\r\n# Bob\r\nB, Bob_shared = DiffieHellman.Stage2(gp, Bpriv, A)\r\nprint(Bob_shared)\r\n# send B to Alice\r\n\r\n# Alice\r\nAlice_shared = DH.Stage3(B)\r\nprint(Alice_shared)\r\nprint(Alice_shared == Bob_shared)\r\n\r\n```\r\n\r\n# Yak\r\n[Wiki](https://en.wikipedia.org/wiki/YAK_(cryptography))\r\n\r\nThe YAK is a public-key authenticated key-agreement protocol, proposed by Feng Hao in 2010. It is claimed to be the simplest authenticated key exchange protocol among the related schemes, including MQV, HMQV, Station-to-Station protocol, SSL/TLS etc. The authentication is based on public key pairs. As with other protocols, YAK normally requires a Public Key Infrastructure to distribute authentic public keys to the communicating parties. The security of YAK is disputed (see below and the talk page).\r\n\r\n## Yak Math\r\nSee [Wiki description](https://en.wikipedia.org/wiki/YAK_(cryptography))\r\n## Yak Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Protocols.YAK import YAK\r\nfrom AsymmetricEncryptions.PublicPrivateKey import DLIESKey\r\n\r\n\r\nyakAlice = YAK.new(2048)\r\ngq = yakAlice.get_gq()\r\nyakBob = YAK(*gq)\r\ng, q = gq\r\n\r\nAlicePriv = DLIESKey.build(g, q)\r\nAlicePub = AlicePriv.public\r\nBobPriv = DLIESKey.build(g, q)\r\nBobPub = BobPriv.public\r\n\r\nx, AliceSend = yakAlice.stage_1()\r\ny, BobSend = yakBob.stage_1()\r\n\r\nsharedAlice = yakAlice.stage_2(x, AlicePriv, BobSend, BobPub)\r\nsharedBob = yakBob.stage_2(y, BobPriv, AliceSend, AlicePub)\r\nprint(sharedAlice)\r\nprint(sharedBob)\r\nassert sharedAlice == sharedBob\r\n```\r\n\r\n# MQV\r\n[Wiki](https://en.wikipedia.org/wiki/MQV)\r\n\r\nMQV (Menezes\u00e2\u20ac\u201cQu\u00e2\u20ac\u201cVanstone) is an authenticated protocol for key agreement based on the Diffie\u00e2\u20ac\u201cHellman scheme. Like other authenticated Diffie\u00e2\u20ac\u201cHellman schemes, MQV provides protection against an active attacker. The protocol can be modified to work in an arbitrary finite group, and, in particular, elliptic curve groups, where it is known as elliptic curve MQV (ECMQV).\r\n\r\n## MQV Math\r\nSee [Wiki description](https://en.wikipedia.org/wiki/MQV)\r\nSee [Video](https://www.youtube.com/watch?v=JKlTdY07IY4)\r\n## MQV Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.PublicPrivateKey.ECC import ECMQV, ECKey, EllipticCurveNISTP256\r\n\r\na = ECKey.new(EllipticCurveNISTP256.get_curve())\r\nb = ECKey.new(EllipticCurveNISTP256.get_curve())\r\n\r\nx = ECMQV.Stage1n2(EllipticCurveNISTP256.get_curve())\r\ny = ECMQV.Stage1n2(EllipticCurveNISTP256.get_curve())\r\n\r\nSa = ECMQV.Stage3n4(a, x)\r\nSb = ECMQV.Stage3n4(b, y)\r\n\r\nkeyA = ECMQV.Stage5(b.get_public_key(), Sa, y.get_public_key())\r\nkeyB = ECMQV.Stage5(a.get_public_key(), Sb, x.get_public_key())\r\n\r\nprint(keyA)\r\nprint(keyB)\r\nassert keyA == keyB\r\n```\r\n\r\n\r\n\r\n\r\n# PKCS7\r\nPadding for symmetric algorithms\r\n\r\n## PKCS7 Math\r\n[Video](https://www.youtube.com/watch?v=3OTMLUEPZUc)\r\n\r\n## PKCS7 Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Protocols import PKCS7\r\n\r\nmsg = b\"PKCS7\"\r\nblock_size = 32\r\npadded = PKCS7(block_size).pad(msg)\r\nprint(padded)\r\nunpadded = PKCS7(block_size).unpad(padded)\r\nprint(unpadded)\r\nprint(unpadded == msg)  # True if the msg is small \r\n```\r\n\r\n\r\n# OAEP\r\n```\r\nO-ptimal\r\nA-symmetric\r\nE-ncryption\r\nP-adding\r\n```\r\n[Here](https://www.youtube.com/watch?v=ZwPGE5GgG_E) and [Here](https://www.youtube.com/watch?v=bU4so01qMP4)\r\n## OAEP Math\r\n![image](https://miro.medium.com/v2/resize:fit:1358/1*ppgvPx2BA-Il2w9aZhRrWw.png)\r\n```\r\nG(x) |\r\n     | -> hash functions outputing g and h bits\r\nH(H) |\r\n\r\nPad\r\n================================\r\nr -> random nonce of g bits\r\nm = m || 0**(g-len(m))\r\nx = m ^ G(r)\r\ny = H(X) ^ r\r\np =  x || y\r\nremember that x and y are g and h bits long\r\n--------------------------------\r\nUnpad\r\n================================\r\nx = p[:g]\r\ny = p[g:]\r\nr = H(X) ^ y\r\nm = x ^ G(r)\r\n```\r\n\r\n## OAEP Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Protocols import OAEP\r\n\r\nmsg = b\"OAEP\"\r\npadded = OAEP.oaep_pad(msg)\r\nprint(padded)\r\nunpadded = OAEP.oaep_unpad(padded)\r\nprint(unpadded)\r\nprint(unpadded == msg)  # True if the msg is small \r\n```\r\n\r\n# SSS\r\n\r\nSSS stands for Shamir's Secret Sharing.\r\n\r\nAdi Shamir, an Israeli scientist, first formulated the scheme in 1979.\r\n\r\nBTW the S in RSA is also Adi Shamir.\r\n\r\n[This video here](https://www.youtube.com/watch?v=iFY5SyY3IMQ) explains SSS very well.\r\n\r\nPlease check out [Pycryptodome's SSS](https://pycryptodome.readthedocs.io/en/latest/src/protocol/ss.html).\r\n\r\nSSS is used to secure a secret in a distributed form, most often to secure encryption keys. The secret is split into multiple shares, which individually do not give any information about the secret.\r\n\r\nTo reconstruct a secret secured by SSS, a number of shares is needed, called the threshold. No information about the secret can be gained from any number of shares below the threshold (a property called perfect secrecy). In this sense, SSS is a generalisation of the one-time pad (which can be viewed as SSS with a two-share threshold and two shares in total).\r\n\r\n## SSS Math\r\n\r\n``` \r\nShamir's Secret Sharing\r\n---------------------------\r\nn -> the number of shares\r\nt (k in the video above) -> the number of people needed to find the answer\r\n\r\nTake your secret as m.\r\nPlace m on a graph such that the point is (0, m).\r\n\r\nGenerate a polynomial with a0 being m. Like this for example (3 degree polynomial):\r\ny = a0 + a1*x + a2*x**2 + a3*x**3\r\n\r\n\r\nGenerate n points on the polynomial and share them. (x != 0 of course)\r\n-----------------------------\r\nTo find m back you need to calculate the formula again, which you can do that with t shares.\r\nAnd then plug 0 into the formula and you get m.\r\n\r\nBetter m finding method\r\n------------------------\r\nSince we only need to calculate a0 there is and easier formula:\r\n\r\na0 = 0\r\nforeach xj, yj in the points:\r\n    prod = 1\r\n    foreach xi, yi in the points:\r\n        if xi == xj: continue\r\n        prod *= xi/(xi-xj)\r\n    prod *= yj\r\n    a0 + prod\r\n    \r\nExample:\r\n------------------------------\r\nn = 4\r\nt = 2\r\n\r\nm = 3\r\na1 = 4 \r\ny = 4x + 3\r\nrandom point on the line number 1: x = 4, y = 16\r\nrandom point on the line number 2: x = 6, y = 27\r\nrandom point on the line number 3: x = 2, y = 11\r\nrandom point on the line number 4: x = 1, y = 7\r\n\r\n\r\n|\r\n|\r\n|\r\n|\r\n|                               (6, 27)\r\n|\r\n|               (4, 16)\r\n|\r\n|          (2, 11)                         \r\n|   \r\n|       (1, 7)\r\n|\r\n(0; 3)\r\n|\r\n|\r\n0------------------------------------------\r\n\r\nGiven only one of those points it's impossible to find m.\r\nHowever, given t points you can always find m again.\r\n\r\nlet's say that we got both points (1, 7) and (6, 27).\r\nslope = (27 - 7) / (6 - 1) = 20 / 5 = 4\r\nUsing the formula y = m(x - x1) + y1 we get:\r\ny = 4x - 4 + 7, y = 4x + 3.\r\nPluge x = 0 and we get our answer: m = 3.\r\n\r\n```\r\n\r\n## SSS Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Protocols import SSS\r\nimport secrets\r\n\r\n# (3,5) sharing scheme\r\nt, n = 3, 5\r\nsecret = b\"test\"\r\nprint(f'Original Secret: {secret}')\r\n# Phase I: Generation of shares\r\nshares = SSS.generate_shares(n, t, secret)\r\nprint(f'Shares: {\", \".join(str(share) for share in shares)}')\r\n# Phase II: Secret Reconstruction\r\n# Picking t shares randomly for\r\n# reconstruction\r\npool = secrets.SystemRandom().sample(shares, t)\r\nprint(f'Combining shares: {\", \".join(str(share) for share in pool)}')\r\nprint(f'Reconstructed secret: {SSS.reconstruct_secret(pool)}')\r\n```\r\n```\r\nSSS.generate_shares -> returns a list of cordinets as a tuple: list[tuple[int, int]]\r\nsecret: the secret\r\nn: number of shares to generate\r\nt: the number of shares needed to reconstruct the secret\r\n\r\nSSS.reconstruct_secret(shares) -> returns the secret\r\nshares: the shares needed to reconstruct the secret\r\n\r\n\r\n```\r\n\r\n# Fiat Shamir Zero Knowledge Proof\r\n\r\nFrom Wikipedia, the free encyclopedia\r\nIn cryptography, the Fiat\u00e2\u20ac\u201cShamir heuristic is a technique for taking an interactive proof of knowledge and creating a digital signature based on it. This way, some fact (for example, knowledge of a certain secret number) can be publicly proven without revealing underlying information. The technique is due to Amos Fiat and Adi Shamir (1986). For the method to work, the original interactive proof must have the property of being public-coin, i.e. verifier's random coins are made public throughout the proof protocol.\r\n\r\nThe heuristic was originally presented without a proof of security; later, Pointcheval and Stern proved its security against chosen message attacks in the random oracle model, that is, assuming random oracles exist. This result was generalized to the quantum-accessible random oracle (QROM) by Don, Fehr, Majenz and Schaffner, and concurrently by Liu and Zhandry. In the case that random oracles do not exist, the Fiat\u00e2\u20ac\u201cShamir heuristic has been proven insecure by Shafi Goldwasser and Yael Tauman Kalai. The Fiat\u00e2\u20ac\u201cShamir heuristic thus demonstrates a major application of random oracles. More generally, the Fiat\u00e2\u20ac\u201cShamir heuristic may also be viewed as converting a public-coin interactive proof of knowledge into a non-interactive proof of knowledge. If the interactive proof is used as an identification tool, then the non-interactive version can be used directly as a digital signature by using the message as part of the input to the random oracle.\r\n\r\n## Fiat Shamir Math\r\n``` \r\n\r\n---------------------------------------------------\r\nLets say Alice want to prove Bob that she know a password without reveling any information about it.\r\nAlice and Bob agree on a generator g and a prime n such that g < n\r\n\r\nAlice:\r\nx = Hash(Password)\r\ny = g**x % n\r\nv < n\r\nt = g**v % n\r\n\r\nAlice sends to Bob: {y, t}\r\n\r\nBob:\r\nBob generates a number c such that c < n\r\nBob sends Alice: {c}\r\n\r\nAlice:\r\nr = v - c * x\r\nAlice sends Bob: {r}\r\n\r\nBob:\r\nz = (g**r * y**c) % n\r\nverified if z == t\r\n---------------------------------------------------\r\nExample:\r\ng = 35\r\nn = 53\r\n\r\nAlice:\r\nx = 17\r\ny = 35**17 % 53 = 20\r\nv = 7\r\nt = 35**7 % 53 = 34\r\n{y = 20, t = 34}\r\n\r\nBob:\r\nc = 15\r\n{c = 15}\r\n\r\nAlice:\r\nr = 7 - 15 * 17 = -248\r\n{r = -248}\r\n\r\nBob:\r\nz = ((35**-248) * (20**15)) % 53 = 34\r\nt == z = 34 -> verified\r\n```\r\n## Fiat Shamir Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Protocols import FiatShamirZeroKnowledgeProof\r\n\r\nif __name__ == '__main__':\r\n    m = b'test'\r\n    fs = FiatShamirZeroKnowledgeProof.new()\r\n    y = fs.AliceStage1(m)\r\n    # send y to bob\r\n    v, t = fs.AliceStage2()\r\n    # keep v send t\r\n    c = fs.BobStage1()\r\n    # Bob sends c as a challenge\r\n    r = fs.AliceStage3(v, c, m)\r\n    # Alice sends r to bob\r\n    ver = fs.BobStage2(y, r, c, t)\r\n    print(ver)\r\n```\r\n\r\n# Oblivious Transfer\r\n[Wiki](https://en.wikipedia.org/wiki/Oblivious_transfer), [Computerphile](https://www.youtube.com/watch?v=wE5cl8J27Is)\r\n\r\nIn a 1\u00e2\u20ac\u201c2 oblivious transfer protocol, Alice the sender has two messages m0 and m1, and wants to ensure that the receiver only learns one. Bob, the receiver, has a bit b and wishes to receive mb without Alice learning b. The protocol of Even, Goldreich, and Lempel (which the authors attribute partially to Silvio Micali) is general, but can be instantiated using RSA encryption as follows.\r\n\r\n## Oblivious Transfer Math\r\n``` \r\n# Alice\r\n1. Alice has two messages: {m0, m1} and wants to send exactly one of them to Bob. Bob does not want Alice to know which one he receives.\r\n2. Alice generates an RSA key pair {e, n, d: keep private}\r\n3. She also generates two random values, {x0, x1} and sends them to Bob along with her public RSA key.\r\n# Bob gets e, n, x0, x1\r\n4. Bob picks b to be 0 or 1 (b is what message he will get) and selects xb\r\n5. Bob generate a random value k (k < n) and computes:\r\nv = (xb - k**e) % n\r\nBob sends to Alice: {v} and keeps {b, k} a secret.\r\n# Alice gets: {v}\r\n6. Alice computes:\r\nk0 = (v - x0)**d % n\r\nk1 = (v - x1)**d % n\r\n7. \r\nmp0 = (m0 + k0) % n\r\nmp1 = (m1 + k1) % n\r\nAlice sends Bob: {mp0, mp1}\r\n# Bob\r\nmb = (mpb - k) % n\r\nmb == the message that bob chose.\r\n\r\n\r\n```\r\n\r\n## Oblivious Transfer Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Protocols import ObliviousTransfer\r\n\r\n# Alice\r\notProt = ObliviousTransfer(b\"test A\", b\"test B\")\r\nsendBob = otProt.Stage1and2and3()\r\n# Bob\r\nb = int(input(\"Choice 0 or 1: \")) % 2\r\nAlicePubKey = sendBob[0]\r\nsendAlice, keepPrivate = ObliviousTransfer.Stage4and5(sendBob, b)\r\n# Alice\r\nsendBob = otProt.Stage6and7(sendAlice)\r\n# Bob\r\nm = ObliviousTransfer.Stage8(sendBob, keepPrivate, b, AlicePubKey)\r\nprint(m)\r\n```\r\n\r\n# Three Pass Protocol\r\nThe first three-pass protocol was the Shamir three-pass protocol developed circa in 1980. It is also called the Shamir No-Key Protocol because the sender and the receiver do not exchange any keys, however the protocol requires the sender and receiver to have two private keys for encrypting and decrypting messages.\r\n\r\n**WARNING:** the protocol needs external authentication, please authenticate.\r\n\r\n[Spanning Tree's excellent video](https://www.youtube.com/watch?v=I6Unxb-PFhs)\r\n\r\n## TPP Math\r\n``` \r\nlet keyA be a key to a symmetric encryption function EA and decryption function DA\r\nlet keyB be a key to a symmetric encryption function EB and decryption function DB\r\n\r\nc1 = EA(m, keyA)\r\n# Pass to Bob\r\nc2 = EB(c1, keyB)\r\n# Pass to Alice\r\nc3 = DA(c2, keyA)\r\n# Pass to Bob\r\nm = DB(c2, keyB)\r\n\r\n```\r\n\r\n## TPP Code\r\n**WARNING:** An added encryption and decryption function needs to be added. XOR IS UNSAFE IN TPP! \r\n```python\r\nfrom AsymmetricEncryptions.Protocols.ThreePass import ThreePassProtocol\r\n\r\n\r\nif __name__ == '__main__':\r\n    \r\n    def encryption_function(msg: bytes, key: bytes) -> bytes:\r\n        ct = msg\r\n        ... # Actually encrypt\r\n        return ct # send \r\n    \r\n    def decryption_function(ct: bytes, key: bytes) -> bytes:\r\n        msg = ct\r\n        ... # Actually decrypt\r\n        return msg\r\n    \r\n    msg = b\"message\"\r\n    alice = b\"Alice's key\"\r\n\r\n    bob = b\"Bob's key\"\r\n\r\n    TPP = ThreePassProtocol(alice)\r\n    c1 = TPP.stage1(msg, encryption_function)\r\n    print(c1)\r\n    c2 = ThreePassProtocol.stage2(bob, c1, encryption_function)\r\n    print(c2)\r\n    c3 = TPP.stage3(c2, decryption_function)\r\n    print(c3)\r\n    m = ThreePassProtocol.stage4(c3, bob, decryption_function)\r\n    print(m)\r\n    assert m == msg\r\n```\r\n\r\n# Proof of Knowledge\r\nProving that you have a knowledge of a value m.\r\n\r\nThis is using Schnorr non-interactive\r\n\r\n## POK Math\r\n``` \r\nConstruct a new discret log key (n -> prime, g -> generator, x -> m, y = g**x % n).\r\nr -> random value\r\nt = g ** r % n\r\nc = Hash(g | y | t)\r\ns = r + c * x\r\nsend {t, s, new_key.public}\r\n# verify\r\nc = Hash(g | y | t)\r\nif g**s % n == (t * y**c) % n: the proof is true\r\n\r\n```\r\n\r\n\r\n## POK Code\r\n```python\r\nfrom AsymmetricEncryptions.Protocols.SchnorrPOK import POK\r\nfrom AsymmetricEncryptions.PublicPrivateKey import DLIES\r\n\r\npriv, pub = DLIES.generate_key_pair(1024)\r\nm = b\"test\"\r\nprover = POK(priv)\r\nproof = prover.prove(m)\r\nprint(POK.verify(proof))\r\n```\r\n\r\n# Feistel\r\nA feistel network is a symmetric cipher builder.\r\n\r\n[Computerphile](https://www.youtube.com/watch?v=FGhj3CGxl8I)\r\n\r\n## Feistel Code\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Protocols import Feistel\r\n# Here is a built-in Feistel encryption function\r\ndef func(m, k):\r\n    ...\r\n    # Encrypt or hash\r\n    return m\r\nmsg = b\"lol\"\r\nkey = b\"test key\"\r\nf = Feistel(func)\r\nc = f.encrypt(msg, key)\r\nm = f.decrypt(c, key)\r\nprint(c)\r\nprint(m)\r\n```\r\n\r\n\r\n```python\r\nfrom AsymmetricEncryptions.General import Feistel_sha256\r\n# Here is a built-in Feistel encryption function\r\n\r\nmsg = b\"lol\"\r\nkey = b\"test key\"\r\nf = Feistel_sha256.FeistelSha256.get_feistel()\r\nc = f.encrypt(msg, key)\r\nm = f.decrypt(c, key)\r\nprint(c)\r\nprint(m)\r\n\r\n```\r\n\r\n\r\n\r\n# Unhazardous\r\n\r\n# Contents in UH\r\n\r\n| Algorithm              | Code                     |\r\n|------------------------|--------------------------|\r\n| [RSA](#uh-rsa)         | [Code](#uh-rsa-code)     |\r\n| [ElGamal](#uh-elgamal) | [Code](#uh-elgamal-code) |\r\n---\r\n\r\n# UH RSA\r\nTurns RSA into a KEM. with signatures.\r\n\r\n## UH RSA Code\r\n**WARNING:** This uses XOR (Aka OTP), please use a different function like AES.\r\nModify the function by putting `encryption_function=` for encryption and `decryption_function=` for decryption.\r\nThe signature of both functions have to be `(msg or ciphertxt: bytes, key: bytes) -> msg or ciphertxt: bytes`\r\nFor the padding: `padding_block_size=`\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Unhazardous.PublicKey.UHRSA import UHRSA\r\nfrom AsymmetricEncryptions.PublicPrivateKey.RSA import RSA\r\n\r\nAlicesPriv, AlicesPub = RSA.generate_key_pair(2048)  # Alice creates a key pair\r\nBobsPriv, BobsPub = RSA.generate_key_pair(2048)  # Bob creates a key pair\r\n# Alice wants to send bob a message\r\nmsg = b\"Long Message\" * 200\r\ncipherAlice = UHRSA(AlicesPriv)  # Alice inits a UHRSA cipher object\r\nciphertext = cipherAlice.encrypt(BobsPub, msg)  # Alice encrypts using Bob's public key, also creating a signature.\r\nprint(ciphertext)\r\n# Bob gets the ciphertext and Alices public key\r\ncipherBob = UHRSA(BobsPriv)  # Bob makes his own UHRSA cipher object\r\npt = cipherBob.decrypt(ciphertext, AlicesPub)  # Bob verifies the signature and finds the message\r\nprint(pt)\r\nassert pt == msg\r\n```\r\n\r\n\r\n# UH ElGamal\r\nTurns ElGamal into a KEM, with signatures.\r\n\r\n## UH ElGamal Code\r\n**WARNING:** This uses XOR (Aka OTP), please use a different function like AES.\r\nModify the function by putting `encryption_function=` for encryption and `decryption_function=` for decryption.\r\nThe signature of both functions have to be `(msg or ciphertxt: bytes, key: bytes) -> msg or ciphertxt: bytes`\r\nFor the padding: `padding_block_size=`\r\n\r\n```python\r\nfrom AsymmetricEncryptions.Unhazardous.PublicKey.UHElGamal import UHElGamal\r\nfrom AsymmetricEncryptions.PublicPrivateKey.ElGamal import ElGamal\r\n\r\nAlicesPriv, AlicesPub = ElGamal.generate_key_pair(2048) # Alice's keys\r\nBobsPriv, BobsPub = ElGamal.generate_key_pair(2048) # Bob's keys\r\nmsg = b\"test\" # Message\r\ncipherAlice = UHElGamal(AlicesPriv)\r\nct = cipherAlice.encrypt(BobsPub, msg) # Encrypts and signs\r\ncipherBob = UHElGamal(BobsPriv)\r\npt = cipherBob.decrypt(ct, AlicesPub) # Decrypts and verify\r\nassert pt == msg\r\nprint(ct)\r\nprint(pt)\r\n```\r\n\r\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A simple asymmetric encryption module",
    "version": "0.1.6",
    "project_urls": {
        "Homepage": "https://github.com/RoyNisimov/AsymmetricEncryption"
    },
    "split_keywords": [
        "python",
        " cipher",
        " asymmetric encryptions",
        " signing",
        " verifying",
        " protocols",
        " encryption",
        " decryption"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b6593a2a3489c99af8f7f64fb510d6c23b7a05da262fde8d5ee1999c611c393b",
                "md5": "ef6de2f8142c7f590b17790bb0f91761",
                "sha256": "62baf46777f710ce219e95a3238dbae17c396bd524daa2e1b677d6e925d634c9"
            },
            "downloads": -1,
            "filename": "asymmetric_encryption-0.1.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ef6de2f8142c7f590b17790bb0f91761",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 55610,
            "upload_time": "2024-03-21T12:43:24",
            "upload_time_iso_8601": "2024-03-21T12:43:24.323705Z",
            "url": "https://files.pythonhosted.org/packages/b6/59/3a2a3489c99af8f7f64fb510d6c23b7a05da262fde8d5ee1999c611c393b/asymmetric_encryption-0.1.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2877b80c14245ebf1c5169289ef0b864c4ed87ed1c0cdd525827e78fa5c4db6c",
                "md5": "c405d52bdffd69083d3171907f707b7c",
                "sha256": "8bf32eebf735201b2bf73859e01827714e0ffba922bc693a9f0d9611ef4ffb55"
            },
            "downloads": -1,
            "filename": "asymmetric-encryption-0.1.6.tar.gz",
            "has_sig": false,
            "md5_digest": "c405d52bdffd69083d3171907f707b7c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 62743,
            "upload_time": "2024-03-21T12:43:26",
            "upload_time_iso_8601": "2024-03-21T12:43:26.393954Z",
            "url": "https://files.pythonhosted.org/packages/28/77/b80c14245ebf1c5169289ef0b864c4ed87ed1c0cdd525827e78fa5c4db6c/asymmetric-encryption-0.1.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-21 12:43:26",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "RoyNisimov",
    "github_project": "AsymmetricEncryption",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "asymmetric-encryption"
}
        
Elapsed time: 0.24664s