def signed_unpack(data, hmac_data, hmac_keys): """Unpack data and check that it was signed with hmac_key. :param data: json string that was singed_packed. :param hmac_data: hmac data that was generated from json by hmac_key on user side :param hmac_keys: server side hmac_keys, one of these should be the same as user used to sign with :returns: None in case of something wrong, Object in case of everything OK. """ # NOTE(boris-42): For security reason, if there is no hmac_data or # hmac_keys we don't trust data => return None. if not (hmac_keys and hmac_data): return None hmac_data = hmac_data.strip() if not hmac_data: return None for hmac_key in hmac_keys: try: user_hmac_data = generate_hmac(data, hmac_key) except Exception: # nosec pass else: if secretutils.constant_time_compare(hmac_data, user_hmac_data): try: contents = json.loads( binary_decode(base64.urlsafe_b64decode(data))) contents["hmac_key"] = hmac_key return contents except Exception: return None return None
def signed_unpack(data, hmac_data, hmac_keys): """Unpack data and check that it was signed with hmac_key. :param data: json string that was singed_packed. :param hmac_data: hmac data that was generated from json by hmac_key on user side :param hmac_keys: server side hmac_keys, one of these should be the same as user used to sign with :returns: None in case of something wrong, Object in case of everything OK. """ # NOTE(boris-42): For security reason, if there is no hmac_data or # hmac_keys we don't trust data => return None. if not (hmac_keys and hmac_data): return None hmac_data = hmac_data.strip() if not hmac_data: return None for hmac_key in hmac_keys: try: user_hmac_data = generate_hmac(data, hmac_key) except Exception: # nosec pass else: if secretutils.constant_time_compare(hmac_data, user_hmac_data): try: contents = json.loads( binary_decode(base64.urlsafe_b64decode(data))) contents["hmac_key"] = hmac_key return contents except Exception: return None return None
def unprotect_data(keys, signed_data): """De-serialize data given a dict of keys. Given keys and cached string data, verifies the signature, decrypts if necessary, and returns the original serialized data. """ # cache backends return None when no data is found. We don't mind # that this particular special value is unsigned. if signed_data is None: return None # First we calculate the signature provided_mac = signed_data[:DIGEST_LENGTH_B64] calculated_mac = sign_data( keys['MAC'], signed_data[DIGEST_LENGTH_B64:]) # Then verify that it matches the provided value if not secretutils.constant_time_compare(provided_mac, calculated_mac): raise InvalidMacError(_('Invalid MAC; data appears to be corrupted.')) data = base64.b64decode(signed_data[DIGEST_LENGTH_B64:]) # then if necessary decrypt the data if keys['strategy'] == b'ENCRYPT': data = decrypt_data(keys['ENCRYPTION'], data) return data
def _validate_shared_secret(self, requestor_id, signature, requestor_address): expected_signature = hmac.new( CONF.neutron.metadata_proxy_shared_secret, requestor_id, hashlib.sha256 ).hexdigest() if not secutils.constant_time_compare(expected_signature, signature): if requestor_id: LOG.warning( _LW( "X-Instance-ID-Signature: %(signature)s does " "not match the expected value: " "%(expected_signature)s for id: " "%(requestor_id)s. Request From: " "%(requestor_address)s" ), { "signature": signature, "expected_signature": expected_signature, "requestor_id": requestor_id, "requestor_address": requestor_address, }, ) msg = _("Invalid proxy request signature.") raise webob.exc.HTTPForbidden(explanation=msg)
def login(ra_name, user, secret): """Validates a user supplied user/password against an expected value. The expected value is pulled from the pecan config. Note that this value is currently stored in the clear inside that config, so we are assuming that the config is protected using file perms, etc. This function provides some resistance to timing attacks, but information on the expected user/password lengths can still be leaked. It may also be possible to use a timing attack to see which input failed validation. See comments below for details. :param ra_name: name of the registration authority :param user: The user supplied username (unicode or string) :param secret: The user supplied password (unicode or string) :return: None on failure or an AuthDetails object on success """ auth_conf = jsonloader.authentication_for_registration_authority(ra_name) # convert input to strings user = str(user) secret = str(secret) # expected values try: expected_user = str(auth_conf['user']) expected_secret = str(auth_conf['secret']) except (KeyError, TypeError): logger.warning("auth conf missing static user or secret") return None # This technique is used to provide a constant time string compare # between the user input and the expected values. valid_user = util.constant_time_compare(user, expected_user) valid_secret = util.constant_time_compare(secret, expected_secret) # This if statement results in a potential timing attack where the # statement could return more quickly if valid_secret=False. We # do not see an obvious solution to this problem, but also believe # that leaking which input was valid isn't as big of a concern. if valid_user and valid_secret: return results.AuthDetails(username=expected_user, groups=[]) logger.info("failed static auth for user {}".format(user))
def unwrap_envelope(envelope, key): payload = envelope[:-hash_len] expected_hmc = envelope[-hash_len:] calculated_hmc = get_hmac(payload, key) if not secretutils.constant_time_compare(expected_hmc, calculated_hmc): LOG.warning(_LW('calculated hmac: %(s1)s not equal to msg hmac: ' '%(s2)s dropping packet'), {'s1': to_hex(calculated_hmc), 's2': to_hex(expected_hmc)}) fmt = 'calculated hmac: {0} not equal to msg hmac: {1} dropping packet' raise exceptions.InvalidHMACException(fmt.format( to_hex(calculated_hmc), to_hex(expected_hmc))) obj = decode_obj(payload) return obj
def unwrap_envelope(envelope, key): payload = envelope[:-hash_len] expected_hmc = envelope[-hash_len:] calculated_hmc = get_hmac(payload, key) if not secretutils.constant_time_compare(expected_hmc, calculated_hmc): LOG.warn( _LW('calculated hmac: %(s1)s not equal to msg hmac: ' '%(s2)s dropping packet'), { 's1': to_hex(calculated_hmc), 's2': to_hex(expected_hmc) }) fmt = 'calculated hmac: {0} not equal to msg hmac: {1} dropping packet' raise exceptions.InvalidHMACException( fmt.format(to_hex(calculated_hmc), to_hex(expected_hmc))) obj = decode_obj(payload) return obj
def get_payload(envelope, key, hex=True): len = hex_hash_len if hex else hash_len payload = envelope[:-len] expected_hmc = envelope[-len:] calculated_hmc = get_hmac(payload, key, hex=hex) if not secretutils.constant_time_compare(expected_hmc, calculated_hmc): LOG.warning( 'calculated hmac(hex=%(hex)s): %(s1)s not equal to msg hmac: ' '%(s2)s dropping packet', { 'hex': hex, 's1': to_hex(calculated_hmc), 's2': to_hex(expected_hmc) }) fmt = 'calculated hmac: {0} not equal to msg hmac: {1} dropping packet' raise exceptions.InvalidHMACException( fmt.format(to_hex(calculated_hmc), to_hex(expected_hmc))) obj = decode_obj(payload) return obj
def _validate_shared_secret(self, requestor_id, signature, requestor_address): expected_signature = hmac.new( CONF.neutron.metadata_proxy_shared_secret, requestor_id, hashlib.sha256).hexdigest() if not secutils.constant_time_compare(expected_signature, signature): if requestor_id: LOG.warning(_LW('X-Instance-ID-Signature: %(signature)s does ' 'not match the expected value: ' '%(expected_signature)s for id: ' '%(requestor_id)s. Request From: ' '%(requestor_address)s'), {'signature': signature, 'expected_signature': expected_signature, 'requestor_id': requestor_id, 'requestor_address': requestor_address}) msg = _('Invalid proxy request signature.') raise webob.exc.HTTPForbidden(explanation=msg)
def verify_signature(message, secret): """Check the signature in the message. Message is verified against the value computed from the rest of the contents. """ if not secret: return True old_sig = message.get('message_signature', '') new_sig = compute_signature(message, secret) if isinstance(old_sig, str): try: old_sig = old_sig.encode('ascii') except UnicodeDecodeError: return False new_sig = new_sig.encode('ascii') return secretutils.constant_time_compare(new_sig, old_sig)
def _validate_shared_secret(self, requestor_id, signature, requestor_address): expected_signature = hmac.new( encodeutils.to_utf8(CONF.neutron.metadata_proxy_shared_secret), encodeutils.to_utf8(requestor_id), hashlib.sha256).hexdigest() if (not signature or not secutils.constant_time_compare(expected_signature, signature)): if requestor_id: LOG.warning('X-Instance-ID-Signature: %(signature)s does ' 'not match the expected value: ' '%(expected_signature)s for id: ' '%(requestor_id)s. Request From: ' '%(requestor_address)s', {'signature': signature, 'expected_signature': expected_signature, 'requestor_id': requestor_id, 'requestor_address': requestor_address}) msg = _('Invalid proxy request signature.') raise webob.exc.HTTPForbidden(explanation=msg)
def get_payload(envelope, key, hex=True): len = hex_hash_len if hex else hash_len payload = envelope[:-len] expected_hmc = envelope[-len:] calculated_hmc = get_hmac(payload, key, hex=hex) if not secretutils.constant_time_compare(expected_hmc, calculated_hmc): LOG.warning( 'calculated hmac(hex=%(hex)s): %(s1)s not equal to msg hmac: ' '%(s2)s dropping packet', { 'hex': hex, 's1': to_hex(calculated_hmc), 's2': to_hex(expected_hmc) } ) fmt = 'calculated hmac: {0} not equal to msg hmac: {1} dropping packet' raise exceptions.InvalidHMACException(fmt.format( to_hex(calculated_hmc), to_hex(expected_hmc))) obj = decode_obj(payload) return obj
def verify_signature(message, secret): """Check the signature in the message. Message is verified against the value computed from the rest of the contents. """ if not secret: return True old_sig = message.get('message_signature', '') new_sig = compute_signature(message, secret) if isinstance(old_sig, six.text_type): try: old_sig = old_sig.encode('ascii') except UnicodeDecodeError: return False if six.PY3: new_sig = new_sig.encode('ascii') return secretutils.constant_time_compare(new_sig, old_sig)
from oslo_utils import secretutils print(secretutils.constant_time_compare('a', 'a')) print(secretutils.constant_time_compare('a', 'b')) print(secretutils.constant_time_compare('a', 'ab')) print(secretutils.constant_time_compare(b'a', b'a')) print(secretutils.constant_time_compare(b'a', b'b')) print(secretutils.constant_time_compare(b'a', b'ab'))