summaryrefslogtreecommitdiffstats
path: root/src/lib/tlslite/utils/RSAKey.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/tlslite/utils/RSAKey.py')
-rwxr-xr-xsrc/lib/tlslite/utils/RSAKey.py264
1 files changed, 264 insertions, 0 deletions
diff --git a/src/lib/tlslite/utils/RSAKey.py b/src/lib/tlslite/utils/RSAKey.py
new file mode 100755
index 000000000..37c292df5
--- /dev/null
+++ b/src/lib/tlslite/utils/RSAKey.py
@@ -0,0 +1,264 @@
+"""Abstract class for RSA."""
+
+from cryptomath import *
+
+
+class RSAKey:
+ """This is an abstract base class for RSA keys.
+
+ Particular implementations of RSA keys, such as
+ L{OpenSSL_RSAKey.OpenSSL_RSAKey},
+ L{Python_RSAKey.Python_RSAKey}, and
+ L{PyCrypto_RSAKey.PyCrypto_RSAKey},
+ inherit from this.
+
+ To create or parse an RSA key, don't use one of these classes
+ directly. Instead, use the factory functions in
+ L{tlslite.utils.keyfactory}.
+ """
+
+ def __init__(self, n=0, e=0):
+ """Create a new RSA key.
+
+ If n and e are passed in, the new key will be initialized.
+
+ @type n: int
+ @param n: RSA modulus.
+
+ @type e: int
+ @param e: RSA public exponent.
+ """
+ raise NotImplementedError()
+
+ def __len__(self):
+ """Return the length of this key in bits.
+
+ @rtype: int
+ """
+ return numBits(self.n)
+
+ def hasPrivateKey(self):
+ """Return whether or not this key has a private component.
+
+ @rtype: bool
+ """
+ raise NotImplementedError()
+
+ def hash(self):
+ """Return the cryptoID <keyHash> value corresponding to this
+ key.
+
+ @rtype: str
+ """
+ raise NotImplementedError()
+
+ def getSigningAlgorithm(self):
+ """Return the cryptoID sigAlgo value corresponding to this key.
+
+ @rtype: str
+ """
+ return "pkcs1-sha1"
+
+ def hashAndSign(self, bytes):
+ """Hash and sign the passed-in bytes.
+
+ This requires the key to have a private component. It performs
+ a PKCS1-SHA1 signature on the passed-in data.
+
+ @type bytes: str or L{array.array} of unsigned bytes
+ @param bytes: The value which will be hashed and signed.
+
+ @rtype: L{array.array} of unsigned bytes.
+ @return: A PKCS1-SHA1 signature on the passed-in data.
+ """
+ if not isinstance(bytes, type("")):
+ bytes = bytesToString(bytes)
+ hashBytes = stringToBytes(sha.sha(bytes).digest())
+ prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes)
+ sigBytes = self.sign(prefixedHashBytes)
+ return sigBytes
+
+ def hashAndVerify(self, sigBytes, bytes):
+ """Hash and verify the passed-in bytes with the signature.
+
+ This verifies a PKCS1-SHA1 signature on the passed-in data.
+
+ @type sigBytes: L{array.array} of unsigned bytes
+ @param sigBytes: A PKCS1-SHA1 signature.
+
+ @type bytes: str or L{array.array} of unsigned bytes
+ @param bytes: The value which will be hashed and verified.
+
+ @rtype: bool
+ @return: Whether the signature matches the passed-in data.
+ """
+ if not isinstance(bytes, type("")):
+ bytes = bytesToString(bytes)
+ hashBytes = stringToBytes(sha.sha(bytes).digest())
+ prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes)
+ return self.verify(sigBytes, prefixedHashBytes)
+
+ def sign(self, bytes):
+ """Sign the passed-in bytes.
+
+ This requires the key to have a private component. It performs
+ a PKCS1 signature on the passed-in data.
+
+ @type bytes: L{array.array} of unsigned bytes
+ @param bytes: The value which will be signed.
+
+ @rtype: L{array.array} of unsigned bytes.
+ @return: A PKCS1 signature on the passed-in data.
+ """
+ if not self.hasPrivateKey():
+ raise AssertionError()
+ paddedBytes = self._addPKCS1Padding(bytes, 1)
+ m = bytesToNumber(paddedBytes)
+ if m >= self.n:
+ raise ValueError()
+ c = self._rawPrivateKeyOp(m)
+ sigBytes = numberToBytes(c)
+ return sigBytes
+
+ def verify(self, sigBytes, bytes):
+ """Verify the passed-in bytes with the signature.
+
+ This verifies a PKCS1 signature on the passed-in data.
+
+ @type sigBytes: L{array.array} of unsigned bytes
+ @param sigBytes: A PKCS1 signature.
+
+ @type bytes: L{array.array} of unsigned bytes
+ @param bytes: The value which will be verified.
+
+ @rtype: bool
+ @return: Whether the signature matches the passed-in data.
+ """
+ paddedBytes = self._addPKCS1Padding(bytes, 1)
+ c = bytesToNumber(sigBytes)
+ if c >= self.n:
+ return False
+ m = self._rawPublicKeyOp(c)
+ checkBytes = numberToBytes(m)
+ return checkBytes == paddedBytes
+
+ def encrypt(self, bytes):
+ """Encrypt the passed-in bytes.
+
+ This performs PKCS1 encryption of the passed-in data.
+
+ @type bytes: L{array.array} of unsigned bytes
+ @param bytes: The value which will be encrypted.
+
+ @rtype: L{array.array} of unsigned bytes.
+ @return: A PKCS1 encryption of the passed-in data.
+ """
+ paddedBytes = self._addPKCS1Padding(bytes, 2)
+ m = bytesToNumber(paddedBytes)
+ if m >= self.n:
+ raise ValueError()
+ c = self._rawPublicKeyOp(m)
+ encBytes = numberToBytes(c)
+ return encBytes
+
+ def decrypt(self, encBytes):
+ """Decrypt the passed-in bytes.
+
+ This requires the key to have a private component. It performs
+ PKCS1 decryption of the passed-in data.
+
+ @type encBytes: L{array.array} of unsigned bytes
+ @param encBytes: The value which will be decrypted.
+
+ @rtype: L{array.array} of unsigned bytes or None.
+ @return: A PKCS1 decryption of the passed-in data or None if
+ the data is not properly formatted.
+ """
+ if not self.hasPrivateKey():
+ raise AssertionError()
+ c = bytesToNumber(encBytes)
+ if c >= self.n:
+ return None
+ m = self._rawPrivateKeyOp(c)
+ decBytes = numberToBytes(m)
+ if (len(decBytes) != numBytes(self.n)-1): #Check first byte
+ return None
+ if decBytes[0] != 2: #Check second byte
+ return None
+ for x in range(len(decBytes)-1): #Scan through for zero separator
+ if decBytes[x]== 0:
+ break
+ else:
+ return None
+ return decBytes[x+1:] #Return everything after the separator
+
+ def _rawPrivateKeyOp(self, m):
+ raise NotImplementedError()
+
+ def _rawPublicKeyOp(self, c):
+ raise NotImplementedError()
+
+ def acceptsPassword(self):
+ """Return True if the write() method accepts a password for use
+ in encrypting the private key.
+
+ @rtype: bool
+ """
+ raise NotImplementedError()
+
+ def write(self, password=None):
+ """Return a string containing the key.
+
+ @rtype: str
+ @return: A string describing the key, in whichever format (PEM
+ or XML) is native to the implementation.
+ """
+ raise NotImplementedError()
+
+ def writeXMLPublicKey(self, indent=''):
+ """Return a string containing the key.
+
+ @rtype: str
+ @return: A string describing the public key, in XML format.
+ """
+ return Python_RSAKey(self.n, self.e).write(indent)
+
+ def generate(bits):
+ """Generate a new key with the specified bit length.
+
+ @rtype: L{tlslite.utils.RSAKey.RSAKey}
+ """
+ raise NotImplementedError()
+ generate = staticmethod(generate)
+
+
+ # **************************************************************************
+ # Helper Functions for RSA Keys
+ # **************************************************************************
+
+ def _addPKCS1SHA1Prefix(self, bytes):
+ prefixBytes = createByteArraySequence(\
+ [48,33,48,9,6,5,43,14,3,2,26,5,0,4,20])
+ prefixedBytes = prefixBytes + bytes
+ return prefixedBytes
+
+ def _addPKCS1Padding(self, bytes, blockType):
+ padLength = (numBytes(self.n) - (len(bytes)+3))
+ if blockType == 1: #Signature padding
+ pad = [0xFF] * padLength
+ elif blockType == 2: #Encryption padding
+ pad = createByteArraySequence([])
+ while len(pad) < padLength:
+ padBytes = getRandomBytes(padLength * 2)
+ pad = [b for b in padBytes if b != 0]
+ pad = pad[:padLength]
+ else:
+ raise AssertionError()
+
+ #NOTE: To be proper, we should add [0,blockType]. However,
+ #the zero is lost when the returned padding is converted
+ #to a number, so we don't even bother with it. Also,
+ #adding it would cause a misalignment in verify()
+ padding = createByteArraySequence([blockType] + pad + [0])
+ paddedBytes = padding + bytes
+ return paddedBytes