1# Author: Trevor Perrin 2# See the LICENSE file for legal information regarding use of this file. 3 4"""Pure-Python RSA implementation.""" 5 6from .cryptomath import * 7from .asn1parser import ASN1Parser 8from .rsakey import * 9from .pem import * 10 11class Python_RSAKey(RSAKey): 12 def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): 13 if (n and not e) or (e and not n): 14 raise AssertionError() 15 self.n = n 16 self.e = e 17 self.d = d 18 self.p = p 19 self.q = q 20 self.dP = dP 21 self.dQ = dQ 22 self.qInv = qInv 23 self.blinder = 0 24 self.unblinder = 0 25 26 def hasPrivateKey(self): 27 return self.d != 0 28 29 def _rawPrivateKeyOp(self, m): 30 #Create blinding values, on the first pass: 31 if not self.blinder: 32 self.unblinder = getRandomNumber(2, self.n) 33 self.blinder = powMod(invMod(self.unblinder, self.n), self.e, 34 self.n) 35 36 #Blind the input 37 m = (m * self.blinder) % self.n 38 39 #Perform the RSA operation 40 c = self._rawPrivateKeyOpHelper(m) 41 42 #Unblind the output 43 c = (c * self.unblinder) % self.n 44 45 #Update blinding values 46 self.blinder = (self.blinder * self.blinder) % self.n 47 self.unblinder = (self.unblinder * self.unblinder) % self.n 48 49 #Return the output 50 return c 51 52 53 def _rawPrivateKeyOpHelper(self, m): 54 #Non-CRT version 55 #c = powMod(m, self.d, self.n) 56 57 #CRT version (~3x faster) 58 s1 = powMod(m, self.dP, self.p) 59 s2 = powMod(m, self.dQ, self.q) 60 h = ((s1 - s2) * self.qInv) % self.p 61 c = s2 + self.q * h 62 return c 63 64 def _rawPublicKeyOp(self, c): 65 m = powMod(c, self.e, self.n) 66 return m 67 68 def acceptsPassword(self): return False 69 70 def generate(bits): 71 key = Python_RSAKey() 72 p = getRandomPrime(bits//2, False) 73 q = getRandomPrime(bits//2, False) 74 t = lcm(p-1, q-1) 75 key.n = p * q 76 key.e = 65537 77 key.d = invMod(key.e, t) 78 key.p = p 79 key.q = q 80 key.dP = key.d % (p-1) 81 key.dQ = key.d % (q-1) 82 key.qInv = invMod(q, p) 83 return key 84 generate = staticmethod(generate) 85 86 def parsePEM(s, passwordCallback=None): 87 """Parse a string containing a <privateKey> or <publicKey>, or 88 PEM-encoded key.""" 89 90 if pemSniff(s, "PRIVATE KEY"): 91 bytes = dePem(s, "PRIVATE KEY") 92 return Python_RSAKey._parsePKCS8(bytes) 93 elif pemSniff(s, "RSA PRIVATE KEY"): 94 bytes = dePem(s, "RSA PRIVATE KEY") 95 return Python_RSAKey._parseSSLeay(bytes) 96 else: 97 raise SyntaxError("Not a PEM private key file") 98 parsePEM = staticmethod(parsePEM) 99 100 def _parsePKCS8(bytes): 101 p = ASN1Parser(bytes) 102 103 version = p.getChild(0).value[0] 104 if version != 0: 105 raise SyntaxError("Unrecognized PKCS8 version") 106 107 rsaOID = p.getChild(1).value 108 if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: 109 raise SyntaxError("Unrecognized AlgorithmIdentifier") 110 111 #Get the privateKey 112 privateKeyP = p.getChild(2) 113 114 #Adjust for OCTET STRING encapsulation 115 privateKeyP = ASN1Parser(privateKeyP.value) 116 117 return Python_RSAKey._parseASN1PrivateKey(privateKeyP) 118 _parsePKCS8 = staticmethod(_parsePKCS8) 119 120 def _parseSSLeay(bytes): 121 privateKeyP = ASN1Parser(bytes) 122 return Python_RSAKey._parseASN1PrivateKey(privateKeyP) 123 _parseSSLeay = staticmethod(_parseSSLeay) 124 125 def _parseASN1PrivateKey(privateKeyP): 126 version = privateKeyP.getChild(0).value[0] 127 if version != 0: 128 raise SyntaxError("Unrecognized RSAPrivateKey version") 129 n = bytesToNumber(privateKeyP.getChild(1).value) 130 e = bytesToNumber(privateKeyP.getChild(2).value) 131 d = bytesToNumber(privateKeyP.getChild(3).value) 132 p = bytesToNumber(privateKeyP.getChild(4).value) 133 q = bytesToNumber(privateKeyP.getChild(5).value) 134 dP = bytesToNumber(privateKeyP.getChild(6).value) 135 dQ = bytesToNumber(privateKeyP.getChild(7).value) 136 qInv = bytesToNumber(privateKeyP.getChild(8).value) 137 return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) 138 _parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey) 139