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