/home/runner/work/eturnal/eturnal/_build/test/cover/aggregate/eturnal_cert.html

1 %%% eturnal STUN/TURN server.
2 %%%
3 %%% Copyright (c) 2021 Holger Weiss <holger@zedat.fu-berlin.de>.
4 %%% Copyright (c) 2021 ProcessOne, SARL.
5 %%% All rights reserved.
6 %%%
7 %%% Licensed under the Apache License, Version 2.0 (the "License");
8 %%% you may not use this file except in compliance with the License.
9 %%% You may obtain a copy of the License at
10 %%%
11 %%% http://www.apache.org/licenses/LICENSE-2.0
12 %%%
13 %%% Unless required by applicable law or agreed to in writing, software
14 %%% distributed under the License is distributed on an "AS IS" BASIS,
15 %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 %%% See the License for the specific language governing permissions and
17 %%% limitations under the License.
18
19 -module(eturnal_cert).
20 -export([create/1]).
21
22 -include_lib("public_key/include/public_key.hrl").
23 -define(ETURNAL_KEY_SIZE, 4096).
24
25 % Currently not exported by calendar:
26 -type year() :: non_neg_integer().
27 -type month() :: 1..12.
28 -type day() :: 1..31.
29
30 %% API.
31
32 -spec create(string() | binary()) -> binary().
33 create(Domain) when is_binary(Domain) ->
34 2 create(binary_to_list(Domain));
35 create(Domain) ->
36 2 Key = private_key(),
37 2 Crt = certificate(Domain, Key),
38 2 public_key:pem_encode([pem_entry(Key), pem_entry(Crt)]).
39
40 %% Internal functions.
41
42 -spec private_key() -> #'RSAPrivateKey'{}.
43 private_key() ->
44 2 public_key:generate_key({rsa, ?ETURNAL_KEY_SIZE, 65537}).
45
46 -spec certificate(string(), #'RSAPrivateKey'{}) -> public_key:der_encoded().
47 certificate(Domain, Key) ->
48 2 TBS = #'OTPTBSCertificate'{
49 serialNumber = serial_number(),
50 signature = signature(),
51 issuer = issuer(Domain),
52 validity = validity(),
53 subject = subject(Domain),
54 subjectPublicKeyInfo = subject_key_info(Key),
55 extensions = extensions(Domain)},
56 2 public_key:pkix_sign(TBS, Key).
57
58 -spec serial_number() -> pos_integer().
59 serial_number() ->
60 2 rand:uniform(1000000000).
61
62 -spec signature() -> #'SignatureAlgorithm'{}.
63 signature() ->
64 2 #'SignatureAlgorithm'{
65 algorithm = ?'sha256WithRSAEncryption',
66 parameters = 'NULL'}.
67
68 -spec issuer(string()) -> {rdnSequence, [[#'AttributeTypeAndValue'{}]]}.
69 issuer(Domain) -> % Self-signed.
70 2 subject(Domain).
71
72 -spec validity() -> #'Validity'{}.
73 validity() ->
74 2 #'Validity'{
75 notBefore = format_date(calendar:universal_time()),
76 notAfter = format_date(2038, 1, 1)}.
77
78 -spec subject(string()) -> {rdnSequence, [[#'AttributeTypeAndValue'{}]]}.
79 subject(Domain) ->
80 4 {rdnSequence,
81 [[#'AttributeTypeAndValue'{
82 type = ?'id-at-commonName',
83 value = {printableString, Domain}}]]}.
84
85 -spec subject_key_info(#'RSAPrivateKey'{}) -> #'OTPSubjectPublicKeyInfo'{}.
86 subject_key_info(#'RSAPrivateKey'{modulus = Modulus, publicExponent = Exp}) ->
87 2 #'OTPSubjectPublicKeyInfo'{
88 algorithm =
89 #'PublicKeyAlgorithm'{
90 algorithm = ?'rsaEncryption',
91 parameters = 'NULL'},
92 subjectPublicKey =
93 #'RSAPublicKey'{
94 modulus = Modulus,
95 publicExponent = Exp}}.
96
97 -spec extensions(string()) -> [#'Extension'{}].
98 extensions(Domain) ->
99 2 [#'Extension'{
100 extnID = ?'id-ce-subjectAltName',
101 extnValue = [{dNSName, Domain}],
102 critical = false},
103 #'Extension'{
104 extnID = ?'id-ce-basicConstraints',
105 extnValue = #'BasicConstraints'{cA = true},
106 critical = false}].
107
108 -spec pem_entry(#'RSAPrivateKey'{} | public_key:der_encoded())
109 -> public_key:pem_entry().
110 pem_entry(#'RSAPrivateKey'{} = Key) ->
111 2 public_key:pem_entry_encode('RSAPrivateKey', Key);
112 pem_entry(Crt) when is_binary(Crt) ->
113 2 {'Certificate', Crt, not_encrypted}.
114
115 -spec format_date(year(), month(), day()) -> {utcTime, string()}.
116 format_date(Y, M, D) when Y >= 2000 ->
117 4 {utcTime,
118 lists:flatten(
119 io_lib:format("~2.10.0B~2.10.0B~2.10.0B~2.10.0B~2.10.0B~2.10.0BZ",
120 [Y - 2000, M, D, 0, 0, 0]))}.
121
122 -spec format_date(calendar:datetime()) -> {utcTime, string()}.
123 format_date({{Y, M, D}, {_, _, _}}) ->
124 2 format_date(Y, M, D).
Line Hits Source