summaryrefslogtreecommitdiff
path: root/hw3/carpenter_adam_hw3.py
blob: 050afc5b2eaee9235ec8954ce83b0e65a9f3980e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/python2.7
# Authored by Adam Carpenter
# Initial code base authored by Prof. Dmitry Evtyushkin

import random
import time
from mr import is_probable_prime as is_prime

def int_to_bin(i):
    o = '{0:08b}'.format(i)
    o = (8 - (len(o) % 8)) * '0' + o
    return o

# def int_to_bin(i):
#     # deprecated; incorrect output
#     return "{0:08b}".format(i)

def bin_to_int(b):
    return int(b,2)

def str_to_bin(s):
    o = ''
    for i in s:
        o += '{0:08b}'.format(ord(i))
    return o

def bin_to_str(b):
    l = [b[i:i+8] for i in xrange(0,len(b),8)]
    o = ''
    for i in l:
        o+=chr(int(i,2))
    return o

def egcd(a, b): # can be used to test if numbers are co-primes
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)
        #if g==1, the numbers are co-prime

def modinv(a, m):
    #returns multiplicative modular inverse of a in mod m
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

def sxor(a, b):
    # returns the exclusive-or result of two strings/characters
    if a == b:
        return "0"
    else:
        return "1"

def srshift(a, b = "0"):
    # performs right-shift-like operation on a string;
    # b serves as the replacement most-significant bit
    return b + a[:-1]

class lfsr:
    # Class implementing linear feedback shift register. Please note that it operates
    # with binary strings, strings of '0's and '1's.
    taps = []

    def __init__ (self, taps):
        # Receives a list of taps. Taps are the bit positions that are XOR-ed
        # together and provided to the input of lfsr

        globals()['taps'] = taps
        self.register = '1111111111111111'
        # initial state of lfsr

    def clock(self, i='0'):
        # Receives input bit and simulates one cycle
        # This include xoring, shifting, updating input bit and returning output
        # input bit must be XOR-ed with the taps!!

        # grab output bit
        outputBit = self.register[-1]

        # xor all tap values together
        tapXor = "0"

        for tap in taps:
            tapXor = sxor(tapXor, self.register[tap])

        # xor input bit with resulting tap
        inputBit = sxor(i, tapXor)

        # shift register bits and prepend input bit to stream
        self.register = srshift(self.register, inputBit)

        # return output bit
        return outputBit

    def seed(self, s):
        # This function seeds the lfsr by feeding all bits from s into input of
        # lfsr, output is ignored
        for i in s:
            o = self.clock(i)

    def gen(self,n,skip=0):
        # This function clocks lfsr 'skip' number of cycles ignoring output,
        # then clocks 'n' cycles more and records the output. Then returns
        # the recorded output. This is used as hash or pad
        for x in xrange(skip):
            self.clock()
        out = ''
        for x in xrange(n):
            out += self.clock()
        return out

def H(inp):
    # Hash function, it must initialize a new lfsr, seed it with the inp binary string
    # skip and read the required number of lfsr output bits, returns binary string

    l = lfsr([2,4,5,7,11,14])
    l.seed(inp)
    return l.gen(56, 1000)

def enc_pad(m,p):
    # encrypt message m with pad p, return binary string
    # note: function assumes pad p is the same length
    # as message m, per the definition of OTP
    o = ''

    for i in range(0, len(m)):
        o +=  sxor(m[i], p[i])

    return o

def GenRSA():
    # Function to generate RSA keys. Use the Euler's totient function (phi)
    # As we discussed in lectures. Function must: 1) seed python's random number
    # generator with time.time() 2) Generate RSA primes by keeping generating
    # random integers of size 512 bit using the random.getrandbits(512) and testing
    # whether they are prime or not using the is_prime function until both primes found.
    # 3) compute phi 4) find e that is coprime to phi. Start from e=3 and keep
    # incrementing until you find a suitable one. 4) derive d 5) return tuple (n,e,d)
    # n - public modulo, e - public exponent, d - private exponent

    # seed random number generator
    random.seed(time.time())

    # generate rsa primes
    p = random.getrandbits(512)
    q = random.getrandbits(512)

    while not is_prime(p):
        p = random.getrandbits(512)

    while not is_prime(q):
        q = random.getrandbits(512)

    # compute n and phi
    n = p * q
    phi = (p - 1) * (q - 1)

    # find an e coprime to phi
    e = 3

    while egcd(e, phi)[0] != 1:
        e += 1

    # derive d
    d = modinv(e, phi)

    # return the tuple
    return (n,e,d)