def _test_signing(self, dsaObj): k = a2b_hex(self.k) m_hash = a2b_hex(self.m_hash) r = bytes_to_long(a2b_hex(self.r)) s = bytes_to_long(a2b_hex(self.s)) (r_out, s_out) = dsaObj.sign(m_hash, k) self.assertEqual((r, s), (r_out, s_out))
def _exercise_primitive(self, rsaObj): # Since we're using a randomly-generated key, we can't check the test # vector, but we can make sure encryption and decryption are inverse # operations. ciphertext = a2b_hex(self.ciphertext) # Test decryption plaintext = rsaObj.decrypt((ciphertext,)) # Test encryption (2 arguments) (new_ciphertext2,) = rsaObj.encrypt(plaintext, b("")) self.assertEqual(b2a_hex(ciphertext), b2a_hex(new_ciphertext2)) # Test blinded decryption blinding_factor = Random.new().read(len(ciphertext)-1) blinded_ctext = rsaObj.blind(ciphertext, blinding_factor) blinded_ptext = rsaObj.decrypt((blinded_ctext,)) unblinded_plaintext = rsaObj.unblind(blinded_ptext, blinding_factor) self.assertEqual(b2a_hex(plaintext), b2a_hex(unblinded_plaintext)) # Test signing (2 arguments) signature2 = rsaObj.sign(ciphertext, b("")) self.assertEqual((bytes_to_long(plaintext),), signature2) # Test verification self.assertEqual(1, rsaObj.verify(ciphertext, (bytes_to_long(plaintext),)))
def generate_py(bits, randfunc, progress_func=None): """generate(bits:int, randfunc:callable, progress_func:callable) Generate a DSA key of length 'bits', using 'randfunc' to get random data and 'progress_func', if present, to display the progress of the key generation. """ if bits < 160: raise ValueError('Key length < 160 bits') obj = DSAobj() # Generate string S and prime q if progress_func: progress_func('p,q\n') while (1): S, obj.q = generateQ(randfunc) n = divmod(bits - 1, 160)[0] C, N, V = 0, 2, {} b = (obj.q >> 5) & 15 powb = pow(bignum(2), b) powL1 = pow(bignum(2), bits - 1) while C < 4096: for k in range(0, n + 1): V[k] = bytes_to_long(SHA.new(S + bstr(N) + bstr(k)).digest()) W = V[n] % powb for k in range(n - 1, -1, -1): W = (W << 160) + V[k] X = W + powL1 p = X - (X % (2 * obj.q) - 1) if powL1 <= p and isPrime(p): break C, N = C + 1, N + n + 1 if C < 4096: break if progress_func: progress_func('4096 multiples failed\n') obj.p = p power = divmod(p - 1, obj.q)[0] if progress_func: progress_func('h,g\n') while (1): h = bytes_to_long(randfunc(bits)) % (p - 1) g = pow(h, power, p) if 1 < h < p - 1 and g > 1: break obj.g = g if progress_func: progress_func('x,y\n') while (1): x = bytes_to_long(randfunc(20)) if 0 < x < obj.q: break obj.x, obj.y = x, pow(g, x, p) return obj
def undigest(self, blocks): """undigest(blocks : [string]) : string Perform the reverse package transformation on a list of message blocks. Note that the ciphermodule used for both transformations must be the same. blocks is a list of strings of bit length equal to the ciphermodule's block_size. """ # better have at least 2 blocks, for the padbytes package and the hash # block accumulator if len(blocks) < 2: raise ValueError("List must be at least length 2.") # blocks is a list of strings. We need to deal with them as long # integers blocks = list(map(bytes_to_long, blocks)) # Calculate the well-known key, to which the hash blocks are # encrypted, and create the hash cipher. K0 = self.__K0digit * self.__key_size hcipher = self.__newcipher(K0) block_size = self.__ciphermodule.block_size # Since we have all the blocks (or this method would have been called # prematurely), we can calculate all the hash blocks. hashes = [] for i in range(1, len(blocks)): mticki = blocks[i-1] ^ i hi = hcipher.encrypt(long_to_bytes(mticki, block_size)) hashes.append(bytes_to_long(hi)) # now we can calculate K' (key). remember the last block contains # m's' which we don't include here key = blocks[-1] ^ reduce(operator.xor, hashes) # and now we can create the cipher object mcipher = self.__newcipher(long_to_bytes(key, self.__key_size)) # And we can now decode the original message blocks parts = [] for i in range(1, len(blocks)): cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) mi = blocks[i-1] ^ bytes_to_long(cipherblock) parts.append(mi) # The last message block contains the number of pad bytes appended to # the original text string, such that its length was an even multiple # of the cipher's block_size. This number should be small enough that # the conversion from long integer to integer should never overflow padbytes = int(parts[-1]) text = b('').join(map(long_to_bytes, parts[:-1])) return text[:-padbytes]
def setUp(self): global RSA, Random, bytes_to_long from Crypro.PublicKey import RSA from Crypro import Random from Crypro.Util.number import bytes_to_long, inverse self.n = bytes_to_long(a2b_hex(self.modulus)) self.p = bytes_to_long(a2b_hex(self.prime_factor)) # Compute q, d, and u from n, e, and p self.q = divmod(self.n, self.p)[0] self.d = inverse(self.e, (self.p-1)*(self.q-1)) self.u = inverse(self.p, self.q) # u = e**-1 (mod q) self.rsa = RSA
def _exercise_public_primitive(self, rsaObj): plaintext = a2b_hex(self.plaintext) # Test encryption (2 arguments) (new_ciphertext2,) = rsaObj.encrypt(plaintext, b("")) # Exercise verification rsaObj.verify(new_ciphertext2, (bytes_to_long(plaintext),))
def test_construct_4tuple(self): """DSA (default implementation) constructed key (4-tuple)""" (y, g, p, q) = [ bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q) ] dsaObj = self.dsa.construct((y, g, p, q)) self._test_verification(dsaObj)
def _check_verification(self, rsaObj): signature = bytes_to_long(a2b_hex(self.plaintext)) message = a2b_hex(self.ciphertext) # Test verification t = (signature,) # rsaObj.verify expects a tuple self.assertEqual(1, rsaObj.verify(message, t)) # Test verification with overlong tuple (this is a # backward-compatibility hack to support some harmless misuse of the # API) t2 = (signature, '') self.assertEqual(1, rsaObj.verify(message, t2)) # extra garbage at end of tuple
def _decodeLen(self, idx, der): """Given a (part of a) DER element, and an index to the first byte of a DER length tag (L), return a tuple with the payload size, and the index of the first byte of the such payload (V). Raises a ValueError exception if the DER length is invalid. Raises an IndexError exception if the DER element is too short. """ length = bord(der[idx]) if length <= 127: return (length, idx + 1) payloadLength = bytes_to_long(der[idx + 1:idx + 1 + (length & 0x7F)]) if payloadLength <= 127: raise ValueError("Not a DER length tag.") return (payloadLength, idx + 1 + (length & 0x7F))
def generateQ(randfunc): S = randfunc(20) hash1 = SHA.new(S).digest() hash2 = SHA.new(long_to_bytes(bytes_to_long(S) + 1)).digest() q = bignum(0) for i in range(0, 20): c = bord(hash1[i]) ^ bord(hash2[i]) if i == 0: c = c | 128 if i == 19: c = c | 1 q = q * 256 + c while (not isPrime(q)): q = q + 2 if pow(2, 159) < q < pow(2, 160): return S, q raise RuntimeError('Bad q value generated')
def decode(self, derEle, noLeftOvers=0): """Decode a complete INTEGER DER element, and re-initializes this object with it. @param derEle A complete INTEGER DER element. It must start with a DER INTEGER tag. @param noLeftOvers Indicate whether it is acceptable to complete the parsing of the DER element and find that not all bytes in derEle have been used. @return Index of the first unused byte in the given DER element. Raises a ValueError exception if the DER element is not a valid non-negative INTEGER. Raises an IndexError exception if the DER element is too short. """ tlvLength = DerObject.decode(self, derEle, noLeftOvers) if self.typeTag != self.typeTags['INTEGER']: raise ValueError("Not a DER INTEGER.") if bord(self.payload[0]) > 127: raise ValueError("Negative INTEGER.") self.value = bytes_to_long(self.payload) return tlvLength
def _test_verification(self, dsaObj): m_hash = a2b_hex(self.m_hash) r = bytes_to_long(a2b_hex(self.r)) s = bytes_to_long(a2b_hex(self.s)) self.assertEqual(1, dsaObj.verify(m_hash, (r, s))) self.assertEqual(0, dsaObj.verify(m_hash + b("\0"), (r, s)))
def importKey(self, externKey, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :Parameter externKey: The RSA key to import, encoded as a string. An RSA public key can be in any of the following formats: - X.509 `subjectPublicKeyInfo` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ `RSAPublicKey` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 `RSAPrivateKey` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ `PrivateKeyInfo` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. In case of PEM encoding, the private key can be encrypted with DES or 3TDES according to a certain ``pass phrase``. Only OpenSSL-compatible pass phrases are supported. :Type externKey: string :Parameter passphrase: In case of an encrypted PEM key, this is the pass phrase from which the encryption key is derived. :Type passphrase: string :Return: An RSA key object (`_RSAobj`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ externKey = tobytes(externKey) if passphrase is not None: passphrase = tobytes(passphrase) if externKey.startswith(b('-----')): # This is probably a PEM encoded key lines = externKey.replace(b(" "), b('')).split() keyobj = None # The encrypted PEM format if lines[1].startswith(b('Proc-Type:4,ENCRYPTED')): DEK = lines[2].split(b(':')) if len(DEK) != 2 or DEK[0] != b('DEK-Info') or not passphrase: raise ValueError("PEM encryption format not supported.") algo, salt = DEK[1].split(b(',')) salt = binascii.a2b_hex(salt) import Crypro.Hash.MD5 from Crypro.Cipher import DES, DES3 from Crypro.Protocol.KDF import PBKDF1 if algo == b("DES-CBC"): # This is EVP_BytesToKey in OpenSSL key = PBKDF1(passphrase, salt, 8, 1, Crypro.Hash.MD5) keyobj = DES.new(key, Crypro.Cipher.DES.MODE_CBC, salt) elif algo == b("DES-EDE3-CBC"): # Note that EVP_BytesToKey is note exactly the same as PBKDF1 key = PBKDF1(passphrase, salt, 16, 1, Crypro.Hash.MD5) key += PBKDF1(key + passphrase, salt, 8, 1, Crypro.Hash.MD5) keyobj = DES3.new(key, Crypro.Cipher.DES3.MODE_CBC, salt) else: raise ValueError("Unsupport PEM encryption algorithm.") lines = lines[2:] der = binascii.a2b_base64(b('').join(lines[1:-1])) if keyobj: der = keyobj.decrypt(der) padding = bord(der[-1]) der = der[:-padding] return self._importKeyDER(der) if externKey.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(externKey.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = bytes_to_long(keyparts[1]) n = bytes_to_long(keyparts[2]) return self.construct([n, e]) if bord(externKey[0]) == 0x30: # This is probably a DER encoded key return self._importKeyDER(externKey) raise ValueError("RSA key format is not supported")
def digest(self, text): """digest(text:string) : [string] Perform the All-or-Nothing package transform on the given string. Output is a list of message blocks describing the transformed text, where each block is a string of bit length equal to the ciphermodule's block_size. """ # generate a random session key and K0, the key used to encrypt the # hash blocks. Rivest calls this a fixed, publically-known encryption # key, but says nothing about the security implications of this key or # how to choose it. key = self._inventkey(self.__key_size) K0 = self.__K0digit * self.__key_size # we need two cipher objects here, one that is used to encrypt the # message blocks and one that is used to encrypt the hashes. The # former uses the randomly generated key, while the latter uses the # well-known key. mcipher = self.__newcipher(key) hcipher = self.__newcipher(K0) # Pad the text so that its length is a multiple of the cipher's # block_size. Pad with trailing spaces, which will be eliminated in # the undigest() step. block_size = self.__ciphermodule.block_size padbytes = block_size - (len(text) % block_size) text = text + b(' ') * padbytes # Run through the algorithm: # s: number of message blocks (size of text / block_size) # input sequence: m1, m2, ... ms # random key K' (`key' in the code) # Compute output sequence: m'1, m'2, ... m's' for s' = s + 1 # Let m'i = mi ^ E(K', i) for i = 1, 2, 3, ..., s # Let m's' = K' ^ h1 ^ h2 ^ ... hs # where hi = E(K0, m'i ^ i) for i = 1, 2, ... s # # The one complication I add is that the last message block is hard # coded to the number of padbytes added, so that these can be stripped # during the undigest() step s = divmod(len(text), block_size)[0] blocks = [] hashes = [] for i in range(1, s+1): start = (i-1) * block_size end = start + block_size mi = text[start:end] assert len(mi) == block_size cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) mticki = bytes_to_long(mi) ^ bytes_to_long(cipherblock) blocks.append(mticki) # calculate the hash block for this block hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) hashes.append(bytes_to_long(hi)) # Add the padbytes length as a message block i = i + 1 cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) mticki = padbytes ^ bytes_to_long(cipherblock) blocks.append(mticki) # calculate this block's hash hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) hashes.append(bytes_to_long(hi)) # Now calculate the last message block of the sequence 1..s'. This # will contain the random session key XOR'd with all the hash blocks, # so that for undigest(), once all the hash blocks are calculated, the # session key can be trivially extracted. Calculating all the hash # blocks requires that all the message blocks be received, thus the # All-or-Nothing algorithm succeeds. mtick_stick = bytes_to_long(key) ^ reduce(operator.xor, hashes) blocks.append(mtick_stick) # we convert the blocks to strings since in Python, byte sequences are # always represented as strings. This is more consistent with the # model that encryption and hash algorithms always operate on strings. return [long_to_bytes(i,self.__ciphermodule.block_size) for i in blocks]
def _check_signing(self, rsaObj): signature = bytes_to_long(a2b_hex(self.plaintext)) message = a2b_hex(self.ciphertext) # Test signing (2 argument) self.assertEqual((signature,), rsaObj.sign(message, b("")))
def getrandbits(self, k): """Return a python long integer with k random bits.""" if self._randfunc is None: self._randfunc = Random.new().read mask = (1 << k) - 1 return mask & bytes_to_long(self._randfunc(ceil_div(k, 8)))
usage(0) elif opt in ('-c', '--cipher'): ciphermodule = arg elif opt in ('-l', '--aslong'): aslong = 1 # ugly hack to force __import__ to give us the end-path module module = __import__('Crypro.Cipher.'+ciphermodule, None, None, ['new']) x = AllOrNothing(module) print('Original text:\n==========') print(__doc__) print('==========') msgblocks = x.digest(b(__doc__)) print('message blocks:') for i, blk in zip(list(range(len(msgblocks))), msgblocks): # base64 adds a trailing newline print(' %3d' % i, end=' ') if aslong: print(bytes_to_long(blk)) else: print(base64.encodestring(blk)[:-1]) # # get a new undigest-only object so there's no leakage y = AllOrNothing(module) text = y.undigest(msgblocks) if text == b(__doc__): print('They match!') else: print('They differ!')