| """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, numBytes(self.n)) |
| 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 |