Skip to content

Commit db465d1

Browse files
committed
Create keychain utility module
1 parent 9a7375c commit db465d1

2 files changed

Lines changed: 254 additions & 216 deletions

File tree

src/grisp_keychain_filesystem.erl

Lines changed: 9 additions & 216 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ Filesystem `api_module` for grisp_keychain
66
Implements the keychain API for direct filesystem based access to certificates and keys.
77
""".
88

9-
%--- Includes ------------------------------------------------------------------
10-
11-
-include_lib("kernel/include/file.hrl").
12-
13-
149
%--- Exports -------------------------------------------------------------------
1510

1611
% API functions
@@ -29,158 +24,33 @@ Implements the keychain API for direct filesystem based access to certificates a
2924

3025
tls_options(DomainName) ->
3126
{CertsKeys, ClientTrustedCerts} =
32-
case tls_use_client_certificate() of
27+
case grisp_keychain_utils:use_client_certificate() of
3328
false -> {[], []};
3429
true ->
3530
ClientCerts = client_certs(),
3631
ClientKey = client_key(),
37-
TrustedCerts = client_trusted_certs(ClientCerts),
32+
TrustedCerts = grisp_keychain_utils:client_trusted_certs(),
3833
{certs_keys_config(ClientCerts, ClientKey), TrustedCerts}
3934
end,
40-
DomainNameStr = domain_name(DomainName),
41-
ServerNameIndication = server_name_indication(DomainNameStr),
42-
case tls_verify() of
43-
verify_none ->
44-
[{verify, verify_none},
45-
{server_name_indication, ServerNameIndication},
46-
{cacerts, ClientTrustedCerts}
47-
| CertsKeys];
48-
verify_peer ->
49-
ServerTrustedCerts = server_trusted_certs(DomainNameStr),
50-
[{verify, verify_peer},
51-
{depth, 99},
52-
{cacerts, ClientTrustedCerts ++ ServerTrustedCerts},
53-
{server_name_indication, ServerNameIndication},
54-
{customize_hostname_check, [
55-
{match_fun, public_key:pkix_verify_hostname_match_fun(https)}
56-
]}
57-
| CertsKeys]
58-
end.
35+
grisp_keychain_utils:build_tls_options(DomainName, CertsKeys, ClientTrustedCerts).
5936

6037
read_cert(primary, der) ->
6138
[PemBin|_] = client_certs(),
6239
PemBin.
6340

6441
%--- Internal Functions --------------------------------------------------------
6542

66-
locate_test_dir(BasePath) ->
67-
BuildTestDir = filename:join(BasePath, "test"),
68-
case filelib:is_dir(BuildTestDir) of
69-
true -> BuildTestDir;
70-
false ->
71-
% no "test" directory, try to follow "src" link...
72-
SrcDir = filename:join(BasePath, "src"),
73-
case file:read_link(SrcDir) of
74-
{error, einval} ->
75-
throw({test_directory_not_found, BasePath});
76-
{ok, SrcTargetDir} ->
77-
AbsSrcTargetDir = filename:absname(SrcTargetDir, BasePath),
78-
AbsSrcParentDir = filename:dirname(AbsSrcTargetDir),
79-
SrcTestDir = filename:join(AbsSrcParentDir, "test"),
80-
case filelib:is_dir(SrcTestDir) of
81-
true -> SrcTestDir;
82-
false ->
83-
throw({test_directory_not_found, AbsSrcParentDir})
84-
end
85-
end
86-
end.
87-
88-
resolve_path(undefined) -> undefined;
89-
resolve_path(AbsPath) when is_list(AbsPath) -> AbsPath;
90-
resolve_path(AbsPath) when is_binary(AbsPath) ->
91-
unicode:characters_to_list(AbsPath);
92-
resolve_path({Tag, AppName, RelPath})
93-
when is_atom(Tag), is_atom(AppName), is_binary(RelPath) ->
94-
resolve_path({Tag, AppName, unicode:characters_to_list(RelPath)});
95-
resolve_path({priv, AppName, RelPath})
96-
when is_atom(AppName), is_list(RelPath) ->
97-
case code:priv_dir(AppName) of
98-
{error, bad_name} -> throw({bad_appname, AppName});
99-
BasePath -> filename:join(BasePath, RelPath)
100-
end;
101-
resolve_path({test, AppName, RelPath})
102-
when is_atom(AppName), is_list(RelPath) ->
103-
% Only meaningfull for tests, as the test directory is copied into _build
104-
case code:lib_dir(AppName) of
105-
{error, bad_name} -> throw({bad_appname, AppName});
106-
BasePath ->
107-
TestDir = locate_test_dir(BasePath),
108-
filename:join(TestDir, RelPath)
109-
end.
110-
111-
get_config(Key) ->
112-
application:get_env(grisp_keychain, Key).
113-
114-
get_config_path(Key) ->
115-
case get_config(Key) of
116-
undefined -> undefined;
117-
{ok, PathSpec} -> {ok, resolve_path(PathSpec)}
118-
end.
119-
120-
get_config_path(Key, DefaultPathSpec) ->
121-
case get_config(Key) of
122-
undefined -> resolve_path(DefaultPathSpec);
123-
{ok, PathSpec} -> resolve_path(PathSpec)
124-
end.
125-
126-
get_config_cb(Key, Default) ->
127-
case application:get_env(grisp_keychain, Key) of
128-
undefined -> Default;
129-
{ok, undefined} -> Default;
130-
{ok, {ModName, FunName}}
131-
when is_atom(ModName), is_atom(FunName) ->
132-
ModName:FunName();
133-
{ok, {ModName, FunName, FunArgs}}
134-
when is_atom(ModName), is_atom(FunName) ->
135-
erlang:apply(ModName, FunName, FunArgs)
136-
end.
137-
138-
tls_use_client_certificate() ->
139-
case get_config(tls_use_client_certificate) of
140-
undefined -> true;
141-
{ok, Flag} when is_boolean(Flag) -> Flag
142-
end.
143-
144-
tls_server_trusted_certs(DomainName) ->
145-
case get_config_path(tls_server_trusted_certs) of
146-
undefined -> [];
147-
{ok, BasePath} ->
148-
FilePath = filename:join(BasePath, DomainName),
149-
try load_cert_file(FilePath, [".pem", ".crt"])
150-
catch throw:_Reason -> []
151-
end
152-
end.
153-
154-
tls_server_trusted_certs_cb() ->
155-
get_config_cb(tls_server_trusted_certs_cb, []).
156-
157-
tls_client_trusted_certs() ->
158-
DefaultClientPath = {priv, grisp_keychain, ""},
159-
BasePath = get_config_path(tls_client_trusted_certs, DefaultClientPath),
160-
try load_certs(BasePath)
161-
catch throw:_Reason -> []
162-
end.
163-
164-
tls_client_trusted_certs_cb() ->
165-
get_config_cb(tls_client_trusted_certs_cb, []).
166-
167-
tls_verify() ->
168-
case get_config(tls_verify) of
169-
undefined -> verify_peer;
170-
{ok, Value} when Value =:= verify_none; Value =:= verify_peer -> Value
171-
end.
172-
17343
client_certs() ->
174-
case get_config_path(client_certs) of
44+
case grisp_keychain_utils:get_config_path(client_certs) of
17545
undefined -> throw(no_client_certificates);
176-
{ok, Path} -> load_certs(Path)
46+
{ok, Path} -> grisp_keychain_utils:load_certs(Path)
17747
end.
17848

17949

18050
client_key() ->
181-
case get_config_path(client_key) of
51+
case grisp_keychain_utils:get_config_path(client_key) of
18252
undefined -> throw(no_client_key);
183-
{ok, Path} -> load_key(Path)
53+
{ok, Path} -> grisp_keychain_utils:load_key(Path)
18454
end.
18555

18656
certs_keys_config(ClientCerts, ClientKey) ->
@@ -189,85 +59,8 @@ certs_keys_config(ClientCerts, ClientKey) ->
18959
key => ClientKey
19060
}]}].
19161

192-
domain_name(undefined) -> undefined;
193-
domain_name(Name) when is_atom(Name) -> atom_to_list(Name);
194-
domain_name(Name) when is_list(Name) -> Name;
195-
domain_name(Name) when is_binary(Name) -> unicode:characters_to_list(Name).
196-
197-
server_name_indication(undefined) -> disabled;
198-
server_name_indication(DomainName) -> DomainName.
199-
200-
server_trusted_certs(undefined) ->
201-
tls_server_trusted_certs_cb();
202-
server_trusted_certs(DomainName) ->
203-
tls_server_trusted_certs(DomainName) ++ tls_server_trusted_certs_cb().
204-
205-
client_trusted_certs(_ClientCerts) ->
206-
tls_client_trusted_certs() ++ tls_client_trusted_certs_cb().
207-
20862
load_key(FilePath) ->
209-
case file:read_file(FilePath) of
210-
{error, enoent} ->
211-
throw({file_not_found, FilePath});
212-
{ok, PemData} ->
213-
case public_key:pem_decode(PemData) of
214-
[] ->
215-
throw({invalid_key, FilePath});
216-
[{Asn1Type, DerData, not_encrypted}] ->
217-
{Asn1Type, DerData};
218-
[{_Asn1Type, _DerData, _}] ->
219-
throw(encrypted_key_not_supported);
220-
[_|_] ->
221-
throw(multiple_key_not_supported)
222-
end
223-
end.
63+
grisp_keychain_utils:load_key(FilePath).
22464

22565
load_certs(FilePath) ->
226-
case filelib:is_dir(FilePath) of
227-
true -> load_cert_dir(FilePath, [".pem", ".crt"]);
228-
false -> load_cert_file(FilePath, ["", ".pem", ".crt"])
229-
end.
230-
231-
load_cert_dir(DirPath, Extentions) ->
232-
case file:list_dir(DirPath) of
233-
{error, Reason} -> throw(Reason);
234-
{ok, Files} ->
235-
lists:foldl(fun(Filename, Acc) ->
236-
FullPath = filename:join(DirPath, Filename),
237-
Ext = filename:extension(Filename),
238-
IsFile = filelib:is_file(FullPath),
239-
HasExt = lists:member(Ext, Extentions),
240-
case IsFile and HasExt of
241-
false -> Acc;
242-
true -> load_cert_file(FullPath) ++ Acc
243-
end
244-
end, [], Files)
245-
end.
246-
247-
load_cert_file(FilePath, []) ->
248-
{ok, Cwd} = file:get_cwd(),
249-
throw({certificate_not_found, FilePath, Cwd});
250-
load_cert_file(FilePath, [Ext | Rest]) ->
251-
FullPath = FilePath ++ Ext,
252-
case filelib:is_file(FullPath) of
253-
true -> load_cert_file(FullPath);
254-
false -> load_cert_file(FilePath, Rest)
255-
end.
256-
257-
load_cert_file(FilePath) ->
258-
case file:read_file(FilePath) of
259-
{error, enoent} ->
260-
throw({certificate_not_found, FilePath});
261-
{ok, PemData} ->
262-
decode_certs(PemData)
263-
end.
264-
265-
decode_certs(PemData) ->
266-
decode_certs(public_key:pem_decode(PemData), []).
267-
268-
decode_certs([], Acc) ->
269-
lists:reverse(Acc);
270-
decode_certs([{'Certificate', D, not_encrypted} | Rest], Acc) ->
271-
decode_certs(Rest, [D | Acc]);
272-
decode_certs([Bad | _Rest], _Acc) ->
273-
throw({bad_certificate, Bad}).
66+
grisp_keychain_utils:load_certs(FilePath).

0 commit comments

Comments
 (0)