Skip to content

Commit

Permalink
Merge pull request #8 from Bl4omArchie/binary
Browse files Browse the repository at this point in the history
Binary
  • Loading branch information
Bl4omArchie authored Aug 20, 2023
2 parents e885d6d + 496a566 commit cf56bfc
Show file tree
Hide file tree
Showing 20 changed files with 242 additions and 122 deletions.
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

This library aim to generate an RSA keypair in python.
Other features like encryption, decryption and prime_factor recovery are available.
I've started this project in the purpose of training myself to implemente cryptographic algorithm. In consequence, this library do NOT aim for a profesional purpose.
I've started this project in the purpose of training myself to implemente cryptographic algorithm. In consequence, you shall not use it for real life purpose.


## Table of contents
Table of contents:
- [Pubcrypt ~ RSA keypair](#pubcrypt--rsa-keypair)
- [Table of contents](#table-of-contents)
- [Installation](#installation)
- [Documentation](#documentation)
- [Benchmark](#benchmark)
- [Features](#features)
- [Version](#version)
- [Author](#author)
- [References](#references)
- [Features](#features)
- [Version](#version)
- [Author](#author)
- [References](#references)

## Installation

Expand Down Expand Up @@ -93,17 +92,22 @@ A more precise description is available below each function

## Benchmark

Refactoring...
This benchmark intend to evaluate the effiency of Pubcrypt's function
With only two simples class: GraphVisualization and EffiencyProfile, I have access to a good overview of the performance.

## Features
- GraphVisualization allow me to generate a graph that plot the times of execution for one or severals function.
- EffiencyProfile generate a profile with the library cProfile which is specialised in examining function in details. It give me information about witch modules are called in the function, how many I have been calling them and the final generation time.
This evaluation is more accurated for huge function that used many external packages.

Main product:
Find two samples in the benchmark.py file.

# Features
- RSA keypair generator
- RSA message encryption and decryption
- RSA prime factors recovery


## Version
# Version

| Version | Description |
| :--------------: |:---------------:|
Expand All @@ -113,13 +117,13 @@ Main product:
| v1.3 | correction of the get_prime_factor function |


## Author
# Author
You can contact me and see my work here:
- Blog: https://bl4omarchie.github.io/archX/
- Discord server: https://discord.com/invite/D2wGP62
- Twitter: https://twitter.com/Bl4om_Archie

## References
# References
- [NIST FIPS 186-4: Digital Signature Standard (DSS)](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.186-4.pdf)
- [NIST SP 800-56Br2: Recommendation for Pair-Wise Key Establishment Using Integer Factorization Cryptography](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf)
- [PKCS #1 Version 2.2: RSA Cryptography Specifications draft-moriarty-pkcs1-01](https://datatracker.ietf.org/doc/pdf/draft-moriarty-pkcs1-01.pdf)
Expand Down
56 changes: 56 additions & 0 deletions benchmark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from pubcrypt.cryptosystem.rsa import *
from pubcrypt.number.primality import *
from pubcrypt.number.util import *
from benchmark.plotting import *
from benchmark.profiler import *

import random, math, os


"""
This benchmark intend to evaluate the effiency of Pubcrypt's function
With only two simples class: GraphVisualization and EffiencyProfile, I have access to a good overview of the performance.
- GraphVisualization allow me to generate a graph that plot the times of execution for one or severals function.
- EffiencyProfile generate a profile with the library cProfile which is specialised in examining function in details. It give me information about witch modules are called in the function, how many I have been calling them and the final generation time.
This evaluation is more accurated for huge function that used many external packages.
There is two examples:
"""


def plotting_sample():
obj = GraphVisualization("Function generate")
obj.measure_execution_time(100, generate, 2048)
obj.plot_data(["green"], ["generate()"], show_stats=True)

def profile_sample():
obj = EffiencyProfile(generate)
obj.create_profile(2048)
obj.read_profile()

def gcd_test():
n = 5000
obj = GraphVisualization("GCD comparison")
obj.measure_execution_time(n, gcd, random.randint(2**2048, 2**2049), random.randint(2**2048, 2**2049))
obj.measure_execution_time(n, math.gcd, random.randint(2**2048, 2**2049), random.randint(2**2048, 2**2049))
obj.plot_data(["green", "red"], ["gcd()", "GCD()"], show_stats=True)

def pow_test():
n = 5000
obj = GraphVisualization("Fast exponentiation comparison")
obj.measure_execution_time(n, pow, random.randint(2**2048, 2**2049), random.randint(2**2048, 2**2049), random.randint(2**2048, 2**2049))
obj.measure_execution_time(n, pow_fast, random.randint(2**2048, 2**2049), random.randint(2**2048, 2**2049), random.randint(2**2048, 2**2049))
obj.plot_data(["green", "red"], ["pow()", "pow_fast()"], show_stats=True)

def rng_test():
n = 1000
obj = GraphVisualization("RNG comparison")
obj.measure_execution_time(n, random.getrandbits, 2048)
obj.measure_execution_time(n, random.randint, 2**2048, 2**2049)
obj.measure_execution_time(n, random.randrange, 2**2048, 2**2049)
obj.measure_execution_time(n, os.urandom, 2048)
obj.plot_data(["green", "red", "blue", "purple"], ["getrandbits()", "randint()", "randrange()", "urandom()"], show_stats=False)

if __name__ == "__main__":
plotting_sample()
Empty file added benchmark/__init__.py
Empty file.
12 changes: 0 additions & 12 deletions benchmark/generate_bench.py

This file was deleted.

13 changes: 0 additions & 13 deletions benchmark/graph.py

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/graph/Function generate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/graph/GCD comparison.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added benchmark/graph/RNG comparison.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file removed benchmark/picture/Generate function.png
Binary file not shown.
49 changes: 49 additions & 0 deletions benchmark/plotting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import matplotlib.pyplot as plt
import numpy as np
import time


class GraphVisualization:
def __init__(self, title, size=(15, 13)) -> None:
self.title = title
self.size = size
self.execution_times = []
self.path = "benchmark/graph/"


def measure_execution_time(self, n, func, *args, **kwargs):
func_times = []
for _ in range(n):
start = time.time()
func(*args, **kwargs)
func_times.append(time.time() - start)
self.execution_times.append(func_times)


def plot_data(self, colors, labels, graph_type='lines', show_stats=False):
if len(self.execution_times) != len(colors) or len(self.execution_times) != len(labels):
raise ValueError("Lengths of execution_times, colors, and labels should be the same.")

if graph_type not in ['lines', 'scatter']:
raise ValueError("Invalid graph_type. Choose from 'lines' or 'scatter'")

plt.figure(figsize=self.size)
for i in range(len(self.execution_times)):
data_chunk_size = len(self.execution_times[i])
if graph_type == 'lines':
plt.plot(np.linspace(0, data_chunk_size, data_chunk_size), self.execution_times[i], color=colors[i], label=labels[i])

if show_stats:
min_value = np.min(self.execution_times[i])
max_value = np.max(self.execution_times[i])
mean_value = np.mean(self.execution_times[i])
plt.text(data_chunk_size, min_value, f"Min: {min_value:.10f}", verticalalignment='top', horizontalalignment='left')
plt.text(data_chunk_size, max_value, f"Max: {max_value:.10f}", verticalalignment='top', horizontalalignment='right')
plt.text(data_chunk_size, mean_value, f"Mean: {mean_value:.10f}", verticalalignment='top', horizontalalignment='right')

plt.title(self.title)
plt.xlabel('Test Iteration')
plt.ylabel('Elapsed Time (seconds)')
plt.legend()
plt.grid(True)
plt.savefig(self.path + self.title)
25 changes: 25 additions & 0 deletions benchmark/profiler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import cProfile, pstats
import numpy as np
import matplotlib.pyplot as plt


path = "benchmark/profiles/"
sufix = ".ep"

class EffiencyProfile:
def __init__(self, func):
self.func = func
self.size = (10, 8)
self.func_name = str(func)
self.profile_file = path + f"{self.func_name}" + sufix

def create_profile(self, *args, **kwargs):
profiler = cProfile.Profile()
profiler.enable()
self.func(*args, **kwargs)
profiler.disable()
profiler.dump_stats(self.profile_file)

def read_profile(self):
stats = pstats.Stats(self.profile_file)
stats.print_stats()
Binary file not shown.
Binary file not shown.
19 changes: 9 additions & 10 deletions pubcrypt/cryptosystem/rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ def generate(nBits, e=65537):

elif e%2 == 0 or not pow(2, 16) <= e <= pow(2, 256):
raise ValueError("Incorrect puclic exponent. e must be odd and in the range [2^16, 2^256]")

pBits = nBits//2

p, q = get_prime_factor(pBits, e)
Expand All @@ -18,31 +17,31 @@ def generate(nBits, e=65537):

if pair_wise_consistency_test(n, e, d) == 0:
raise ValueError("Error, please retry. Consistency test failed")

return n, e, d


def primitive_exp(m, exp, n):
""" This function represent the encryption/decryption/signature operation """
if 0 < m < n-1:
return pow_mod(m, exp, n)
return pow(m, exp, n)

else:
raise ValueError("Data representative out of range")


def prime_recovery(n, e, d):
""" Recover the primes factor p and q that compose N """
a = (d*e-1) * gcd(n-1, d*e-1)
m = a//n
r = a - m*n
b = (n-r) // (m+1) +1
a = (d * e - 1) * gcd(n - 1, d * e - 1)
m = a // n
r = a - m * n
b = (n - r) // (m + 1) + 1

if pow(b, 2) <= 4*n:
if pow(b, 2) <= (n << 2):
raise ValueError("Error")

y = isqrt(pow(b, 2)-4*n)
return (b+y) // 2, (b-y) // 2
y = isqrt(pow(b, 2) - (n << 2))
return (b + y) >> 1, (b - y) >> 1



def pair_wise_consistency_test(n, e, d):
Expand Down
35 changes: 18 additions & 17 deletions pubcrypt/number/primality.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,50 @@
from pubcrypt.number.util import gcd, isqrt
from pubcrypt.number.util import RNG, pow_mod
from random import randint
from pubcrypt.number.util import *
from random import randrange, getrandbits


round = 5

def get_prime_factor(pBits, e):
""" Generate a prime factor """
""" Generate a prime factor """
i = 0
candidate = 0

while candidate == 0:
while i<5*pBits:
p = RNG(pBits)
if p%2 == 0:
p += 1
while i < (5 * pBits):
p = getrandbits(pBits)
if p & 1 == 0: #if the number is odd, add one
p |= 1

if p >= isqrt(2)*(pow(2, pBits-1)):
if p >= (isqrt(2) << (pBits - 1)):
if gcd(p-1, e) == 1:
candidate = miller_rabin(p, 5)
candidate = miller_rabin(p, round)
break
i += 1

i = 0
candidate = 0
while candidate == 0:
while i<5*pBits:
q = RNG(pBits)
if q%2 == 0:
q += 1
q = getrandbits(pBits)
if q & 1 == 0:
q |= 1

if (abs(p-q) > pow(2, pBits/2-100)) or q >= isqrt(2)*(pow(2, pBits-1)):
if (abs(p-q) > pow(2, (pBits//2)-100)) or (q >= (isqrt(2) << (pBits - 1))):
if gcd(q-1, e) == 1:
candidate = miller_rabin(q, 5)
candidate = miller_rabin(q, round)
break
i += 1
return p, q


def miller_rabin(p, r):
s, d = 0, p - 1
while d % 2 == 0:
while d & 1 == 0:
d >>= 1
s += 1

for _ in range(r):
a = randint(2, p - 2)
a = randrange(2, p - 2)
x = pow(a, d, p)
if x == 1 or x == p - 1:
continue
Expand Down
Loading

0 comments on commit cf56bfc

Please sign in to comment.