Exemplo n.º 1
0
def get_wrapped_key_as_jwe(priv_dek: bytearray,
                           tenant: str,
                           jwe_kid: str,
                           nonce: str = '') -> str:
    """Creates a JWE."""
    trace_enter(inspect.currentframe())

    logger.info('Creating JWE token for request with kid "%s"...', jwe_kid)

    # Generate a 256 bit AES content encryption key (32 bytes * 8).
    try:
        cek = bytearray(get_random_bytes(32))
    except Exception as exc:
        ret = ''
        logger.error('Failed to get random bytes: %s', exc)
        trace_exit(inspect.currentframe(), ret)
        return ret

    if config.get_config_by_keypath('DEV_MODE'):
        logger.debug('Generated cek (BYOK AES key): %s (hex)', cek.hex())

    if not (b64_cek_ciphertext := _encrypt_cek_with_key_consumer_key(
            tenant, jwe_kid, cek)):

        logger.error(
            'Cannot encrypt content encryption key with key consumer '
            'key of %s/%s.', tenant, jwe_kid)

        trace_exit(inspect.currentframe(), '')
        return ''
Exemplo n.º 2
0
def _create_jwe_token_json(jwe_kid: str, b64_protected_header: bytes,
                           b64_cek_ciphertext: bytes, b64_iv: bytes,
                           b64_encrypted_dek: bytes, b64_tag: bytes) -> str:
    """
    Creates JWE token according to:
        https://tools.ietf.org/html/rfc7516#section-3.3

    Compact Serialization representation:
        BASE64URL(UTF8(JWE Protected Header)) || '.' ||
        BASE64URL(JWE Encrypted Key) || '.' ||
        BASE64URL(JWE Initialization Vector) || '.' ||
        BASE64URL(JWE Ciphertext) || '.' ||
        BASE64URL(JWE Authentication Tag)
    """
    trace_enter(inspect.currentframe())

    try:
        jwe = b64_protected_header + b'.' + b64_cek_ciphertext + b'.' + \
            b64_iv + b'.' + b64_encrypted_dek + b'.' + b64_tag

        jwe_token = {'kid': jwe_kid, 'jwe': jwe.decode()}

        json_jwe_token = json.dumps(jwe_token)
    except Exception as exc:
        ret = ''
        logger.error('Failed to create JWE token: %s', exc)
        trace_exit(inspect.currentframe(), ret)
        return ret

    logger.debug('Created JWE token: %s', json_jwe_token)

    trace_exit(inspect.currentframe(), json_jwe_token)
    return json_jwe_token
Exemplo n.º 3
0
def _encrypt_dek_with_cek(priv_cek: bytearray, initialization_vector: bytes,
                          priv_dek: bytearray,
                          ascii_b64_protected_header: bytes) \
        -> Tuple[bytes, bytes]:
    """
    Wrap dek with cek:
    - Perform authenticated encryption on dek with the AES GCM algorithm.
    - Use cek as encryption key, the initialization vector,
      and the protected header as Additional Authenticated Data value.
    - Request a 128-bit Authentication Tag output.
    """
    trace_enter(inspect.currentframe())

    try:
        # mac_len=16 bytes: 128 bit authentication tag
        dek_cipher = AES.new(priv_cek,
                             AES.MODE_GCM,
                             nonce=initialization_vector,
                             mac_len=16)

        # add additional authenticated data (aad)
        dek_cipher.update(ascii_b64_protected_header)

        # TODO: Autom. padding helpful? Might replace pycryptodome anyway.
        # from Cryptodome.Util.Padding import pad
        # encrypted_dek, tag = \
        #   dek_cipher.encrypt_and_digest(pad(dek, AES.block_size))
        encrypted_dek, tag = dek_cipher.encrypt_and_digest(priv_dek)

        # Remove sensitive data from memory
        del priv_dek[:]
        del priv_cek[:]

        b64_encrypted_dek = base64.urlsafe_b64encode(encrypted_dek)
        b64_tag = base64.urlsafe_b64encode(tag)
    except Exception as exc:
        ret = (b'', b'')
        logger.error('Failed to encrypt dek: %s', exc)
        trace_exit(inspect.currentframe(), ret)
        return ret

    if config.get_config_by_keypath('DEV_MODE'):
        logger.debug('Additional authenticated data (aad): '
                     '%s', ascii_b64_protected_header.decode())
        logger.debug('Encrypted dek: "%s" (hex), '
                     'tag :"%s" (hex).', encrypted_dek.hex(), tag.hex())

    trace_exit(inspect.currentframe(), (b64_encrypted_dek, b64_tag))
    return b64_encrypted_dek, b64_tag
