• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Pure-Python RSA implementation."""
2
3from cryptomath import *
4import xmltools
5from ASN1Parser import ASN1Parser
6from RSAKey import *
7
8class Python_RSAKey(RSAKey):
9    def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
10        if (n and not e) or (e and not n):
11            raise AssertionError()
12        self.n = n
13        self.e = e
14        self.d = d
15        self.p = p
16        self.q = q
17        self.dP = dP
18        self.dQ = dQ
19        self.qInv = qInv
20        self.blinder = 0
21        self.unblinder = 0
22
23    def hasPrivateKey(self):
24        return self.d != 0
25
26    def hash(self):
27        s = self.writeXMLPublicKey('\t\t')
28        return hashAndBase64(s.strip())
29
30    def _rawPrivateKeyOp(self, m):
31        #Create blinding values, on the first pass:
32        if not self.blinder:
33            self.unblinder = getRandomNumber(2, self.n)
34            self.blinder = powMod(invMod(self.unblinder, self.n), self.e,
35                                  self.n)
36
37        #Blind the input
38        m = (m * self.blinder) % self.n
39
40        #Perform the RSA operation
41        c = self._rawPrivateKeyOpHelper(m)
42
43        #Unblind the output
44        c = (c * self.unblinder) % self.n
45
46        #Update blinding values
47        self.blinder = (self.blinder * self.blinder) % self.n
48        self.unblinder = (self.unblinder * self.unblinder) % self.n
49
50        #Return the output
51        return c
52
53
54    def _rawPrivateKeyOpHelper(self, m):
55        #Non-CRT version
56        #c = powMod(m, self.d, self.n)
57
58        #CRT version  (~3x faster)
59        s1 = powMod(m, self.dP, self.p)
60        s2 = powMod(m, self.dQ, self.q)
61        h = ((s1 - s2) * self.qInv) % self.p
62        c = s2 + self.q * h
63        return c
64
65    def _rawPublicKeyOp(self, c):
66        m = powMod(c, self.e, self.n)
67        return m
68
69    def acceptsPassword(self): return False
70
71    def write(self, indent=''):
72        if self.d:
73            s = indent+'<privateKey xmlns="http://trevp.net/rsa">\n'
74        else:
75            s = indent+'<publicKey xmlns="http://trevp.net/rsa">\n'
76        s += indent+'\t<n>%s</n>\n' % numberToBase64(self.n)
77        s += indent+'\t<e>%s</e>\n' % numberToBase64(self.e)
78        if self.d:
79            s += indent+'\t<d>%s</d>\n' % numberToBase64(self.d)
80            s += indent+'\t<p>%s</p>\n' % numberToBase64(self.p)
81            s += indent+'\t<q>%s</q>\n' % numberToBase64(self.q)
82            s += indent+'\t<dP>%s</dP>\n' % numberToBase64(self.dP)
83            s += indent+'\t<dQ>%s</dQ>\n' % numberToBase64(self.dQ)
84            s += indent+'\t<qInv>%s</qInv>\n' % numberToBase64(self.qInv)
85            s += indent+'</privateKey>'
86        else:
87            s += indent+'</publicKey>'
88        #Only add \n if part of a larger structure
89        if indent != '':
90            s += '\n'
91        return s
92
93    def writeXMLPublicKey(self, indent=''):
94        return Python_RSAKey(self.n, self.e).write(indent)
95
96    def generate(bits):
97        key = Python_RSAKey()
98        p = getRandomPrime(bits/2, False)
99        q = getRandomPrime(bits/2, False)
100        t = lcm(p-1, q-1)
101        key.n = p * q
102        key.e = 3L  #Needed to be long, for Java
103        key.d = invMod(key.e, t)
104        key.p = p
105        key.q = q
106        key.dP = key.d % (p-1)
107        key.dQ = key.d % (q-1)
108        key.qInv = invMod(q, p)
109        return key
110    generate = staticmethod(generate)
111
112    def parsePEM(s, passwordCallback=None):
113        """Parse a string containing a <privateKey> or <publicKey>, or
114        PEM-encoded key."""
115
116        start = s.find("-----BEGIN PRIVATE KEY-----")
117        if start != -1:
118            end = s.find("-----END PRIVATE KEY-----")
119            if end == -1:
120                raise SyntaxError("Missing PEM Postfix")
121            s = s[start+len("-----BEGIN PRIVATE KEY -----") : end]
122            bytes = base64ToBytes(s)
123            return Python_RSAKey._parsePKCS8(bytes)
124        else:
125            start = s.find("-----BEGIN RSA PRIVATE KEY-----")
126            if start != -1:
127                end = s.find("-----END RSA PRIVATE KEY-----")
128                if end == -1:
129                    raise SyntaxError("Missing PEM Postfix")
130                s = s[start+len("-----BEGIN RSA PRIVATE KEY -----") : end]
131                bytes = base64ToBytes(s)
132                return Python_RSAKey._parseSSLeay(bytes)
133        raise SyntaxError("Missing PEM Prefix")
134    parsePEM = staticmethod(parsePEM)
135
136    def parseXML(s):
137        element = xmltools.parseAndStripWhitespace(s)
138        return Python_RSAKey._parseXML(element)
139    parseXML = staticmethod(parseXML)
140
141    def _parsePKCS8(bytes):
142        p = ASN1Parser(bytes)
143
144        version = p.getChild(0).value[0]
145        if version != 0:
146            raise SyntaxError("Unrecognized PKCS8 version")
147
148        rsaOID = p.getChild(1).value
149        if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]:
150            raise SyntaxError("Unrecognized AlgorithmIdentifier")
151
152        #Get the privateKey
153        privateKeyP = p.getChild(2)
154
155        #Adjust for OCTET STRING encapsulation
156        privateKeyP = ASN1Parser(privateKeyP.value)
157
158        return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
159    _parsePKCS8 = staticmethod(_parsePKCS8)
160
161    def _parseSSLeay(bytes):
162        privateKeyP = ASN1Parser(bytes)
163        return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
164    _parseSSLeay = staticmethod(_parseSSLeay)
165
166    def _parseASN1PrivateKey(privateKeyP):
167        version = privateKeyP.getChild(0).value[0]
168        if version != 0:
169            raise SyntaxError("Unrecognized RSAPrivateKey version")
170        n = bytesToNumber(privateKeyP.getChild(1).value)
171        e = bytesToNumber(privateKeyP.getChild(2).value)
172        d = bytesToNumber(privateKeyP.getChild(3).value)
173        p = bytesToNumber(privateKeyP.getChild(4).value)
174        q = bytesToNumber(privateKeyP.getChild(5).value)
175        dP = bytesToNumber(privateKeyP.getChild(6).value)
176        dQ = bytesToNumber(privateKeyP.getChild(7).value)
177        qInv = bytesToNumber(privateKeyP.getChild(8).value)
178        return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
179    _parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey)
180
181    def _parseXML(element):
182        try:
183            xmltools.checkName(element, "privateKey")
184        except SyntaxError:
185            xmltools.checkName(element, "publicKey")
186
187        #Parse attributes
188        xmltools.getReqAttribute(element, "xmlns", "http://trevp.net/rsa\Z")
189        xmltools.checkNoMoreAttributes(element)
190
191        #Parse public values (<n> and <e>)
192        n = base64ToNumber(xmltools.getText(xmltools.getChild(element, 0, "n"), xmltools.base64RegEx))
193        e = base64ToNumber(xmltools.getText(xmltools.getChild(element, 1, "e"), xmltools.base64RegEx))
194        d = 0
195        p = 0
196        q = 0
197        dP = 0
198        dQ = 0
199        qInv = 0
200        #Parse private values, if present
201        if element.childNodes.length>=3:
202            d = base64ToNumber(xmltools.getText(xmltools.getChild(element, 2, "d"), xmltools.base64RegEx))
203            p = base64ToNumber(xmltools.getText(xmltools.getChild(element, 3, "p"), xmltools.base64RegEx))
204            q = base64ToNumber(xmltools.getText(xmltools.getChild(element, 4, "q"), xmltools.base64RegEx))
205            dP = base64ToNumber(xmltools.getText(xmltools.getChild(element, 5, "dP"), xmltools.base64RegEx))
206            dQ = base64ToNumber(xmltools.getText(xmltools.getChild(element, 6, "dQ"), xmltools.base64RegEx))
207            qInv = base64ToNumber(xmltools.getText(xmltools.getLastChild(element, 7, "qInv"), xmltools.base64RegEx))
208        return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
209    _parseXML = staticmethod(_parseXML)
210