def encrypt_with_hmac(self, data, data_id, iv): if not isinstance(data, bytes): raise TypeError("data must be bytes.") if not isinstance(data_id, int): raise TypeError("data_id must be int.") main_parts = ( struct.pack(config.FORMAT_CHAR, data_id) + data ) # PKCS7 padding padded_data = self.add_padding(main_parts, algorithms.AES.block_size) # AES with CBC mode encryptor = Cipher(algorithms.AES(self.aes_key), modes.CBC(iv), backend=self.backend).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x80" + iv + ciphertext ) h = HMAC(self.mac_key, hashes.SHA256(), backend=self.backend) h.update(basic_parts) hmac = h.finalize() return basic_parts + hmac
def encrypt(pubkey, data): public_key = _pub_to_public(pubkey) private_key = ec.generate_private_key(ec.SECP256K1(), openssl.backend) secret = private_key.exchange(ec.ECDH(), public_key) key = hashlib.sha512(secret).digest() enckey = key[0:32] mackey = key[32:64] iv = os.urandom(16) padder = padding.PKCS7(128).padder() paddeddata = padder.update(data) + padder.finalize() cipher = Cipher(algorithms.AES(enckey), modes.CBC(iv), openssl.backend) encryptor = cipher.encryptor() ciphertext = encryptor.update(paddeddata) + encryptor.finalize() s = serialize.Serializer() s.bytes(iv) s.uint(0x02ca, 2) public_numbers = private_key.public_key().public_numbers() x = public_numbers.x.to_bytes(32, 'big').lstrip(b'\x00') s.uint(len(x), 2) s.bytes(x) y = public_numbers.y.to_bytes(32, 'big').lstrip(b'\x00') s.uint(len(y), 2) s.bytes(y) s.bytes(ciphertext) maccer = HMAC(mackey, hashes.SHA256(), openssl.backend) maccer.update(s.data) mac = maccer.finalize() s.bytes(mac) return s.data
def checkHMAC(self, fp, segments_start, segments_end, fileHMAC): '''Check the file's integrity''' filehash = HMAC(self.hmackey, primitives.hashes.SHA256(), backend) filehash.update(self.FileMACPrefix) for segmentIndex, startpos, datalen in self.segment_ranges(segments_start, segments_end): print(" Segment %d" % (segmentIndex)) fp.seek(startpos) segmentIV = fp.read(self.SegIVLen) segmentMAC = fp.read(self.SegMACLen) # Verify the segment's own MAC against the segment data segmenthash = HMAC(self.hmackey, primitives.hashes.SHA256(), backend) segmenthash.update(segmentIV) segmenthash.update(struct.pack('>I', segmentIndex)) segmenthash.update(fp.read(datalen)) # The cryptography module doesn't handle truncated HMACs directly computed = segmenthash.finalize() assert primitives.constant_time.bytes_eq(computed[:self.SegMACLen], segmentMAC) # Add the segment's MAC to the file-MAC context filehash.update(segmentMAC) # Finally, verify the file MAC print(" File hash") filehash.verify(fileHMAC) # Raises on mismatch.
def _get_hmac(key, ciphertext, digest_method): hmac = HMAC( key, get_digest(digest_method), backend=default_backend() ) hmac.update(ciphertext) return hmac.finalize()
class Decryptor(object): def __init__(self, rsa_private_key_pem): if not isinstance(rsa_private_key_pem, bytes): rsa_private_key_pem = rsa_private_key_pem.encode("ascii") self.rsa_private_key = serialization.load_pem_private_key( data=rsa_private_key_pem, password=None, backend=default_backend()) self.cipher = None self.authenticator = None self.buf = b"" def update(self, data): self.buf += data if self.cipher is None: if len(self.buf) < 8: return b"" if self.buf[0:6] != FILEMAGIC: raise EncryptorError("Invalid magic bytes") cipherkeylen = struct.unpack(">H", self.buf[6:8])[0] if len(self.buf) < 8 + cipherkeylen: return b"" pad = padding.OAEP(mgf=padding.MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None) try: plainkey = self.rsa_private_key.decrypt(self.buf[8:8 + cipherkeylen], pad) except AssertionError: raise EncryptorError("Decrypting key data failed") if len(plainkey) != 64: raise EncryptorError("Integrity check failed") key = plainkey[0:16] nonce = plainkey[16:32] auth_key = plainkey[32:64] self.cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend()).decryptor() self.authenticator = HMAC(auth_key, SHA256(), backend=default_backend()) self.buf = self.buf[8 + cipherkeylen:] if len(self.buf) < 32: return b"" self.authenticator.update(self.buf[:-32]) result = self.cipher.update(self.buf[:-32]) self.buf = self.buf[-32:] return result def finalize(self): if self.cipher is None: return b"" # empty encrypted input yields empty plaintext output elif self.buf != self.authenticator.finalize(): raise EncryptorError("Integrity check failed") result = self.cipher.finalize() self.buf = b"" self.cipher = None self.authenticator = None return result
def calculate_hmac(key, data): """Shortcut for calculating HMAC of a string.""" h = HMAC( key=key, algorithm=hashes.SHA256(), backend=backend ) h.update(data) return h.finalize()
def __init__(self, key, backend=None): if backend is None: backend = default_backend() key = base64.urlsafe_b64decode(key) if len(key) != 32: raise ValueError( "Fernet key must be 32 url-safe base64-encoded bytes." ) h = HMAC(key, hashes.SHA256(), backend=backend) h.update(b"\x00") self._signing_key = h.finalize()[:16] h = HMAC(key, hashes.SHA256(), backend=backend) h.update(b"\x01") self._encryption_key = h.finalize()[:16] self._backend = backend
def generate_mac(key, data, algorithm="SHA256", backend=BACKEND): """ Returns a message authentication code for verifying the integrity and authenticity of data by entities that possess the key. Note this is a lower level function then apply_mac and only returns the mac itself. The mac is generated via HMAC with the specified algorithm and key. """ hasher = HMAC(key, getattr(hashes, algorithm.upper())(), backend=backend) hasher.update(algorithm + '::' + data) return hasher.finalize()
def compute_verify_data(self, basekey, handshake_context): hash_len = self.hash.digest_size finished_key = self.expand_label(basekey, b"finished", b"", hash_len) h = Hash(self.hash, backend=default_backend()) h.update(handshake_context) hash_value = h.finalize() hm = HMAC(finished_key, self.hash, default_backend()) hm.update(hash_value) return hm.finalize()
def _a(secret, hash_algorithm, n, seed): """ a() is defined as: a(0) = seed a(i) = HMAC_hash(secret, A(i-1)) """ if n == 0: return seed else: h = HMAC(secret, hash_algorithm, default_backend()) h.update(_a(secret, hash_algorithm, n - 1, seed)) return h.finalize()
def _encrypt_cryptography(b_plaintext, b_key1, b_key2, b_iv): cipher = C_Cipher(algorithms.AES(b_key1), modes.CTR(b_iv), CRYPTOGRAPHY_BACKEND) encryptor = cipher.encryptor() padder = padding.PKCS7(algorithms.AES.block_size).padder() b_ciphertext = encryptor.update(padder.update(b_plaintext) + padder.finalize()) b_ciphertext += encryptor.finalize() # COMBINE SALT, DIGEST AND DATA hmac = HMAC(b_key2, hashes.SHA256(), CRYPTOGRAPHY_BACKEND) hmac.update(b_ciphertext) b_hmac = hmac.finalize() return to_bytes(hexlify(b_hmac), errors='surrogate_or_strict'), hexlify(b_ciphertext)
def _p_hash(hash_algorithm, secret, seed, output_length): """ A seed expansion function that uses a single hash function to expand a secret and seed into the number of bytes specified by output_length. """ result = bytearray() i = 1 while len(result) < output_length: h = HMAC(secret, hash_algorithm, default_backend()) h.update(_a(secret, hash_algorithm, i, seed)) h.update(seed) result.extend(h.finalize()) i += 1 return bytes(result[:output_length])
def MyencryptMAC(message, EncKey, HMACKey): # encrypts message with encryption key c, iv = myencrypt(message, EncKey) if len(HMACKey) != KEY_BYTES: # prints error message sys.stderr.write('Error: HMAC key length must be 32 bytes.') else: # generates tag using HMAC on ciphertext generated above tag = HMAC(HMACKey, hashes.SHA256(), backend=default_backend()) tag.update(c) tag = tag.finalize() return c, iv, tag
def __init__(self, key, backend=None): if backend is None: backend = default_backend() # initialize a fernet object self._f = Fernet(key, backend=backend) key = base64.urlsafe_b64decode(key) if len(key) != 32: raise ValueError( "Fernet2 key must be 32 url-safe base64-encoded bytes.") h0 = HMAC(key, hashes.SHA256(), backend=backend) h1 = HMAC(key, hashes.SHA256(), backend=backend) # h0.update(b"0") h1.update(b"1") k0 = h0.finalize()[:16] k1 = h1.finalize()[:16] self._signing_key = k0 self._encryption_key = k1 self._backend = backend
def _encrypt_from_parts(self, data, seed, iv): utils._check_bytes("data", data) padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher(algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = (b"\x80" + struct.pack(">Q", seed) + iv + ciphertext) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts) hmac = h.finalize() return base64.urlsafe_b64encode(basic_parts + hmac).decode()
def __init__(self, key, backend=None): if backend is None: backend = default_backend() # initialize a fernet object self._f = Fernet(key, backend=backend) key = base64.urlsafe_b64decode(key) if len(key) != 32: raise ValueError( "Fernet2 key must be 32 url-safe base64-encoded bytes." ) h0 = HMAC(key, hashes.SHA256(), backend=backend) h1 = HMAC(key, hashes.SHA256(), backend=backend) # h0 .update(b"0") h1 .update(b"1") k0 = h0.finalize()[:16] k1 = h1.finalize()[:16] self._signing_key = k0 self._encryption_key = k1 self._backend = backend
def _hmac_signature(self, work_bytes): try: iv = six.binary_type(work_bytes[self.IV_START:self.IV_SIZE]) payload = six.binary_type( work_bytes[self.PAYLOAD_START:self.SIGNATURE_RSTART]) h = HMAC(self._keys.integrity_key, self.KEY_ALGORITHM(), backend=self._backend) h.update(payload) h.update(iv) hmac = h.finalize() return hmac[0:4] except Exception: raise SignatureException
def _encrypt_from_parts(self, data, current_time, iv): if isinstance(data, six.text_type): raise TypeError("Unicode-objects must be encoded before encryption") padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher(algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts) hmac = h.finalize() return base64.urlsafe_b64encode(basic_parts + hmac)
def encrypt(cls, data: bytes, key: SSSSKey, name: str, iv: Optional[IV] = None) -> SSSSData: if iv: ivb = bytearray(iv) else: ivb = bytearray(secrets.token_bytes(16)) ivb[8] = ivb[8] & 0x7f; iv = IV(bytes(ivb)) keys = key.derive_keys(name) ct = aes256ctr(iv, keys.aes_key, data) hm = HMAC(keys.hmac_key, SHA256(), backend = default_backend()) hm.update(ct) mac = MAC(hm.finalize()) return cls(mac, iv, ct)
def _encrypt_from_parts(self, data, current_time, iv): if not isinstance(data, bytes): raise TypeError("data must be bytes.") padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher(algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = (b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts) hmac = h.finalize() return base64.urlsafe_b64encode(basic_parts + hmac)
def test_decrypt_and_unpack_bad_signature(caplog, mock_open_streams): orig, new, _ = mock_open_streams orig_contents = orig._fd.getvalue() cipher = Cipher(AES(TMP_KEY), CTR(TMP_NONCE), backend=default_backend()).encryptor() ct = cipher.update(zlib.compress(orig_contents)) hmac = HMAC(TMP_KEY, SHA256(), default_backend()) hmac.update(ct) signature = hmac.finalize() orig._fd.write(ct) with pytest.raises(BackupCorruptedError): decrypt_and_unpack( orig, new, TMP_KEYPAIR + signature[:-2] + b'f', dict(use_compression=True, use_encryption=True), )
def encrypt(self, message): iv = urandom(int(self.cipherAlg.block_size / 8)) #generate random iv encryptor = Cipher(self.cipherAlg, self.mode(iv), default_backend()).encryptor() ct = encryptor.update(message) + encryptor.finalize() #encryption ct, iv = b64encode(ct), b64encode(iv) h = HMAC(self.macKey, SHA256(), default_backend()) h.update(ct + iv) mac = h.finalize() #generate MAC return { "ct": ct.decode("ascii"), "iv_nonce": iv.decode("ascii"), "mac": b64encode(mac).decode("ascii") }
def _encrypt_from_parts(self, data, current_time, iv): utils._check_bytes("data", data) padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher( algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend ).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext ) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts) hmac = h.finalize() return base64.urlsafe_b64encode(basic_parts + hmac)
class FernetEncryptMiddleware(BaseMiddleware): """ Encrypt data based on Fernet from the Cryptography library. - Algorithm: AES-128 - Mode: CBC - Padding: PKCS#7 - MAC: SHA-256, Encrypt-then-MAC - Key: User-provided or 32-bytes from /dev/urandom - IV: 16-bytes from /dev/urandom """ writeable = True def __init__(self, key=None, iv=None): self.key = key or os.urandom(32) self.iv = iv or os.urandom(16) self.encryptor = Cipher(algorithms.AES(self.key[16:]), modes.CBC(self.iv), backend=default_backend()).encryptor() self.padder = padding.PKCS7(algorithms.AES.block_size).padder() self.hmac = HMAC(key[:16], hashes.SHA256(), backend=default_backend()) self.header = False def flush(self): if self.encryptor: data = self.padder.finalize() data = self.encryptor.update(data) + self.encryptor.finalize() self.encryptor = None self.hmac.update(data) return data + self.hmac.finalize() return b'' def write(self, data): data = self.padder.update(data) data = self.encryptor.update(data) self.hmac.update(data) if not self.header: data = self.iv + data self.header = True return data
class Encryptor: def __init__(self, rsa_public_key_pem): if not isinstance(rsa_public_key_pem, bytes): rsa_public_key_pem = rsa_public_key_pem.encode("ascii") self.rsa_public_key = serialization.load_pem_public_key( rsa_public_key_pem, backend=default_backend()) self.cipher = None self.authenticator = None def update(self, data): ret = b"" if self.cipher is None: key = os.urandom(16) nonce = os.urandom(16) auth_key = os.urandom(32) self.cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend()).encryptor() self.authenticator = HMAC(auth_key, SHA256(), backend=default_backend()) pad = padding.OAEP(mgf=padding.MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None) cipherkey = self.rsa_public_key.encrypt(key + nonce + auth_key, pad) ret = FILEMAGIC + struct.pack(">H", len(cipherkey)) + cipherkey cur = self.cipher.update(data) self.authenticator.update(cur) if ret: return ret + cur else: return cur def finalize(self): if self.cipher is None: return b"" # empty plaintext input yields empty encrypted output ret = self.cipher.finalize() self.authenticator.update(ret) ret += self.authenticator.finalize() self.cipher = None self.authenticator = None return ret
def encryptThenMac(data, key): #set up keys, current timestamp and initialization vector encryptKey = key[16:] signKey = key[:16] curTime = int(time.time()) iv = os.urandom(16) #pad the data and encrypt using AES in CBC mode padder = padding.PKCS7(algorithms.AES.block_size).padder() paddedData = padder.update(data) + padder.finalize() encryptor = Cipher(algorithms.AES(encryptKey), modes.CBC(iv), default_backend()).encryptor() cipher = encryptor.update(paddedData) + encryptor.finalize() #get the HMAC using SHA256 of the combined parts and return everything combined parts = (b"\x80" + struct.pack(">Q", curTime) + iv + cipher) hasher = HMAC(signKey, hashes.SHA256(), backend=default_backend()) hasher.update(parts) hmac = hasher.finalize() return base64.urlsafe_b64encode(parts + hmac)
def encrypt(self, message): nonce = urandom(16) #generate random nonce encryptor = Cipher(self.cipherAlg(self.key, nonce), None, default_backend()).encryptor() ct = encryptor.update(message) + encryptor.finalize() #encryption ct, nonce = b64encode(ct), b64encode(nonce) h = HMAC(self.macKey, SHA256(), default_backend()) h.update(ct + nonce) mac = h.finalize() #generate MAC return { "ct": ct.decode("ascii"), "iv_nonce": nonce.decode("ascii"), "mac": b64encode(mac).decode("ascii") }
def _xor_payload_to_hmac_pad(self, work_bytes): """ payload = payload ^ hmac(encryptionKey, initVector || counterBytes) """ payload_size = len(work_bytes) - self.OVERHEAD_SIZE sections = (payload_size + self.COUNTER_PAGESIZE - 1) // self.COUNTER_PAGESIZE if sections > self.COUNTER_SECTIONS: raise OverflowError( 'Payload is {} bytes, exceeds limit of {}'.format( payload_size, self.COUNTER_PAGESIZE * self.COUNTER_SECTIONS)) iv = six.binary_type(work_bytes[self.IV_START:self.IV_END]) pad = bytearray(self.COUNTER_PAGESIZE + 3) counter_size = 0 for section in xrange(0, sections): section_base = section * self.COUNTER_PAGESIZE section_size = min(payload_size - section_base, self.COUNTER_PAGESIZE) h = HMAC(self._keys.encryption_key, self.KEY_ALGORITHM(), backend=self._backend) h.update(iv) if counter_size != 0: pad_page = six.binary_type( pad[self.COUNTER_PAGESIZE:(self.COUNTER_PAGESIZE + counter_size)]) h.update(pad_page) pad[0:self.COUNTER_PAGESIZE] = h.finalize() for i in xrange(0, section_size): work_bytes[self.PAYLOAD_START + section_base + i] ^= pad[i] pad[0:self.COUNTER_PAGESIZE] = b'\x00' * self.COUNTER_PAGESIZE if counter_size == 0 or ++pad[self.COUNTER_PAGESIZE + counter_size - 1] == 0: counter_size += 1
def _encrypt_from_parts(self, data, current_time, iv): if not isinstance(data, bytes): raise TypeError("data must be bytes.") padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher( algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend ).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext ) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts) hmac = h.finalize() return basic_parts + hmac
def compress_and_encrypt( input_file: IOIter, output_file: IOIter, key_pair: Optional[bytes], options: OptionsDict, ) -> bytes: """ Read data from an open file descriptor, and write the compressed, encrypted data to another file descriptor; compute the HMAC of the encrypted data to ensure integrity :param input_file: an IOIter object to read plaintext data from :param output_file: an IOIter object to write compressed ciphertext to """ key, nonce = (key_pair[:AES_KEY_SIZE], key_pair[AES_KEY_SIZE:]) if key_pair else (b'', b'') compressobj = zlib.compressobj() zip_fn: Callable[[bytes], bytes] = ( # type: ignore compressobj.compress if options['use_compression'] else identity ) encrypt_fn: Callable[[bytes], bytes] = ( Cipher(AES(key), CTR(nonce), backend=default_backend()).encryptor().update if options['use_encryption'] else identity ) hmac = HMAC(key, SHA256(), default_backend()) def last_block() -> Generator[Tuple[bytes, bool], None, None]: yield (compressobj.flush(), False) if options['use_compression'] else (b'', False) writer = output_file.writer(); next(writer) logger.debug2('starting to compress') for block, needs_compression in chain(zip(input_file.reader(), repeat(True)), last_block()): if needs_compression: block = zip_fn(block) logger.debug2(f'zip_fn returned {len(block)} bytes') block = encrypt_fn(block) logger.debug2(f'encrypt_fn returned {len(block)} bytes') if options['use_encryption']: hmac.update(block) writer.send(block) if options['use_encryption']: return hmac.finalize() else: return b''
def test_decrypt_and_unpack_no_compression(caplog, mock_open_streams): orig, new, _ = mock_open_streams orig_contents = orig._fd.getvalue() cipher = Cipher(AES(TMP_KEY), CTR(TMP_NONCE), backend=default_backend()).encryptor() ct = cipher.update(orig_contents) hmac = HMAC(TMP_KEY, SHA256(), default_backend()) hmac.update(ct) signature = hmac.finalize() orig._fd.write(ct) decrypt_and_unpack( orig, new, TMP_KEYPAIR + signature, dict(use_compression=False, use_encryption=True), ) assert new._fd.getvalue() == orig_contents assert count_matching_log_lines('read 2 bytes from /orig', caplog) == 4 assert count_matching_log_lines('wrote 2 bytes to /new', caplog) == 4
def _encrypting_message(self, data, current_value, iv): # Get key key = base64.urlsafe_b64decode(self.key) # Pad data padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() # Get encryptor encryptor = Cipher( algorithms.AES(key[16:]), modes.CBC(iv), default_backend(), ).encryptor() # Encrypt text ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = b"\x80" + struct.pack(">Q", current_value) + iv + ciphertext h = HMAC(key[:16], hashes.SHA256(), backend=default_backend()) h.update(basic_parts) hmac = h.finalize() return base64.urlsafe_b64encode(basic_parts + hmac)
def _encrypt_from_parts(self, data, iv, associated_data): if not isinstance(data, bytes): raise TypeError("data must be bytes.") padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher( algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend ).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x81" + iv + ciphertext + associated_data ) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts) hmac = h.finalize() # TAG return base64.urlsafe_b64encode(b"\x81" + iv + ciphertext + hmac)
def symmetric_decrypt(data, key, hmac_key=None): enc_iv, enc_data = data[:16], data[16:] decryptor = Cipher(algorithms.AES(key), modes.ECB(), backend).decryptor() iv = decryptor.update(enc_iv) + decryptor.finalize() decryptor = Cipher(algorithms.AES(key), modes.CBC(iv), backend).decryptor() unenc_data = decryptor.update(enc_data) + decryptor.finalize() unpadder = PKCS7(128).unpadder() unenc_data = unpadder.update(unenc_data) + unpadder.finalize() if hmac_key: prefix = iv[13:] hmac = HMAC(hmac_key, SHA1(), backend) hmac.update(prefix + unenc_data) hmac_msg = hmac.finalize() if hmac_msg[:13] != iv[:13]: raise RuntimeError("Unable to decrypt message, HMAC doesn't match. %s:%s" % (hmac_msg, iv)) return unenc_data
def _encrypt_from_parts(self, data, iv, adata=""): if not isinstance(data, bytes): raise TypeError("data must be bytes.") padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher(algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend).encryptor() # ctx = AES( iv || msg ) ctx = encryptor.update(padded_data) + encryptor.finalize() basic_parts = (b"\x81" + iv + ctx) # print(str(len(basic_parts)), "basic_parts_len == ", basic_parts) # print("iv = " + str(len(iv)), iv) # print(str(len(ctx)), "ctx == ", ctx) # print("adata == " , len(adata), adata) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts + adata) # tag = HMAC( 0x81 || iv || ctx ) tag = h.finalize() # print("tag = " , len(tag)) return base64.urlsafe_b64encode(basic_parts + tag)
def test_hmac(): current_time = int(time.time()) salt = os.urandom(16) ciphertext = b'this is garbelled normally' key = pbkdf2_hmac('sha256', b"password", salt, 100000) iv = os.urandom(16) basic_parts = (b"\x80" + struct.pack(">Q", current_time) + iv + ciphertext) enc_key = base64.urlsafe_b64encode(key) hmac = HMAC.new(enc_key[:16], digestmod=SHA256) hmac.update(basic_parts) hmac = hmac.digest() from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.hmac import HMAC as CHMAC from cryptography.hazmat.backends import default_backend h = CHMAC(enc_key[:16], hashes.SHA256(), backend=default_backend()) h.update(basic_parts) chmac = h.finalize() assert hmac == chmac
class Encryptor: def __init__(self, rsa_public_key_pem): if not isinstance(rsa_public_key_pem, bytes): rsa_public_key_pem = rsa_public_key_pem.encode("ascii") self.rsa_public_key = serialization.load_pem_public_key(rsa_public_key_pem, backend=default_backend()) self.cipher = None self.authenticator = None def update(self, data): ret = b"" if self.cipher is None: key = os.urandom(16) nonce = os.urandom(16) auth_key = os.urandom(32) self.cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend()).encryptor() self.authenticator = HMAC(auth_key, SHA256(), backend=default_backend()) pad = padding.OAEP(mgf=padding.MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None) cipherkey = self.rsa_public_key.encrypt(key + nonce + auth_key, pad) ret = FILEMAGIC + struct.pack(">H", len(cipherkey)) + cipherkey cur = self.cipher.update(data) self.authenticator.update(cur) if ret: return ret + cur else: return cur def finalize(self): if self.cipher is None: return b"" # empty plaintext input yields empty encrypted output ret = self.cipher.finalize() self.authenticator.update(ret) ret += self.authenticator.finalize() self.cipher = None self.authenticator = None return ret
def _encrypt_from_parts(self, data, adata="", salt = "", signing_key = "", encryption_key = ""): if not isinstance(data, bytes): raise TypeError("data must be bytes.") # print("signing_key = " + str(len(signing_key)), signing_key) # print("encryption_key = " + str(len(encryption_key)), encryption_key) padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher( algorithms.AES(encryption_key), modes.CBC("0"*16), self._backend ).encryptor() # ctx = AES( iv || msg ) ctx = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x82" + salt + ctx ) h = HMAC(signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts + adata) # tag = HMAC( 0x81 || iv || ctx ) tag = h.finalize() return base64.urlsafe_b64encode( basic_parts + tag )
def signMsg(self, msg, password): ''' Signs the given message. This procedure ensures that a message can not be altered without detecting it. Parameters: - msg [bytes/str]: the message to sign. - password [bytes/str]: the password to use for signing the message. Return: [bytes]: the raw bytes string that contains both the messsage and the signature. Usage: - signed_msg = self.signMsg("my message", "my password") ''' msg = self.msgToBytes(msg) cred = self.getCredentialsFromPassword( password, length=self.hash_algorithm.digest_size) data = b"\x80" + cred["salt"] + msg # Generate the signature. The signature will have the same size as the hash algorithm digest. signer = HMAC(cred["key"], self.hash_algorithm, backend=default_backend()) signer.update(data) signature = signer.finalize() return data + signature
def encrypt_with_hmac(self, data, data_id, iv): if not isinstance(data, bytes): raise TypeError("data must be bytes.") if not isinstance(data_id, int): raise TypeError("data_id must be int.") main_parts = (struct.pack(config.FORMAT_CHAR, data_id) + data) # PKCS7 padding padded_data = self.add_padding(main_parts, algorithms.AES.block_size) # AES with CBC mode encryptor = Cipher(algorithms.AES(self.aes_key), modes.CBC(iv), backend=self.backend).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = (b"\x80" + iv + ciphertext) h = HMAC(self.mac_key, hashes.SHA256(), backend=self.backend) h.update(basic_parts) hmac = h.finalize() return basic_parts + hmac
def our_kdf(secret, extra_secret, info, output_len): """ Noise Key derivation function. Outputs a byte sequence that the caller typically splits into multiple variables such as a chain variable and cipher context, or two cipher contexts. Args: secret: secret for key derivation extra_secret: is used to pass a chaining variable to mix into the KDF. info: ensures that applying the KDF to the same secret values will produce independent output, provided 'info' is different. output_len: length out the output Returns: derived key of output_len bytes (BytesIO) """ # XXX either I used hkdf below wrong and never managed to use it correctly, or the function we're using ain't # XXX the RFC5869 HKDF. Anyway, this is compatible with the Java implementation we have. output = io.BytesIO() t = bytearray(H_LEN) for c in range(output_len // H_LEN + 1): bs = b''.join(( info, struct.pack('B', c), t[:32], extra_secret )) assert len(bs) == len(info) + 1 + 32 + len(extra_secret) hmac = HMAC(key=secret, algorithm=SHA512(), backend=backend) hmac.update(bs) t = hmac.finalize() output.write(t) output_val = output.getvalue()[:output_len] return io.BytesIO(output_val)
def _encrypt_from_parts(self, data, iv, adata=""): if not isinstance(data, bytes): raise TypeError("data must be bytes.") padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher( algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend ).encryptor() # ctx = AES( iv || msg ) ctx = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x81" + iv + ctx ) # print(str(len(basic_parts)), "basic_parts_len == ", basic_parts) # print("iv = " + str(len(iv)), iv) # print(str(len(ctx)), "ctx == ", ctx) # print("adata == " , len(adata), adata) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts+adata) # tag = HMAC( 0x81 || iv || ctx ) tag = h.finalize() # print("tag = " , len(tag)) return base64.urlsafe_b64encode( basic_parts + tag)
def sign(self, pairs): """ Generate a signature for a sequence of (key, value) pairs @param pairs: The pairs to sign, in order @type pairs: Iterable[six.text_type, six.text_type], six.binary_type is deprecated @return: The binary signature of this sequence of pairs @rtype: six.binary_type """ warning_msg = "Binary values for pairs are deprecated. Use text input instead." pairs = [(string_to_text(a, warning_msg), string_to_text(b, warning_msg)) for a, b in pairs] kv = kvform.seqToKV(pairs) try: algorithm = self.hmac_algorithms[self.assoc_type] except KeyError: raise ValueError( 'Unknown association type: %r' % (self.assoc_type,)) hmac = HMAC(self.secret, algorithm, backend=default_backend()) hmac.update(kv.encode('utf-8')) return hmac.finalize()
def decrypt_encryption_key_fallback(self): """Decrypts the encryption key using the FALLBACK method. In this method, the context string, usually "CredEncryption" or "PSEEncryption", is encrypted using a derivation of a fixed key hardcoded in CommonCryptoLib, and used as key to encrypt the actual encryption key used in the file with the AES cipher. :return: Encryption key decrypted :rtype: string """ log_lps.debug("Obtaining encryption key with FALLBACK LPS mode") digest = Hash(SHA1(), backend=default_backend()) digest.update(cred_key_lps_fallback) hashed_key = digest.finalize() hmac = HMAC(hashed_key, SHA1(), backend=default_backend()) hmac.update(self.context) default_key = hmac.finalize()[:16] iv = "\x00" * 16 decryptor = Cipher(algorithms.AES(default_key), modes.CBC(iv), backend=default_backend()).decryptor() encryption_key = decryptor.update(self.encrypted_key) + decryptor.finalize() return encryption_key
def _encrypt_from_parts( self, data: bytes, current_time: int, iv: bytes ) -> bytes: utils._check_bytes("data", data) padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher( algorithms.AES(self._encryption_key), modes.CBC(iv), ).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x80" + current_time.to_bytes(length=8, byteorder="big") + iv + ciphertext ) h = HMAC(self._signing_key, hashes.SHA256()) h.update(basic_parts) hmac = h.finalize() return base64.urlsafe_b64encode(basic_parts + hmac)
def _hmac(key, message, context): hmac = HMAC(key, SHA256(), context.crypto) hmac.update(message) return hmac.finalize()
def hmac(secret, digest_type, msg): tmp = HMAC(secret, digest_type, default_backend()) tmp.update(msg) return tmp.finalize()
def sign(self, method=methods.enveloped, algorithm="rsa-sha256", key=None, passphrase=None, cert=None, c14n_algorithm=default_c14n_algorithm, reference_uri=None, key_name=None): """ Sign the data and return the root element of the resulting XML tree. :param method: ``signxml.methods.enveloped``, ``signxml.methods.enveloping``, or ``signxml.methods.detached``. See the list of signature types under `XML Signature Syntax and Processing Version 2.0, Definitions <http://www.w3.org/TR/xmldsig-core2/#sec-Definitions>`_. :type method: :py:class:`methods` :param algorithm: Algorithm that will be used to generate the signature, composed of the signature algorithm and the digest algorithm, separated by a hyphen. All algorthm IDs listed under the `Algorithm Identifiers and Implementation Requirements <http://www.w3.org/TR/xmldsig-core1/#sec-AlgID>`_ section of the XML Signature 1.1 standard are supported. :type algorithm: string :param key: Key to be used for signing. When signing with a certificate or RSA/DSA/ECDSA key, this can be a string containing a PEM-formatted key, or a :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object. When signing with a HMAC, this should be a string containing the shared secret. :type key: string, :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object :param passphrase: Passphrase to use to decrypt the key, if any. :type passphrase: string :param cert: X.509 certificate to use for signing. This should be a string containing a PEM-formatted certificate, or an array of strings or OpenSSL.crypto.X509 objects containing the certificate and a chain of intermediate certificates. :type cert: string, array of strings, or array of OpenSSL.crypto.X509 objects :param c14n_algorithm: Canonicalization (c14n) algorithm to use. Supported algorithms are listed in the class variable ``xmldsig.known_c14n_algorithms``. :type c14n_algorithm: string :param reference_uri: Custom reference URI to incorporate into the signature. Only used when ``method`` is set to ``detached``. :type reference_uri: string :param key_name: Add a KeyName element in the KeyInfo element that may be used by the signer to communicate a key identifier to the recipient. Typically, KeyName contains an identifier related to the key pair used to sign the message :type key_name: string :returns: A :py:class:`lxml.etree.Element` object representing the root of the XML tree containing the signature and the payload data. To specify the location of an enveloped signature within **data**, insert a `<Signature Id="placeholder"></Signature>` element in **data**. This element will be replaced by the generated signature, and excised when generating the digest. """ self.signature_alg = algorithm self.key = key self._reference_uri = reference_uri if not isinstance(method, methods): raise InvalidInput("Unknown signature method {}".format(method)) if isinstance(cert, (str, bytes)): cert_chain = [cert] else: cert_chain = cert self.payload_c14n = self._get_payload_c14n(method, c14n_algorithm=c14n_algorithm) self.digest = self._get_digest(self.payload_c14n, self._get_digest_method_by_tag(self.digest_alg)) signed_info = SubElement(self.sig_root, ds_tag("SignedInfo"), nsmap=self.namespaces) c14n_method = SubElement(signed_info, ds_tag("CanonicalizationMethod"), Algorithm=c14n_algorithm) if self.signature_alg.startswith("hmac-"): algorithm_id = self.known_hmac_digest_tags[self.signature_alg] else: algorithm_id = self.known_signature_digest_tags[self.signature_alg] signature_method = SubElement(signed_info, ds_tag("SignatureMethod"), Algorithm=algorithm_id) reference = SubElement(signed_info, ds_tag("Reference"), URI=self._reference_uri) if method == methods.enveloped: transforms = SubElement(reference, ds_tag("Transforms")) SubElement(transforms, ds_tag("Transform"), Algorithm=namespaces.ds + "enveloped-signature") SubElement(transforms, ds_tag("Transform"), Algorithm=c14n_algorithm) digest_method = SubElement(reference, ds_tag("DigestMethod"), Algorithm=self.known_digest_tags[self.digest_alg]) digest_value = SubElement(reference, ds_tag("DigestValue")) digest_value.text = self.digest signature_value = SubElement(self.sig_root, ds_tag("SignatureValue")) signed_info_c14n = self._c14n(signed_info, algorithm=c14n_algorithm) if self.signature_alg.startswith("hmac-"): from cryptography.hazmat.primitives.hmac import HMAC signer = HMAC(key=self.key, algorithm=self._get_hmac_digest_method_by_tag(self.signature_alg), backend=default_backend()) signer.update(signed_info_c14n) signature_value.text = ensure_str(b64encode(signer.finalize())) self.sig_root.append(signature_value) elif self.signature_alg.startswith("dsa-") or self.signature_alg.startswith("rsa-") or self.signature_alg.startswith("ecdsa-"): if isinstance(self.key, (str, bytes)): from cryptography.hazmat.primitives.serialization import load_pem_private_key key = load_pem_private_key(self.key, password=passphrase, backend=default_backend()) else: key = self.key hash_alg = self._get_signature_digest_method_by_tag(self.signature_alg) if self.signature_alg.startswith("dsa-"): signer = key.signer(signature_algorithm=hash_alg) elif self.signature_alg.startswith("ecdsa-"): signer = key.signer(signature_algorithm=ec.ECDSA(algorithm=hash_alg)) elif self.signature_alg.startswith("rsa-"): signer = key.signer(padding=PKCS1v15(), algorithm=hash_alg) else: raise NotImplementedError() signer.update(signed_info_c14n) signature = signer.finalize() if self.signature_alg.startswith("dsa-"): # Note: The output of the DSA signer is a DER-encoded ASN.1 sequence of two DER integers. decoded_signature = der_decoder.decode(signature)[0] r = decoded_signature.getComponentByPosition(0) s = decoded_signature.getComponentByPosition(1) signature = long_to_bytes(r).rjust(32, b"\0") + long_to_bytes(s).rjust(32, b"\0") signature_value.text = ensure_str(b64encode(signature)) key_info = SubElement(self.sig_root, ds_tag("KeyInfo")) if key_name is not None: keyname = SubElement(key_info, ds_tag("KeyName")) keyname.text = key_name if cert_chain is None: self._serialize_key_value(key, key_info) else: x509_data = SubElement(key_info, ds_tag("X509Data")) for cert in cert_chain: x509_certificate = SubElement(x509_data, ds_tag("X509Certificate")) if isinstance(cert, (str, bytes)): x509_certificate.text = strip_pem_header(cert) else: from OpenSSL.crypto import dump_certificate, FILETYPE_PEM x509_certificate.text = dump_certificate(FILETYPE_PEM, cert) else: raise NotImplementedError() if method == methods.enveloped: return self.payload elif method == methods.enveloping: self.sig_root.append(self.payload) return self.sig_root elif method == methods.detached: return self.sig_root
def sign(self, data, key=None, passphrase=None, cert=None, reference_uri=None, key_name=None, key_info=None, id_attribute=None): """ Sign the data and return the root element of the resulting XML tree. :param data: Data to sign :type data: String, file-like object, or XML ElementTree Element API compatible object :param key: Key to be used for signing. When signing with a certificate or RSA/DSA/ECDSA key, this can be a string containing a PEM-formatted key, or a :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object. When signing with a HMAC, this should be a string containing the shared secret. :type key: string, :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object :param passphrase: Passphrase to use to decrypt the key, if any. :type passphrase: string :param cert: X.509 certificate to use for signing. This should be a string containing a PEM-formatted certificate, or an array of strings or OpenSSL.crypto.X509 objects containing the certificate and a chain of intermediate certificates. :type cert: string, array of strings, or array of OpenSSL.crypto.X509 objects :param reference_uri: Custom reference URI or list of reference URIs to incorporate into the signature. When ``method`` is set to ``detached`` or ``enveloped``, reference URIs are set to this value and only the referenced elements are signed. :type reference_uri: string or list :param key_name: Add a KeyName element in the KeyInfo element that may be used by the signer to communicate a key identifier to the recipient. Typically, KeyName contains an identifier related to the key pair used to sign the message. :type key_name: string :param key_info: A custom KeyInfo element to insert in the signature. Use this to supply ``<wsse:SecurityTokenReference>`` or other custom key references. :type key_info: :py:class:`lxml.etree.Element` :param id_attribute: Name of the attribute whose value ``URI`` refers to. By default, SignXML will search for "Id", then "ID". :type id_attribute: string :returns: A :py:class:`lxml.etree.Element` object representing the root of the XML tree containing the signature and the payload data. To specify the location of an enveloped signature within **data**, insert a ``<ds:Signature Id="placeholder"></ds:Signature>`` element in **data** (where "ds" is the "http://www.w3.org/2000/09/xmldsig#" namespace). This element will be replaced by the generated signature, and excised when generating the digest. """ if id_attribute is not None: self.id_attributes = (id_attribute, ) if isinstance(cert, (str, bytes)): cert_chain = list(iterate_pem(cert)) else: cert_chain = cert if isinstance(reference_uri, (str, bytes)): reference_uris = [reference_uri] else: reference_uris = reference_uri sig_root, doc_root, c14n_inputs, reference_uris = self._unpack(data, reference_uris) signed_info_element, signature_value_element = self._build_sig(sig_root, reference_uris, c14n_inputs) if key is None: raise InvalidInput('Parameter "key" is required') signed_info_c14n = self._c14n(signed_info_element, algorithm=self.c14n_alg) if self.sign_alg.startswith("hmac-"): from cryptography.hazmat.primitives.hmac import HMAC signer = HMAC(key=key, algorithm=self._get_hmac_digest_method_by_tag(self.sign_alg), backend=default_backend()) signer.update(signed_info_c14n) signature_value_element.text = ensure_str(b64encode(signer.finalize())) sig_root.append(signature_value_element) elif any(self.sign_alg.startswith(i) for i in ["dsa-", "rsa-", "ecdsa-"]): if isinstance(key, (str, bytes)): from cryptography.hazmat.primitives.serialization import load_pem_private_key key = load_pem_private_key(key, password=passphrase, backend=default_backend()) hash_alg = self._get_signature_digest_method_by_tag(self.sign_alg) if self.sign_alg.startswith("dsa-"): signature = key.sign(signed_info_c14n, algorithm=hash_alg) elif self.sign_alg.startswith("ecdsa-"): signature = key.sign(signed_info_c14n, signature_algorithm=ec.ECDSA(algorithm=hash_alg)) elif self.sign_alg.startswith("rsa-"): signature = key.sign(signed_info_c14n, padding=PKCS1v15(), algorithm=hash_alg) else: raise NotImplementedError() if self.sign_alg.startswith("dsa-"): # Note: The output of the DSA signer is a DER-encoded ASN.1 sequence of two DER integers. from asn1crypto.algos import DSASignature decoded_signature = DSASignature.load(signature).native r = decoded_signature['r'] s = decoded_signature['s'] signature = long_to_bytes(r).rjust(32, b"\0") + long_to_bytes(s).rjust(32, b"\0") signature_value_element.text = ensure_str(b64encode(signature)) if key_info is None: key_info = SubElement(sig_root, ds_tag("KeyInfo")) if key_name is not None: keyname = SubElement(key_info, ds_tag("KeyName")) keyname.text = key_name if cert_chain is None: self._serialize_key_value(key, key_info) else: x509_data = SubElement(key_info, ds_tag("X509Data")) for cert in cert_chain: x509_certificate = SubElement(x509_data, ds_tag("X509Certificate")) if isinstance(cert, (str, bytes)): x509_certificate.text = strip_pem_header(cert) else: from OpenSSL.crypto import dump_certificate, FILETYPE_PEM x509_certificate.text = strip_pem_header(dump_certificate(FILETYPE_PEM, cert)) else: sig_root.append(key_info) else: raise NotImplementedError() if self.method == methods.enveloping: for c14n_input in c14n_inputs: doc_root.append(c14n_input) return doc_root if self.method == methods.enveloped else sig_root
class Decryptor: def __init__(self, rsa_private_key_pem): if not isinstance(rsa_private_key_pem, bytes): rsa_private_key_pem = rsa_private_key_pem.encode("ascii") self.rsa_private_key = serialization.load_pem_private_key( data=rsa_private_key_pem, password=None, backend=default_backend()) self.cipher = None self.authenticator = None self._cipher_key_len = None self._header_size = None self._footer_size = 32 def expected_header_bytes(self): if self._header_size is not None: return 0 return self._cipher_key_len or 8 def header_size(self): return self._header_size def footer_size(self): return self._footer_size def process_header(self, data): if self._cipher_key_len is None: if data[0:6] != FILEMAGIC: raise EncryptorError("Invalid magic bytes") self._cipher_key_len = struct.unpack(">H", data[6:8])[0] else: pad = padding.OAEP(mgf=padding.MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None) try: plainkey = self.rsa_private_key.decrypt(data, pad) except AssertionError: raise EncryptorError("Decrypting key data failed") if len(plainkey) != 64: raise EncryptorError("Integrity check failed") key = plainkey[0:16] nonce = plainkey[16:32] auth_key = plainkey[32:64] self._header_size = 8 + len(data) self.cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend()).decryptor() self.authenticator = HMAC(auth_key, SHA256(), backend=default_backend()) def process_data(self, data): if not data: return b"" self.authenticator.update(data) return self.cipher.update(data) def finalize(self, footer): if footer != self.authenticator.finalize(): raise EncryptorError("Integrity check failed") result = self.cipher.finalize() self.cipher = None self.authenticator = None return result
def sign(self, data, key=None, passphrase=None, cert=None, reference_uri=None, key_name=None): """ Sign the data and return the root element of the resulting XML tree. :param data: Data to sign :type data: String, file-like object, or XML ElementTree Element API compatible object :param key: Key to be used for signing. When signing with a certificate or RSA/DSA/ECDSA key, this can be a string containing a PEM-formatted key, or a :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object. When signing with a HMAC, this should be a string containing the shared secret. :type key: string, :py:class:`cryptography.hazmat.primitives.interfaces.RSAPublicKey`, :py:class:`cryptography.hazmat.primitives.interfaces.DSAPublicKey`, or :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePublicKey` object :param passphrase: Passphrase to use to decrypt the key, if any. :type passphrase: string :param cert: X.509 certificate to use for signing. This should be a string containing a PEM-formatted certificate, or an array of strings or OpenSSL.crypto.X509 objects containing the certificate and a chain of intermediate certificates. :type cert: string, array of strings, or array of OpenSSL.crypto.X509 objects :param reference_uri: Custom reference URI to incorporate into the signature. Only used when ``method`` is set to ``detached``. :type reference_uri: string :param key_name: Add a KeyName element in the KeyInfo element that may be used by the signer to communicate a key identifier to the recipient. Typically, KeyName contains an identifier related to the key pair used to sign the message. :type key_name: string :returns: A :py:class:`lxml.etree.Element` object representing the root of the XML tree containing the signature and the payload data. To specify the location of an enveloped signature within **data**, insert a `<Signature Id="placeholder"></Signature>` element in **data**. This element will be replaced by the generated signature, and excised when generating the digest. """ if isinstance(cert, (str, bytes)): cert_chain = [cert] else: cert_chain = cert sig_root, doc_root, c14n_input, reference_uri = self._unpack(data, reference_uri) payload_c14n = self._c14n(c14n_input, algorithm=self.c14n_alg) digest = self._get_digest(payload_c14n, self._get_digest_method_by_tag(self.digest_alg)) signed_info_element, signature_value_element = self._build_sig(sig_root, reference_uri, digest) signed_info_c14n = self._c14n(signed_info_element, algorithm=self.c14n_alg) if self.sign_alg.startswith("hmac-"): from cryptography.hazmat.primitives.hmac import HMAC signer = HMAC(key=key, algorithm=self._get_hmac_digest_method_by_tag(self.sign_alg), backend=default_backend()) signer.update(signed_info_c14n) signature_value_element.text = ensure_str(b64encode(signer.finalize())) sig_root.append(signature_value_element) elif any(self.sign_alg.startswith(i) for i in ["dsa-", "rsa-", "ecdsa-"]): if isinstance(key, (str, bytes)): from cryptography.hazmat.primitives.serialization import load_pem_private_key key = load_pem_private_key(key, password=passphrase, backend=default_backend()) hash_alg = self._get_signature_digest_method_by_tag(self.sign_alg) if self.sign_alg.startswith("dsa-"): signer = key.signer(signature_algorithm=hash_alg) elif self.sign_alg.startswith("ecdsa-"): signer = key.signer(signature_algorithm=ec.ECDSA(algorithm=hash_alg)) elif self.sign_alg.startswith("rsa-"): signer = key.signer(padding=PKCS1v15(), algorithm=hash_alg) else: raise NotImplementedError() signer.update(signed_info_c14n) signature = signer.finalize() if self.sign_alg.startswith("dsa-"): # Note: The output of the DSA signer is a DER-encoded ASN.1 sequence of two DER integers. decoded_signature = der_decoder.decode(signature)[0] r = decoded_signature.getComponentByPosition(0) s = decoded_signature.getComponentByPosition(1) signature = long_to_bytes(r).rjust(32, b"\0") + long_to_bytes(s).rjust(32, b"\0") signature_value_element.text = ensure_str(b64encode(signature)) key_info = SubElement(sig_root, ds_tag("KeyInfo")) if key_name is not None: keyname = SubElement(key_info, ds_tag("KeyName")) keyname.text = key_name if cert_chain is None: self._serialize_key_value(key, key_info) else: x509_data = SubElement(key_info, ds_tag("X509Data")) for cert in cert_chain: x509_certificate = SubElement(x509_data, ds_tag("X509Certificate")) if isinstance(cert, (str, bytes)): x509_certificate.text = strip_pem_header(cert) else: from OpenSSL.crypto import dump_certificate, FILETYPE_PEM x509_certificate.text = dump_certificate(FILETYPE_PEM, cert) else: raise NotImplementedError() if self.method == methods.enveloping: doc_root.append(c14n_input) return doc_root
def verify(self, data, require_x509=True, x509_cert=None, ca_pem_file=None, ca_path=None, hmac_key=None, validate_schema=True, parser=None, uri_resolver=None, id_attribute=None): """ Verify the XML signature supplied in the data and return the XML node signed by the signature, or raise an exception if the signature is not valid. By default, this requires the signature to be generated using a valid X.509 certificate. To enable other means of signature validation, set the **require_x509** argument to `False`. .. admonition:: See what is signed It is important to understand and follow the best practice rule of "See what is signed" when verifying XML signatures. The gist of this rule is: if your application neglects to verify that the information it trusts is what was actually signed, the attacker can supply a valid signature but point you to malicious data that wasn't signed by that signature. In SignXML, you can ensure that the information signed is what you expect to be signed by only trusting the data returned by the ``verify()`` method. The return value is the XML node or string that was signed. Also, depending on the signature settings used, comments in the XML data may not be subject to signing, so may need to be untrusted. **Recommended reading:** http://www.w3.org/TR/xmldsig-bestpractices/#practices-applications TODO: CN verification :param data: Signature data to verify :type data: String, file-like object, or XML ElementTree Element API compatible object :param require_x509: If ``True``, a valid X.509 certificate-based signature is required to pass validation. If ``False``, other types of valid signatures (e.g. HMAC or RSA public key) are accepted. :type require_x509: boolean :param x509_cert: An external X.509 certificate, given as a PEM-formatted string or OpenSSL.crypto.X509 object, to use for verification. Overrides any X.509 certificate information supplied by the signature. If left set to ``None``, requires that the signature supply a valid X.509 certificate chain that validates against the known certificate authorities. Implies **require_x509=True**. :type x509_cert: string or OpenSSL.crypto.X509 :param ca_pem_file: Filename of a PEM file containing certificate authority information to use when verifying certificate-based signatures. :type ca_pem_file: string or bytes :param ca_path: Path to a directory containing PEM-formatted certificate authority files to use when verifying certificate-based signatures. If neither **ca_pem_file** nor **ca_path** is given, the Mozilla CA bundle provided by :py:mod:`certifi` will be loaded. :type ca_path: string :param hmac_key: If using HMAC, a string containing the shared secret. :type hmac_key: string :param validate_schema: Whether to validate **data** against the XML Signature schema. :type validate_schema: boolean :param parser: Custom XML parser instance to use when parsing **data**. :type parser: :py:class:`lxml.etree.XMLParser` compatible parser :param uri_resolver: Function to use to resolve reference URIs that don't start with "#". :type uri_resolver: callable :param id_attribute: Name of the attribute whose value ``URI`` refers to. By default, SignXML will search for "Id", then "ID". :type id_attribute: string :raises: :py:class:`cryptography.exceptions.InvalidSignature` :returns: VerifyResult object with the signed data, signed xml and signature xml :rtype: VerifyResult """ self.hmac_key = hmac_key self.require_x509 = require_x509 self.x509_cert = x509_cert self._parser = parser if x509_cert: self.require_x509 = True if id_attribute is None: self.id_attributes = ("Id", "ID", "id", "xml:id") else: self.id_attributes = (id_attribute, ) root = self.get_root(data) if root.tag == ds_tag("Signature"): signature_ref = root else: signature_ref = self._find(root, "Signature", anywhere=True) # HACK: deep copy won't keep root's namespaces signature = fromstring(etree.tostring(signature_ref), parser=parser) if validate_schema: self.schema().assertValid(signature) signed_info = self._find(signature, "SignedInfo") c14n_method = self._find(signed_info, "CanonicalizationMethod") c14n_algorithm = c14n_method.get("Algorithm") reference = self._find(signed_info, "Reference") transforms = self._find(reference, "Transforms", require=False) signed_info_c14n = self._c14n(signed_info, algorithm=c14n_algorithm) digest_algorithm = self._find(reference, "DigestMethod").get("Algorithm") digest_value = self._find(reference, "DigestValue") signature_method = self._find(signed_info, "SignatureMethod") signature_value = self._find(signature, "SignatureValue") signature_alg = signature_method.get("Algorithm") raw_signature = b64decode(signature_value.text) x509_data = signature.find("ds:KeyInfo/ds:X509Data", namespaces=namespaces) if x509_data is not None or self.require_x509: from OpenSSL.crypto import load_certificate, X509, FILETYPE_PEM, verify, Error as OpenSSLCryptoError if self.x509_cert is None: if x509_data is None: raise InvalidInput("Expected a X.509 certificate based signature") certs = [cert.text for cert in self._findall(x509_data, "X509Certificate")] if not certs: msg = "Expected to find an X509Certificate element in the signature (X509SubjectName, X509SKI are not supported)" # noqa raise InvalidInput(msg) cert_chain = [load_certificate(FILETYPE_PEM, add_pem_header(cert)) for cert in certs] verify_x509_cert_chain(cert_chain, ca_pem_file=ca_pem_file, ca_path=ca_path) elif isinstance(self.x509_cert, X509): cert_chain = [self.x509_cert] else: cert_chain = [load_certificate(FILETYPE_PEM, add_pem_header(self.x509_cert))] signature_digest_method = self._get_signature_digest_method(signature_alg).name try: verify(cert_chain[-1], raw_signature, signed_info_c14n, signature_digest_method) except OpenSSLCryptoError as e: try: lib, func, reason = e.message[0] except Exception: reason = e raise InvalidSignature("Signature verification failed: {}".format(reason)) elif "hmac-sha" in signature_alg: if self.hmac_key is None: raise InvalidInput('Parameter "hmac_key" is required when verifying a HMAC signature') from cryptography.hazmat.primitives.hmac import HMAC signer = HMAC(key=ensure_bytes(self.hmac_key), algorithm=self._get_hmac_digest_method(signature_alg), backend=default_backend()) signer.update(signed_info_c14n) if raw_signature != signer.finalize(): raise InvalidSignature("Signature mismatch (HMAC)") else: key_value = signature.find("ds:KeyInfo/ds:KeyValue", namespaces=namespaces) if key_value is None: raise InvalidInput("Expected to find either KeyValue or X509Data XML element in KeyInfo") self._verify_signature_with_pubkey(signed_info_c14n, raw_signature, key_value, signature_alg) payload = self._resolve_reference(root, reference, uri_resolver=uri_resolver) payload_c14n = self._apply_transforms(payload, transforms, signature_ref, c14n_algorithm) if digest_value.text != self._get_digest(payload_c14n, self._get_digest_method(digest_algorithm)): raise InvalidDigest("Digest mismatch") # We return the signed XML (and only that) to ensure no access to unsigned data happens try: payload_c14n_xml = fromstring(payload_c14n) except etree.XMLSyntaxError: payload_c14n_xml = None return VerifyResult(payload_c14n, payload_c14n_xml, signature)
loads(PeerInfo, object_pairs_hook=OrderedDict), KeyingMode, "", Ns2_b64, "", Np2_b64, ""] MACs2_input = HMAC(Kms2, SHA256(), backend=default_backend()) MACs2_input.update(dumps(MACs2_values['MACs2'], separators=(',', ':')).encode()) # MACp2 MACp2_values = loads('{"MACp2":[]}', object_pairs_hook=OrderedDict) MACp2_values['MACp2'] = [1, Vers, Verp, PeerId, Cryptosuites, "", loads(ServerInfo, object_pairs_hook=OrderedDict), Cryptosuitep, "", Realm, loads(PeerInfo, object_pairs_hook=OrderedDict), KeyingMode, "", Ns2_b64, "", Np2_b64, ""] MACp2_input = HMAC(Kmp2, SHA256(), backend=default_backend()) MACp2_input.update(dumps(MACp2_values['MACp2'], separators=(',', ':')).encode()) # MAC - base64 encoded MACs = base64url_encode(MACs_input.finalize()[:32]).decode().strip('=') MACp = base64url_encode(MACp_input.finalize()[:32]).decode().strip('=') MACs2 = base64url_encode(MACs2_input.finalize()[:32]).decode().strip('=') MACp2 = base64url_encode(MACp2_input.finalize()[:32]).decode().strip('=') ################################################################################ ############################## CREATE JSON ARRAYS ############################## # REQUEST/RESPONSE 1 req1 = loads( '{"Type":1, "Vers":"", "PeerId":"", "Realm":"", "Cryptosuites":"",\ "Dirs":"", "ServerInfo":""}' , object_pairs_hook = OrderedDict ) res1 = loads( '{"Type":1, "Verp":"", "PeerId":"", "Cryptosuitep":"", "Dirp":"",\ "PeerInfo":""}'