Exemplo n.º 4
0
def __authenticate_vault_client(client: hvac.Client, tenant: str,
                                priv_jwt_token: str) -> hvac.Client:

    trace_enter(inspect.currentframe())

    vault_auth_jwt_path = config.get_vault_auth_jwt_path(tenant)

    if not vault_auth_jwt_path:
        logger.error('Failed to load auth jwt path for tenant "%s"', tenant)
        return None

    if config.get_config_by_keypath('DEV_MODE'):
        logger.debug('Attempting to authenticate against Vault using JWT: %s',
                     priv_jwt_token)

    cache_id = utils.get_vault_token_cache_id(tenant, priv_jwt_token)

    if cache_id in __VAULT_TOKEN_CACHE:
        logger.debug('Cache hit: Found token for "%s".', cache_id)
        client.token = __VAULT_TOKEN_CACHE[cache_id]
    else:
        logger.debug('Cache miss: Token for "%s" not found.', cache_id)

        token = __get_vault_token(client, tenant, priv_jwt_token,
                                  vault_auth_jwt_path)

        if not token:
            ret = None
            logger.error('Failed to get Vault token.')
            trace_exit(inspect.currentframe(), ret)
            return ret

        client.token = token
        __VAULT_TOKEN_CACHE[cache_id] = token

    if not client.is_authenticated():
        # token might be invalid/has expired
        del __VAULT_TOKEN_CACHE[cache_id]

        ret = None

        logger.error('Failed to validate Vault client. '
                     'Review configuration (config/config.json). '
                     'Retry as token might have expired.')

        trace_exit(inspect.currentframe(), ret)
        return ret

    logger.debug('Successfully authenticated Vault client '
                 'for tenant "%s"', tenant)

    trace_exit(inspect.currentframe(), client)
    return client
Exemplo n.º 5
0
def get_kid_from_jwt(priv_token: str) -> str:
    """
    Extracts KID from JWT token. Only use this method after JWT token
    has been validated.
    """
    trace_enter(inspect.currentframe())

    try:
        protected_header_unverified = jwt.get_unverified_header(priv_token)
    except jwt.DecodeError as exc:
        ret = ''
        logger.error('Cannot decode JWT to get kid: %s', exc)
        logger.debug('JWT: %s', priv_token)
        trace_exit(inspect.currentframe(), ret)
        return ret

    ret = protected_header_unverified.get('kid', '')
    trace_exit(inspect.currentframe(), ret)
    return ret
Exemplo n.º 6
0
def __get_vault_token(client: hvac.Client, tenant: str, priv_jwt_token: str,
                      vault_auth_jwt_path: str) -> str:

    trace_enter(inspect.currentframe())

    default_role = config.get_vault_default_role(tenant)

    if not default_role:
        logger.error('Failed to load Vault default role for tenant "%s"',
                     tenant)
        return ''

    try:
        response = client.auth.jwt.jwt_login(role=default_role,
                                             jwt=priv_jwt_token,
                                             path=vault_auth_jwt_path)
    except Exception as exc:
        ret = ''
        logger.error('Failed to authenticate against Vault: %s', exc)
        trace_exit(inspect.currentframe(), ret)
        return ret

    if config.get_config_by_keypath('DEV_MODE'):
        logger.debug('Vault login response: %s', response)

    try:
        vault_token = response['auth']['client_token']
    except KeyError as exc:
        ret = ''

        logger.error(
            'Failed to access the Vault token from auth response: '
            'KeyError on key %s. '
            'This is most likely a permission issue.', exc)

        trace_exit(inspect.currentframe(), ret)
        return ret

    if config.get_config_by_keypath('DEV_MODE'):
        logger.debug('Vault client token returned: %s', vault_token)

    logger.debug('Retrieved new Vault token.')

    trace_exit(inspect.currentframe(), CAMOUFLAGE_SIGN)
    return vault_token
Exemplo n.º 7
0
        trace_exit(inspect.currentframe(), '')
        return ''

    # Generate an initialization vector (IV)
    # (BASE64URL(IV)) (12 bytes * 8 = 96 bit)
    try:
        initialization_vector = get_random_bytes(12)
        b64_iv = base64.urlsafe_b64encode(initialization_vector)
    except Exception as exc:
        ret = ''
        logger.error('Failed to create initialization vector: %s', exc)
        trace_exit(inspect.currentframe(), ret)
        return ret

    if config.get_config_by_keypath('DEV_MODE'):
        logger.debug('Generated IV/Nonce "%s" '
                     '(base64 encoded, bytes).', b64_iv.decode())

    b64_protected_header = _get_jwe_protected_header(jwe_kid, nonce)

    if not b64_protected_header:
        ret = ''
        logger.error('Failed to get JWE protected header.')
        trace_exit(inspect.currentframe(), ret)
        return ret

    # Encode JWE protected header
    # (ASCII(BASE64URL(UTF8(JWE Protected Header))))
    try:
        ascii_b64_protected_header = \
            b64_protected_header.decode().encode('ascii', errors='strict')
    except Exception as exc: