aboutsummaryrefslogtreecommitdiff
path: root/src/ejabberd_acme.erl
diff options
context:
space:
mode:
authorKonstantinos Kallas <konstantinos.kallas@hotmail.com>2017-06-22 14:47:56 +0300
committerKonstantinos Kallas <konstantinos.kallas@hotmail.com>2017-06-22 14:47:56 +0300
commit330456bcf0a4bc28e38178ab86c779c9c7efd030 (patch)
treee3a5c65d685ccf386be4420591f0941568d16630 /src/ejabberd_acme.erl
parentRemoved some ?INFO_MSG (diff)
Indent using Emacs
Diffstat (limited to 'src/ejabberd_acme.erl')
-rw-r--r--src/ejabberd_acme.erl745
1 files changed, 368 insertions, 377 deletions
diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl
index 056ea83b0..927cccf33 100644
--- a/src/ejabberd_acme.erl
+++ b/src/ejabberd_acme.erl
@@ -1,22 +1,18 @@
-module (ejabberd_acme).
--export([ directory/1
-
- , get_account/3
- , new_account/4
- , update_account/4
- , delete_account/3
- % , key_roll_over/5
-
- , new_authz/4
- , get_authz/1
- , complete_challenge/4
-
- , new_cert/4
-
- , scenario/3
- , scenario0/2
- ]).
+-export([directory/1,
+ get_account/3,
+ new_account/4,
+ update_account/4,
+ delete_account/3,
+ new_authz/4,
+ get_authz/1,
+ complete_challenge/4,
+ new_cert/4,
+ scenario/3,
+ scenario0/2
+ %% , key_roll_over/5
+ ]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -36,10 +32,9 @@
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec directory(url()) ->
- {ok, map(), nonce()} | {error, _}.
+-spec directory(url()) -> {ok, map(), nonce()} | {error, _}.
directory(Url) ->
- prepare_get_request(Url, fun get_dirs/1).
+ prepare_get_request(Url, fun get_dirs/1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -48,34 +43,33 @@ directory(Url) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec new_account(url(), jose_jwk:key(), proplist(), nonce()) ->
- {ok, {url(), proplist()}, nonce()} | {error, _}.
+ {ok, {url(), proplist()}, nonce()} | {error, _}.
new_account(Url, PrivateKey, Req, Nonce) ->
- %% Make the request body
- EJson = {[{ <<"resource">>, <<"new-reg">>}] ++ Req},
- prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1).
+ %% Make the request body
+ EJson = {[{ <<"resource">>, <<"new-reg">>}] ++ Req},
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1).
-spec update_account(url(), jose_jwk:key(), proplist(), nonce()) ->
- {ok, proplist(), nonce()} | {error, _}.
+ {ok, proplist(), nonce()} | {error, _}.
update_account(Url, PrivateKey, Req, Nonce) ->
- %% Make the request body
- EJson = {[{ <<"resource">>, <<"reg">>}] ++ Req},
- prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
+ %% Make the request body
+ EJson = {[{ <<"resource">>, <<"reg">>}] ++ Req},
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
-spec get_account(url(), jose_jwk:key(), nonce()) ->
- {ok, {url(), proplist()}, nonce()} | {error, _}.
+ {ok, {url(), proplist()}, nonce()} | {error, _}.
get_account(Url, PrivateKey, Nonce) ->
- %% Make the request body
- EJson = {[{<<"resource">>, <<"reg">>}]},
- prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1).
+ %% Make the request body
+ EJson = {[{<<"resource">>, <<"reg">>}]},
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_tos/1).
-spec delete_account(url(), jose_jwk:key(), nonce()) ->
- {ok, proplist(), nonce()} | {error, _}.
+ {ok, proplist(), nonce()} | {error, _}.
delete_account(Url, PrivateKey, Nonce) ->
- EJson = {
- [ {<<"resource">>, <<"reg">>}
- , {<<"status">>, <<"deactivated">>}
- ]},
- prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
+ EJson =
+ {[{<<"resource">>, <<"reg">>},
+ {<<"status">>, <<"deactivated">>}]},
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -85,21 +79,20 @@ delete_account(Url, PrivateKey, Nonce) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec new_authz(url(), jose_jwk:key(), proplist(), nonce()) ->
- {ok, proplist(), nonce()} | {error, _}.
+ {ok, proplist(), nonce()} | {error, _}.
new_authz(Url, PrivateKey, Req, Nonce) ->
- EJson = {[{<<"resource">>, <<"new-authz">>}] ++ Req},
- prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_location/1).
+ EJson = {[{<<"resource">>, <<"new-authz">>}] ++ Req},
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response_location/1).
--spec get_authz(url()) ->
- {ok, proplist(), nonce()} | {error, _}.
+-spec get_authz(url()) -> {ok, proplist(), nonce()} | {error, _}.
get_authz(Url) ->
- prepare_get_request(Url, fun get_response/1).
+ prepare_get_request(Url, fun get_response/1).
-spec complete_challenge(url(), jose_jwk:key(), proplist(), nonce()) ->
- {ok, proplist(), nonce()} | {error, _}.
+ {ok, proplist(), nonce()} | {error, _}.
complete_challenge(Url, PrivateKey, Req, Nonce) ->
- EJson = {[{<<"resource">>, <<"challenge">>}] ++ Req},
- prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
+ EJson = {[{<<"resource">>, <<"challenge">>}] ++ Req},
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -109,10 +102,10 @@ complete_challenge(Url, PrivateKey, Req, Nonce) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec new_cert(url(), jose_jwk:key(), proplist(), nonce()) ->
- {ok, proplist(), nonce()} | {error, _}.
+ {ok, proplist(), nonce()} | {error, _}.
new_cert(Url, PrivateKey, Req, Nonce) ->
- EJson = {[{<<"resource">>, <<"new-cert">>}] ++ Req},
- prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1, "application/pkix-cert").
+ EJson = {[{<<"resource">>, <<"new-cert">>}] ++ Req},
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, fun get_response/1, "application/pkix-cert").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -123,28 +116,28 @@ new_cert(Url, PrivateKey, Req, Nonce) ->
-spec get_dirs({ok, proplist(), proplist()}) -> {ok, map(), nonce()}.
get_dirs({ok, Head, Return}) ->
- NewNonce = get_nonce(Head),
- StrDirectories = [{bitstring_to_list(X), bitstring_to_list(Y)} ||
- {X, Y} <- Return],
- NewDirs = maps:from_list(StrDirectories),
- {ok, NewDirs, NewNonce}.
+ NewNonce = get_nonce(Head),
+ StrDirectories = [{bitstring_to_list(X), bitstring_to_list(Y)} ||
+ {X, Y} <- Return],
+ NewDirs = maps:from_list(StrDirectories),
+ {ok, NewDirs, NewNonce}.
-spec get_response({ok, proplist(), proplist()}) -> {ok, proplist(), nonce()}.
get_response({ok, Head, Return}) ->
- NewNonce = get_nonce(Head),
- {ok, Return, NewNonce}.
+ NewNonce = get_nonce(Head),
+ {ok, Return, NewNonce}.
-spec get_response_tos({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}.
get_response_tos({ok, Head, Return}) ->
- TOSUrl = get_tos(Head),
- NewNonce = get_nonce(Head),
- {ok, {TOSUrl, Return}, NewNonce}.
+ TOSUrl = get_tos(Head),
+ NewNonce = get_nonce(Head),
+ {ok, {TOSUrl, Return}, NewNonce}.
-spec get_response_location({ok, proplist(), proplist()}) -> {ok, {url(), proplist()}, nonce()}.
get_response_location({ok, Head, Return}) ->
- Location = get_location(Head),
- NewNonce = get_nonce(Head),
- {ok, {Location, Return}, NewNonce}.
+ Location = get_location(Head),
+ NewNonce = get_nonce(Head),
+ {ok, {Location, Return}, NewNonce}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -162,113 +155,113 @@ get_response_location({ok, Head, Return}) ->
-spec make_csr(proplist()) -> binary().
make_csr(Attributes) ->
- Key = generate_key(),
+ Key = generate_key(),
- {_, KeyKey} = jose_jwk:to_key(Key),
+ {_, KeyKey} = jose_jwk:to_key(Key),
- KeyPub = jose_jwk:to_public(Key),
+ KeyPub = jose_jwk:to_public(Key),
- try
- SubPKInfoAlgo = subject_pk_info_algo(KeyPub),
+ try
+ SubPKInfoAlgo = subject_pk_info_algo(KeyPub),
- {ok, RawBinPubKey} = raw_binary_public_key(KeyPub),
- SubPKInfo = subject_pk_info(SubPKInfoAlgo, RawBinPubKey),
+ {ok, RawBinPubKey} = raw_binary_public_key(KeyPub),
+ SubPKInfo = subject_pk_info(SubPKInfoAlgo, RawBinPubKey),
- {ok, Subject} = attributes_from_list(Attributes),
+ {ok, Subject} = attributes_from_list(Attributes),
- CRI = certificate_request_info(SubPKInfo, Subject),
- {ok, EncodedCRI} = der_encode(
- 'CertificationRequestInfo',
- CRI),
+ CRI = certificate_request_info(SubPKInfo, Subject),
+ {ok, EncodedCRI} = der_encode(
+ 'CertificationRequestInfo',
+ CRI),
- SignedCRI = public_key:sign(EncodedCRI, 'sha256', KeyKey),
+ SignedCRI = public_key:sign(EncodedCRI, 'sha256', KeyKey),
- SignatureAlgo = signature_algo(Key, 'sha256'),
+ SignatureAlgo = signature_algo(Key, 'sha256'),
- CSR = certification_request(CRI, SignatureAlgo, SignedCRI),
+ CSR = certification_request(CRI, SignatureAlgo, SignedCRI),
- {ok, DerCSR} = der_encode(
- 'CertificationRequest',
- CSR),
+ {ok, DerCSR} = der_encode(
+ 'CertificationRequest',
+ CSR),
- Result = base64url:encode(DerCSR),
+ Result = base64url:encode(DerCSR),
- Result
- catch
- _:{badmatch, {error, bad_public_key}} ->
- {error, bad_public_key};
- _:{badmatch, {error, bad_attributes}} ->
- {error, bad_public_key};
- _:{badmatch, {error, der_encode}} ->
- {error, der_encode}
- end.
+ Result
+ catch
+ _:{badmatch, {error, bad_public_key}} ->
+ {error, bad_public_key};
+ _:{badmatch, {error, bad_attributes}} ->
+ {error, bad_public_key};
+ _:{badmatch, {error, der_encode}} ->
+ {error, der_encode}
+ end.
subject_pk_info_algo(_KeyPub) ->
- #'SubjectPublicKeyInfoAlgorithm'{
- algorithm = ?'id-ecPublicKey',
- parameters = {asn1_OPENTYPE,<<6,8,42,134,72,206,61,3,1,7>>}
- }.
+ #'SubjectPublicKeyInfoAlgorithm'{
+ algorithm = ?'id-ecPublicKey',
+ parameters = {asn1_OPENTYPE,<<6,8,42,134,72,206,61,3,1,7>>}
+ }.
subject_pk_info(Algo, RawBinPubKey) ->
- #'SubjectPublicKeyInfo-PKCS-10'{
- algorithm = Algo,
- subjectPublicKey = RawBinPubKey
- }.
+ #'SubjectPublicKeyInfo-PKCS-10'{
+ algorithm = Algo,
+ subjectPublicKey = RawBinPubKey
+ }.
certificate_request_info(SubPKInfo, Subject) ->
- #'CertificationRequestInfo'{
- version = 0,
- subject = Subject,
- subjectPKInfo = SubPKInfo,
- attributes = []
- }.
+ #'CertificationRequestInfo'{
+ version = 0,
+ subject = Subject,
+ subjectPKInfo = SubPKInfo,
+ attributes = []
+ }.
signature_algo(_Key, _Hash) ->
- #'CertificationRequest_signatureAlgorithm'{
- algorithm = ?'ecdsa-with-SHA256',
- parameters = asn1_NOVALUE
- }.
+ #'CertificationRequest_signatureAlgorithm'{
+ algorithm = ?'ecdsa-with-SHA256',
+ parameters = asn1_NOVALUE
+ }.
certification_request(CRI, SignatureAlgo, SignedCRI) ->
- #'CertificationRequest'{
- certificationRequestInfo = CRI,
- signatureAlgorithm = SignatureAlgo,
- signature = SignedCRI
- }.
+ #'CertificationRequest'{
+ certificationRequestInfo = CRI,
+ signatureAlgorithm = SignatureAlgo,
+ signature = SignedCRI
+ }.
raw_binary_public_key(KeyPub) ->
- try
- {_, RawPubKey} = jose_jwk:to_key(KeyPub),
- {{_, RawBinPubKey}, _} = RawPubKey,
- {ok, RawBinPubKey}
- catch
- _:_ ->
- ?ERROR_MSG("Bad public key: ~p~n", [KeyPub]),
- {error, bad_public_key}
- end.
+ try
+ {_, RawPubKey} = jose_jwk:to_key(KeyPub),
+ {{_, RawBinPubKey}, _} = RawPubKey,
+ {ok, RawBinPubKey}
+ catch
+ _:_ ->
+ ?ERROR_MSG("Bad public key: ~p~n", [KeyPub]),
+ {error, bad_public_key}
+ end.
der_encode(Type, Term) ->
- try
- {ok, public_key:der_encode(Type, Term)}
- catch
- _:_ ->
- ?ERROR_MSG("Cannot DER encode: ~p, with asn1type: ~p", [Term, Type]),
- {error, der_encode}
- end.
+ try
+ {ok, public_key:der_encode(Type, Term)}
+ catch
+ _:_ ->
+ ?ERROR_MSG("Cannot DER encode: ~p, with asn1type: ~p", [Term, Type]),
+ {error, der_encode}
+ end.
%% TODO: I haven't found a function that does that, but there must exist one
length_bitstring(Bitstring) ->
- Size = byte_size(Bitstring),
- case Size =< 127 of
- true ->
- <<12:8, Size:8, Bitstring/binary>>;
- false ->
- LenOctets = binary:encode_unsigned(Size),
- FirstOctet = byte_size(LenOctets),
- <<12:8, 1:1, FirstOctet:7, LenOctets:(FirstOctet * 8), Bitstring/binary>>
- end.
+ Size = byte_size(Bitstring),
+ case Size =< 127 of
+ true ->
+ <<12:8, Size:8, Bitstring/binary>>;
+ false ->
+ LenOctets = binary:encode_unsigned(Size),
+ FirstOctet = byte_size(LenOctets),
+ <<12:8, 1:1, FirstOctet:7, LenOctets:(FirstOctet * 8), Bitstring/binary>>
+ end.
%%
@@ -276,25 +269,25 @@ length_bitstring(Bitstring) ->
%%
attributes_from_list(Attrs) ->
- ParsedAttrs = [attribute_parser_fun(Attr) || Attr <- Attrs],
- case lists:any(fun is_error/1, ParsedAttrs) of
- true ->
- {error, bad_attributes};
- false ->
- {ok, {rdnSequence, [[PAttr] || PAttr <- ParsedAttrs]}}
- end.
+ ParsedAttrs = [attribute_parser_fun(Attr) || Attr <- Attrs],
+ case lists:any(fun is_error/1, ParsedAttrs) of
+ true ->
+ {error, bad_attributes};
+ false ->
+ {ok, {rdnSequence, [[PAttr] || PAttr <- ParsedAttrs]}}
+ end.
attribute_parser_fun({AttrName, AttrVal}) ->
- try
- #'AttributeTypeAndValue'{
- type = attribute_oid(AttrName),
- value = length_bitstring(list_to_bitstring(AttrVal))
- }
- catch
- _:_ ->
- ?ERROR_MSG("Bad attribute: ~p~n", [{AttrName, AttrVal}]),
- {error, bad_attributes}
- end.
+ try
+ #'AttributeTypeAndValue'{
+ type = attribute_oid(AttrName),
+ value = length_bitstring(list_to_bitstring(AttrVal))
+ }
+ catch
+ _:_ ->
+ ?ERROR_MSG("Bad attribute: ~p~n", [{AttrName, AttrVal}]),
+ {error, bad_attributes}
+ end.
attribute_oid(commonName) -> ?'id-at-commonName';
attribute_oid(countryName) -> ?'id-at-countryName';
@@ -310,38 +303,37 @@ attribute_oid(_) -> error(bad_attributes).
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec get_authz_until_valid(url()) ->
- {ok, proplist(), nonce()} | {error, _}.
+-spec get_authz_until_valid(url()) -> {ok, proplist(), nonce()} | {error, _}.
get_authz_until_valid(Url) ->
- get_authz_until_valid(Url, ?MAX_POLL_REQUESTS).
+ get_authz_until_valid(Url, ?MAX_POLL_REQUESTS).
-spec get_authz_until_valid(url(), non_neg_integer()) ->
- {ok, proplist(), nonce()} | {error, _}.
+ {ok, proplist(), nonce()} | {error, _}.
get_authz_until_valid(Url, 0) ->
- ?ERROR_MSG("Maximum request limit waiting for validation reached", []),
- {error, max_request_limit};
+ ?ERROR_MSG("Maximum request limit waiting for validation reached", []),
+ {error, max_request_limit};
get_authz_until_valid(Url, N) ->
- case get_authz(Url) of
- {ok, Resp, Nonce} ->
- case is_authz_valid(Resp) of
- true ->
- {ok, Resp, Nonce};
- false ->
- timer:sleep(?POLL_WAIT_TIME),
- get_authz_until_valid(Url, N-1)
- end;
- {error, _} = Err ->
- Err
- end.
+ case get_authz(Url) of
+ {ok, Resp, Nonce} ->
+ case is_authz_valid(Resp) of
+ true ->
+ {ok, Resp, Nonce};
+ false ->
+ timer:sleep(?POLL_WAIT_TIME),
+ get_authz_until_valid(Url, N-1)
+ end;
+ {error, _} = Err ->
+ Err
+ end.
-spec is_authz_valid(proplist()) -> boolean().
is_authz_valid(Authz) ->
- case proplists:lookup(<<"status">>, Authz) of
- {<<"status">>, <<"valid">>} ->
- true;
- _ ->
- false
- end.
+ case proplists:lookup(<<"status">>, Authz) of
+ {<<"status">>, <<"valid">>} ->
+ true;
+ _ ->
+ false
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -352,66 +344,66 @@ is_authz_valid(Authz) ->
%% TODO: Fix the duplicated code at the below 4 functions
-spec make_post_request(url(), bitstring(), string()) ->
- {ok, proplist(), proplist()} | {error, _}.
+ {ok, proplist(), proplist()} | {error, _}.
make_post_request(Url, ReqBody, ResponseType) ->
- Options = [],
- HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
- case httpc:request(post,
- {Url, [], "application/jose+json", ReqBody}, HttpOptions, Options) of
- {ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
- decode_response(Head, Body, ResponseType);
- Error ->
- failed_http_request(Error, Url)
- end.
+ Options = [],
+ HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
+ case httpc:request(post,
+ {Url, [], "application/jose+json", ReqBody}, HttpOptions, Options) of
+ {ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
+ decode_response(Head, Body, ResponseType);
+ Error ->
+ failed_http_request(Error, Url)
+ end.
-spec make_get_request(url(), string()) ->
- {ok, proplist(), proplist()} | {error, _}.
+ {ok, proplist(), proplist()} | {error, _}.
make_get_request(Url, ResponseType) ->
- Options = [],
- HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
- case httpc:request(get, {Url, []}, HttpOptions, Options) of
- {ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
- decode_response(Head, Body, ResponseType);
- Error ->
- failed_http_request(Error, Url)
- end.
+ Options = [],
+ HttpOptions = [{timeout, ?REQUEST_TIMEOUT}],
+ case httpc:request(get, {Url, []}, HttpOptions, Options) of
+ {ok, {{_, Code, _}, Head, Body}} when Code >= 200, Code =< 299 ->
+ decode_response(Head, Body, ResponseType);
+ Error ->
+ failed_http_request(Error, Url)
+ end.
-spec prepare_post_request(url(), jose_jwk:key(), jiffy:json_value(),
- nonce(), handle_resp_fun()) -> {ok, _, nonce()} | {error, _}.
+ nonce(), handle_resp_fun()) -> {ok, _, nonce()} | {error, _}.
prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun) ->
- prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun, "application/jose+json").
+ prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun, "application/jose+json").
-spec prepare_post_request(url(), jose_jwk:key(), jiffy:json_value(),
- nonce(), handle_resp_fun(), string()) -> {ok, _, nonce()} | {error, _}.
+ nonce(), handle_resp_fun(), string()) -> {ok, _, nonce()} | {error, _}.
prepare_post_request(Url, PrivateKey, EJson, Nonce, HandleRespFun, ResponseType) ->
- case encode(EJson) of
- {ok, ReqBody} ->
- FinalBody = sign_encode_json_jose(PrivateKey, ReqBody, Nonce),
- case make_post_request(Url, FinalBody, ResponseType) of
- {ok, Head, Return} ->
- HandleRespFun({ok, Head, Return});
- Error ->
- Error
- end;
- {error, Reason} ->
- ?ERROR_MSG("Error: ~p when encoding: ~p", [Reason, EJson]),
- {error, Reason}
- end.
+ case encode(EJson) of
+ {ok, ReqBody} ->
+ FinalBody = sign_encode_json_jose(PrivateKey, ReqBody, Nonce),
+ case make_post_request(Url, FinalBody, ResponseType) of
+ {ok, Head, Return} ->
+ HandleRespFun({ok, Head, Return});
+ Error ->
+ Error
+ end;
+ {error, Reason} ->
+ ?ERROR_MSG("Error: ~p when encoding: ~p", [Reason, EJson]),
+ {error, Reason}
+ end.
-spec prepare_get_request(url(), handle_resp_fun()) ->
- {ok, _, nonce()} | {error, _}.
+ {ok, _, nonce()} | {error, _}.
prepare_get_request(Url, HandleRespFun) ->
- prepare_get_request(Url, HandleRespFun, "application/jose+json").
+ prepare_get_request(Url, HandleRespFun, "application/jose+json").
-spec prepare_get_request(url(), handle_resp_fun(), string()) ->
- {ok, _, nonce()} | {error, _}.
+ {ok, _, nonce()} | {error, _}.
prepare_get_request(Url, HandleRespFun, ResponseType) ->
- case make_get_request(Url, ResponseType) of
- {ok, Head, Return} ->
- HandleRespFun({ok, Head, Return});
- Error ->
- Error
- end.
+ case make_get_request(Url, ResponseType) of
+ {ok, Head, Return} ->
+ HandleRespFun({ok, Head, Return});
+ Error ->
+ Error
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -428,19 +420,19 @@ sign_json_jose(Key, Json, Nonce) ->
%% TODO: Ensure this works for all cases
AlgMap = jose_jwk:signer(Key),
JwsMap =
- #{ <<"jwk">> => PubKeyJson
- % , <<"b64">> => true
- , <<"nonce">> => list_to_bitstring(Nonce)
- },
+ #{ <<"jwk">> => PubKeyJson,
+ %% <<"b64">> => true,
+ <<"nonce">> => list_to_bitstring(Nonce)
+ },
JwsObj0 = maps:merge(JwsMap, AlgMap),
JwsObj = jose_jws:from(JwsObj0),
jose_jws:sign(Key, Json, JwsObj).
-spec sign_encode_json_jose(jose_jwk:key(), string(), nonce()) -> bitstring().
sign_encode_json_jose(Key, Json, Nonce) ->
- {_, Signed} = sign_json_jose(Key, Json, Nonce),
- %% This depends on jose library, so we can consider it safe
- jiffy:encode(Signed).
+ {_, Signed} = sign_json_jose(Key, Json, Nonce),
+ %% This depends on jose library, so we can consider it safe
+ jiffy:encode(Signed).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -451,32 +443,33 @@ sign_encode_json_jose(Key, Json, Nonce) ->
-spec get_nonce(proplist()) -> nonce() | 'none'.
get_nonce(Head) ->
- case proplists:lookup("replay-nonce", Head) of
- {"replay-nonce", Nonce} -> Nonce;
- none -> none
- end.
+ case proplists:lookup("replay-nonce", Head) of
+ {"replay-nonce", Nonce} -> Nonce;
+ none -> none
+ end.
-spec get_location(proplist()) -> url() | 'none'.
get_location(Head) ->
- case proplists:lookup("location", Head) of
- {"location", Location} -> Location;
- none -> none
- end.
+ case proplists:lookup("location", Head) of
+ {"location", Location} -> Location;
+ none -> none
+ end.
%% Very bad way to extract this
%% TODO: Find a better way
-spec get_tos(proplist()) -> url() | 'none'.
get_tos(Head) ->
- try
- [{_, Link}] = [{K, V} || {K, V} <- Head,
- K =:= "link" andalso lists:suffix("\"terms-of-service\"", V)],
- [Link1, _] = string:tokens(Link, ";"),
- Link2 = string:strip(Link1, left, $<),
- string:strip(Link2, right, $>)
- catch
- _:_ ->
- none
- end.
+ try
+ [{_, Link}] = [{K, V} || {K, V} <- Head,
+ K =:= "link" andalso
+ lists:suffix("\"terms-of-service\"", V)],
+ [Link1, _] = string:tokens(Link, ";"),
+ Link2 = string:strip(Link1, left, $<),
+ string:strip(Link2, right, $>)
+ catch
+ _:_ ->
+ none
+ end.
-spec get_challenges(proplist()) -> [{proplist()}].
get_challenges(Body) ->
@@ -484,32 +477,32 @@ get_challenges(Body) ->
Challenges.
decode_response(Head, Body, "application/pkix-cert") ->
- {ok, Head, Body};
+ {ok, Head, Body};
decode_response(Head, Body, "application/jose+json") ->
- case decode(Body) of
- {ok, Return} ->
- {ok, Head, Return};
- {error, Reason} ->
- ?ERROR_MSG("Problem decoding: ~s", [Body]),
- {error, Reason}
- end.
+ case decode(Body) of
+ {ok, Return} ->
+ {ok, Head, Return};
+ {error, Reason} ->
+ ?ERROR_MSG("Problem decoding: ~s", [Body]),
+ {error, Reason}
+ end.
encode(EJson) ->
- try
- {ok, jiffy:encode(EJson)}
- catch
- _:Reason ->
- {error, Reason}
- end.
+ try
+ {ok, jiffy:encode(EJson)}
+ catch
+ _:Reason ->
+ {error, Reason}
+ end.
decode(Json) ->
- try
- {Result} = jiffy:decode(Json),
- {ok, Result}
- catch
- _:Reason ->
- {error, Reason}
- end.
+ try
+ {Result} = jiffy:decode(Json),
+ {ok, Result}
+ catch
+ _:Reason ->
+ {error, Reason}
+ end.
is_error({error, _}) -> true;
is_error(_) -> false.
@@ -522,13 +515,13 @@ is_error(_) -> false.
-spec failed_http_request({ok, _} | {error, _}, url()) -> {error, _}.
failed_http_request({ok, {{_, Code, _}, _Head, Body}}, Url) ->
- ?ERROR_MSG("Got unexpected status code from <~s>: ~B, Body: ~s",
- [Url, Code, Body]),
- {error, unexpected_code};
+ ?ERROR_MSG("Got unexpected status code from <~s>: ~B, Body: ~s",
+ [Url, Code, Body]),
+ {error, unexpected_code};
failed_http_request({error, Reason}, Url) ->
- ?ERROR_MSG("Error making a request to <~s>: ~p",
- [Url, Reason]),
- {error, Reason}.
+ ?ERROR_MSG("Error making a request to <~s>: ~p",
+ [Url, Reason]),
+ {error, Reason}.
@@ -540,121 +533,119 @@ failed_http_request({error, Reason}, Url) ->
%% A typical acme workflow
scenario(CAUrl, AccId, PrivateKey) ->
- DirURL = CAUrl ++ "/directory",
- {ok, Dirs, Nonce0} = directory(DirURL),
+ DirURL = CAUrl ++ "/directory",
+ {ok, Dirs, Nonce0} = directory(DirURL),
- AccURL = CAUrl ++ "/acme/reg/" ++ AccId,
- {ok, {_TOS, Account}, Nonce1} = get_account(AccURL, PrivateKey, Nonce0),
- ?INFO_MSG("Account: ~p~n", [Account]),
+ AccURL = CAUrl ++ "/acme/reg/" ++ AccId,
+ {ok, {_TOS, Account}, Nonce1} = get_account(AccURL, PrivateKey, Nonce0),
+ ?INFO_MSG("Account: ~p~n", [Account]),
- #{"new-authz" := NewAuthz} = Dirs,
- Req =
- [ { <<"identifier">>, {
- [ {<<"type">>, <<"dns">>}
- , {<<"value">>, <<"my-acme-test-ejabberd.com">>}
- ] }}
- , {<<"existing">>, <<"accept">>}
- ],
- {ok, Authz, Nonce2} = new_authz(NewAuthz, PrivateKey, Req, Nonce1),
+ #{"new-authz" := NewAuthz} = Dirs,
+ Req =
+ [{<<"identifier">>,
+ {[{<<"type">>, <<"dns">>},
+ {<<"value">>, <<"my-acme-test-ejabberd.com">>}]}},
+ {<<"existing">>, <<"accept">>}
+ ],
+ {ok, Authz, Nonce2} = new_authz(NewAuthz, PrivateKey, Req, Nonce1),
- {Account, Authz, PrivateKey}.
+ {Account, Authz, PrivateKey}.
new_user_scenario(CAUrl, HttpDir) ->
- PrivateKey = generate_key(),
-
- DirURL = CAUrl ++ "/directory",
- {ok, Dirs, Nonce0} = directory(DirURL),
- % ?INFO_MSG("Directories: ~p", [Dirs]),
-
- #{"new-reg" := NewAccURL} = Dirs,
- Req0 = [{ <<"contact">>, [<<"mailto:cert-example-admin@example2.com">>]}],
- {ok, {TOS, Account}, Nonce1} = new_account(NewAccURL, PrivateKey, Req0, Nonce0),
-
- {_, AccId} = proplists:lookup(<<"id">>, Account),
- AccURL = CAUrl ++ "/acme/reg/" ++ integer_to_list(AccId),
- {ok, {_TOS, Account1}, Nonce2} = get_account(AccURL, PrivateKey, Nonce1),
- % ?INFO_MSG("Old account: ~p~n", [Account1]),
-
- Req1 = [{ <<"agreement">>, list_to_bitstring(TOS)}],
- {ok, Account2, Nonce3} = update_account(AccURL, PrivateKey, Req1, Nonce2),
-
- % %% Delete account
- % {ok, Account3, Nonce4} = delete_account(AccURL, PrivateKey, Nonce3),
- % {ok, {_TOS, Account4}, Nonce5} = get_account(AccURL, PrivateKey, Nonce4),
- % ?INFO_MSG("New account: ~p~n", [Account4]),
-
- % NewKey = generate_key(),
- % KeyChangeUrl = CAUrl ++ "/acme/key-change/",
- % {ok, Account3, Nonce4} = key_roll_over(KeyChangeUrl, AccURL, PrivateKey, NewKey, Nonce3),
- % ?INFO_MSG("Changed key: ~p~n", [Account3]),
-
- % {ok, {_TOS, Account4}, Nonce5} = get_account(AccURL, NewKey, Nonce4),
- % ?INFO_MSG("New account:~p~n", [Account4]),
- % {Account4, PrivateKey}.
-
- AccIdBin = list_to_bitstring(integer_to_list(AccId)),
- #{"new-authz" := NewAuthz} = Dirs,
- DomainName = << <<"my-acme-test-ejabberd">>/binary, AccIdBin/binary, <<".com">>/binary >>,
- Req2 =
- [ { <<"identifier">>, {
- [ {<<"type">>, <<"dns">>}
- , {<<"value">>, DomainName}
- ] }}
- , {<<"existing">>, <<"accept">>}
- ],
- {ok, {AuthzUrl, Authz}, Nonce4} = new_authz(NewAuthz, PrivateKey, Req2, Nonce3),
-
- {ok, Authz2, Nonce5} = get_authz(AuthzUrl),
-
- Challenges = get_challenges(Authz2),
- % ?INFO_MSG("Challenges: ~p~n", [Challenges]),
-
- {ok, ChallengeUrl, KeyAuthz} =
- acme_challenge:solve_challenge(<<"http-01">>, Challenges, {PrivateKey, HttpDir}),
- ?INFO_MSG("File for http-01 challenge written correctly", []),
-
- Req3 =
- [ {<<"type">>, <<"http-01">>}
- , {<<"keyAuthorization">>, KeyAuthz}
- ],
- {ok, SolvedChallenge, Nonce6} = complete_challenge(ChallengeUrl, PrivateKey, Req3, Nonce5),
- % ?INFO_MSG("SolvedChallenge: ~p~n", [SolvedChallenge]),
-
- % timer:sleep(2000),
- {ok, Authz3, Nonce7} = get_authz_until_valid(AuthzUrl),
-
- #{"new-cert" := NewCert} = Dirs,
- CSRSubject = [ {commonName, bitstring_to_list(DomainName)}
- , {organizationName, "Example Corp"}],
- CSR = make_csr(CSRSubject),
- {MegS, Sec, MicS} = erlang:timestamp(),
- NotBefore = xmpp_util:encode_timestamp({MegS-1, Sec, MicS}),
- NotAfter = xmpp_util:encode_timestamp({MegS+1, Sec, MicS}),
- Req4 =
- [ {<<"csr">>, CSR}
- , {<<"notBefore">>, NotBefore}
- , {<<"NotAfter">>, NotAfter}
- ],
- {ok, Certificate, Nonce8} = new_cert(NewCert, PrivateKey, Req4, Nonce7),
-
- {Account2, Authz2, Authz3, CSR, Certificate, PrivateKey}.
+ PrivateKey = generate_key(),
+
+ DirURL = CAUrl ++ "/directory",
+ {ok, Dirs, Nonce0} = directory(DirURL),
+ %% ?INFO_MSG("Directories: ~p", [Dirs]),
+
+ #{"new-reg" := NewAccURL} = Dirs,
+ Req0 = [{ <<"contact">>, [<<"mailto:cert-example-admin@example2.com">>]}],
+ {ok, {TOS, Account}, Nonce1} = new_account(NewAccURL, PrivateKey, Req0, Nonce0),
+
+ {_, AccId} = proplists:lookup(<<"id">>, Account),
+ AccURL = CAUrl ++ "/acme/reg/" ++ integer_to_list(AccId),
+ {ok, {_TOS, Account1}, Nonce2} = get_account(AccURL, PrivateKey, Nonce1),
+ %% ?INFO_MSG("Old account: ~p~n", [Account1]),
+
+ Req1 = [{ <<"agreement">>, list_to_bitstring(TOS)}],
+ {ok, Account2, Nonce3} = update_account(AccURL, PrivateKey, Req1, Nonce2),
+
+ %% Delete account
+ %% {ok, Account3, Nonce4} = delete_account(AccURL, PrivateKey, Nonce3),
+ %% {ok, {_TOS, Account4}, Nonce5} = get_account(AccURL, PrivateKey, Nonce4),
+ %% ?INFO_MSG("New account: ~p~n", [Account4]),
+
+ %% NewKey = generate_key(),
+ %% KeyChangeUrl = CAUrl ++ "/acme/key-change/",
+ %% {ok, Account3, Nonce4} = key_roll_over(KeyChangeUrl, AccURL, PrivateKey, NewKey, Nonce3),
+ %% ?INFO_MSG("Changed key: ~p~n", [Account3]),
+
+ %% {ok, {_TOS, Account4}, Nonce5} = get_account(AccURL, NewKey, Nonce4),
+ %% ?INFO_MSG("New account:~p~n", [Account4]),
+ %% {Account4, PrivateKey}.
+
+ AccIdBin = list_to_bitstring(integer_to_list(AccId)),
+ #{"new-authz" := NewAuthz} = Dirs,
+ DomainName = << <<"my-acme-test-ejabberd">>/binary, AccIdBin/binary, <<".com">>/binary >>,
+ Req2 =
+ [{<<"identifier">>,
+ {[{<<"type">>, <<"dns">>},
+ {<<"value">>, DomainName}]}},
+ {<<"existing">>, <<"accept">>}
+ ],
+ {ok, {AuthzUrl, Authz}, Nonce4} = new_authz(NewAuthz, PrivateKey, Req2, Nonce3),
+
+ {ok, Authz2, Nonce5} = get_authz(AuthzUrl),
+
+ Challenges = get_challenges(Authz2),
+ %% ?INFO_MSG("Challenges: ~p~n", [Challenges]),
+
+ {ok, ChallengeUrl, KeyAuthz} =
+ acme_challenge:solve_challenge(<<"http-01">>, Challenges, {PrivateKey, HttpDir}),
+ ?INFO_MSG("File for http-01 challenge written correctly", []),
+
+ Req3 =
+ [ {<<"type">>, <<"http-01">>}
+ , {<<"keyAuthorization">>, KeyAuthz}
+ ],
+ {ok, SolvedChallenge, Nonce6} = complete_challenge(ChallengeUrl, PrivateKey, Req3, Nonce5),
+ %% ?INFO_MSG("SolvedChallenge: ~p~n", [SolvedChallenge]),
+
+ %% timer:sleep(2000),
+ {ok, Authz3, Nonce7} = get_authz_until_valid(AuthzUrl),
+
+ #{"new-cert" := NewCert} = Dirs,
+ CSRSubject = [{commonName, bitstring_to_list(DomainName)},
+ {organizationName, "Example Corp"}],
+ CSR = make_csr(CSRSubject),
+ {MegS, Sec, MicS} = erlang:timestamp(),
+ NotBefore = xmpp_util:encode_timestamp({MegS-1, Sec, MicS}),
+ NotAfter = xmpp_util:encode_timestamp({MegS+1, Sec, MicS}),
+ Req4 =
+ [{<<"csr">>, CSR},
+ {<<"notBefore">>, NotBefore},
+ {<<"NotAfter">>, NotAfter}
+ ],
+ {ok, Certificate, Nonce8} = new_cert(NewCert, PrivateKey, Req4, Nonce7),
+
+ {Account2, Authz2, Authz3, CSR, Certificate, PrivateKey}.
generate_key() ->
- jose_jwk:generate_key({ec, secp256r1}).
+ jose_jwk:generate_key({ec, secp256r1}).
scenario3() ->
- CSRSubject = [ {commonName, "my-acme-test-ejabberd.com"}
- , {organizationName, "Example Corp"}],
- CSR = make_csr(CSRSubject).
+ CSRSubject = [{commonName, "my-acme-test-ejabberd.com"},
+ {organizationName, "Example Corp"}],
+ CSR = make_csr(CSRSubject).
%% Just a test
scenario0(KeyFile, HttpDir) ->
- PrivateKey = jose_jwk:from_file(KeyFile),
- % scenario("http://localhost:4000", "2", PrivateKey).
- new_user_scenario("http://localhost:4000", HttpDir).
- % scenario3().
+ PrivateKey = jose_jwk:from_file(KeyFile),
+ %% scenario("http://localhost:4000", "2", PrivateKey).
+ new_user_scenario("http://localhost:4000", HttpDir).
+%% scenario3().