• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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