Current section
Files
Jump to
Current section
Files
src/credentials_obfuscation_svc.erl
%% This Source Code Form is subject to the terms of the Mozilla Public
%% License, v. 2.0. If a copy of the MPL was not distributed with this
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
%%
%% Copyright (c) 2019-2020 VMware, Inc. or its affiliates. All rights reserved.
%%
-module(credentials_obfuscation_svc).
-behaviour(gen_server).
-include("credentials_obfuscation.hrl").
%% API functions
-export([start_link/0,
get_config/1,
refresh_config/0,
set_secret/1,
encrypt/1,
decrypt/1]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-record(state, {enabled :: boolean(),
cipher :: atom(),
hash :: atom(),
iterations :: non_neg_integer(),
secret :: binary() | '$pending-secret'}).
%%%===================================================================
%%% API functions
%%%===================================================================
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-spec get_config(atom()) -> term().
get_config(Config) ->
gen_server:call(?MODULE, {get_config, Config}).
-spec refresh_config() -> ok.
refresh_config() ->
gen_server:call(?MODULE, refresh_config).
-spec set_secret(binary()) -> ok.
set_secret(Secret) when is_binary(Secret) ->
gen_server:call(?MODULE, {set_secret, Secret}).
-spec encrypt(term()) -> {plaintext, term()} | {encrypted, binary()}.
encrypt(Term) ->
gen_server:call(?MODULE, {encrypt, Term}).
-spec decrypt({plaintext, term()} | {encrypted, binary()}) -> term().
decrypt(Term) ->
gen_server:call(?MODULE, {decrypt, Term}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
init_state().
handle_call({get_config, enabled}, _From, #state{enabled=Enabled}=State) ->
{reply, Enabled, State};
handle_call({get_config, cipher}, _From, #state{cipher=Cipher}=State) ->
{reply, Cipher, State};
handle_call({get_config, hash}, _From, #state{hash=Hash}=State) ->
{reply, Hash, State};
handle_call({get_config, iterations}, _From, #state{iterations=Iterations}=State) ->
{reply, Iterations, State};
handle_call({get_config, secret}, _From, #state{secret=Secret}=State) ->
{reply, Secret, State};
handle_call(refresh_config, _From, State0) ->
{ok, State1} = refresh_config(State0),
{reply, ok, State1};
handle_call({encrypt, Term}, _From, #state{enabled=false}=State) ->
{reply, Term, State};
handle_call({encrypt, Term}, _From, #state{cipher=Cipher,
hash=Hash,
iterations=Iterations,
secret=Secret}=State) ->
Encrypted = credentials_obfuscation_pbe:encrypt(Cipher, Hash, Iterations, Secret, Term),
{reply, Encrypted, State};
handle_call({decrypt, Term}, _From, #state{enabled=false}=State) ->
{reply, Term, State};
handle_call({decrypt, Term}, _From, #state{cipher=Cipher,
hash=Hash,
iterations=Iterations,
secret=Secret}=State) ->
Decrypted = credentials_obfuscation_pbe:decrypt(Cipher, Hash, Iterations, Secret, Term),
{reply, Decrypted, State};
handle_call({set_secret, Secret}, _From, State0) ->
State1 = State0#state{secret = Secret},
{reply, ok, State1}.
handle_cast(_Message, State) ->
{noreply, State}.
handle_info(_Message, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-spec init_state() -> {'ok', #state{enabled::boolean(), cipher::atom(), hash::atom(), iterations::pos_integer(), secret::'$pending-secret'}}.
init_state() ->
{ok, Enabled, Cipher, Hash, Iterations} = get_config_values(),
ok = check(Cipher, Hash, Iterations),
State = #state{enabled = Enabled, cipher = Cipher, hash = Hash,
iterations = Iterations, secret = ?PENDING_SECRET},
{ok, State}.
-spec refresh_config(#state{enabled::boolean(), cipher::atom(), hash::atom(), iterations::non_neg_integer(), secret::'$pending-secret' | binary()}) ->
{'ok', #state{enabled::boolean(), cipher::atom(), hash::atom(), iterations::non_neg_integer(), secret::'$pending-secret' | binary()}}.
refresh_config(#state{secret=Secret}=State0) ->
{ok, Enabled, Cipher, Hash, Iterations} = get_config_values(),
ok = case Enabled of
true -> check(Cipher, Hash, Iterations);
false -> ok
end,
State1 = State0#state{enabled = Enabled, cipher = Cipher, hash = Hash,
iterations = Iterations, secret = Secret},
{ok, State1}.
get_config_values() ->
Enabled = application:get_env(credentials_obfuscation, enabled, true),
Cipher = application:get_env(credentials_obfuscation, cipher,
credentials_obfuscation_pbe:default_cipher()),
Hash = application:get_env(credentials_obfuscation, hash,
credentials_obfuscation_pbe:default_hash()),
Iterations = application:get_env(credentials_obfuscation, iterations,
credentials_obfuscation_pbe:default_iterations()),
{ok, Enabled, Cipher, Hash, Iterations}.
check(Cipher, Hash, Iterations) ->
Value = <<"dummy">>,
TempSecret = crypto:strong_rand_bytes(128),
E = credentials_obfuscation_pbe:encrypt(Cipher, Hash, Iterations, TempSecret, Value),
Value = credentials_obfuscation_pbe:decrypt(Cipher, Hash, Iterations, TempSecret, E),
ok.