1: -module(sasl_external_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -include_lib("common_test/include/ct.hrl").
    6: -include_lib("eunit/include/eunit.hrl").
    7: -include_lib("exml/include/exml.hrl").
    8: 
    9: -import(domain_helper, [domain/0]).
   10: 
   11: all() ->
   12:     [
   13:      {group, fast_tls},
   14:      {group, just_tls}].
   15: 
   16: groups() ->
   17:     [{standard_keep_auth, [{group, registered}, {group, not_registered}]},
   18:      {registered, [parallel], [cert_one_xmpp_addrs_no_identity]},
   19:      {not_registered, [parallel], [cert_one_xmpp_addrs_no_identity_not_registered]},
   20:      {standard, [parallel], standard_test_cases()},
   21:      {use_common_name, [parallel], use_common_name_test_cases()},
   22:      {allow_just_user_identity, [parallel], allow_just_user_identity_test_cases()},
   23:      {demo_verification_module, [parallel], demo_verification_module_test_cases()},
   24:      {self_signed_certs_allowed, [parallel], self_signed_certs_allowed_test_cases()},
   25:      {self_signed_certs_not_allowed, [parallel], self_signed_certs_not_allowed_test_cases()},
   26:      {ca_signed, [self_signed_certs_not_allowed_group() | base_groups()]},
   27:      {self_signed, [self_signed_certs_allowed_group() | base_groups()]},
   28:      {fast_tls, [{group, ca_signed}]},
   29:      {just_tls, all_groups()} ].
   30: 
   31: all_groups() ->
   32:     [{group, self_signed},
   33:      {group, ca_signed}].
   34: 
   35: self_signed_certs_allowed_group() ->
   36:     {group, self_signed_certs_allowed}.
   37: self_signed_certs_not_allowed_group() ->
   38:     {group, self_signed_certs_not_allowed}.
   39: 
   40: base_groups() ->
   41:     [{group, standard},
   42:      {group, standard_keep_auth},
   43:      {group, use_common_name},
   44:      {group, allow_just_user_identity},
   45:      {group, demo_verification_module}].
   46: 
   47: standard_test_cases() ->
   48:     [
   49:      cert_no_xmpp_addrs_fails,
   50:      cert_no_xmpp_addrs_no_identity,
   51:      cert_one_xmpp_addr_identity_correct,
   52:      cert_one_xmpp_addrs_no_identity,
   53:      cert_one_xmpp_addr_wrong_hostname,
   54:      cert_more_xmpp_addrs_identity_correct,
   55:      cert_more_xmpp_addrs_no_identity_fails,
   56:      cert_more_xmpp_addrs_wrong_identity_fails
   57:     ].
   58: 
   59: use_common_name_test_cases() ->
   60:     [
   61:      cert_with_cn_no_xmpp_addrs_identity_correct,
   62:      cert_with_cn_no_xmpp_addrs_wrong_identity_fails,
   63:      cert_with_cn_no_xmpp_addrs_no_identity
   64:     ].
   65: 
   66: allow_just_user_identity_test_cases() ->
   67:     [
   68:      cert_no_xmpp_addrs_just_use_identity
   69:     ].
   70: 
   71: demo_verification_module_test_cases()->
   72:     [cert_no_xmpp_addrs_just_use_identity,
   73:      cert_one_xmpp_addrs_no_identity,
   74:      cert_with_jid_cn_no_xmpp_addrs_no_identity,
   75:      cert_with_jid_cn_many_xmpp_addrs_no_identity,
   76:      cert_more_xmpp_addrs_no_identity_fails,
   77:      cert_no_xmpp_addrs_no_identity].
   78: 
   79: self_signed_certs_allowed_test_cases() ->
   80:     [self_signed_cert_is_allowed_with_tls,
   81:      self_signed_cert_is_allowed_with_ws,
   82:      self_signed_cert_is_allowed_with_bosh,
   83:      no_cert_fails_to_authenticate].
   84: 
   85: self_signed_certs_not_allowed_test_cases() ->
   86:     [self_signed_cert_fails_to_authenticate_with_tls,
   87:      self_signed_cert_fails_to_authenticate_with_ws,
   88:      self_signed_cert_fails_to_authenticate_with_bosh,
   89:      ca_signed_cert_is_allowed_with_ws,
   90:      ca_signed_cert_is_allowed_with_bosh,
   91:      no_cert_fails_to_authenticate].
   92: 
   93: init_per_suite(Config) ->
   94:     Config0 = escalus:init_per_suite(Config),
   95:     Config1 = ejabberd_node_utils:init(Config0),
   96:     ejabberd_node_utils:backup_config_file(Config1),
   97:     generate_certs(Config1).
   98: 
   99: end_per_suite(Config) ->
  100:     ejabberd_node_utils:restore_config_file(Config),
  101:     ejabberd_node_utils:restart_application(mongooseim),
  102:     escalus:end_per_suite(Config).
  103: 
  104: init_per_group(just_tls, Config) ->
  105:     [{tls_module, just_tls} | Config];
  106: init_per_group(fast_tls, Config) ->
  107:     [{tls_module, fast_tls} | Config];
  108: init_per_group(ca_signed, Config) ->
  109:     [{signed, ca},
  110:      {ssl_options, "\n  tls.disconnect_on_failure = false"},
  111:      {verify_mode, "\n  tls.verify_mode = \"peer\""} | Config];
  112: init_per_group(self_signed, Config) ->
  113:     [{signed, self},
  114:      {verify_mode, "\n  tls.verify_mode = \"selfsigned_peer\""} | Config];
  115: init_per_group(standard, Config) ->
  116:     modify_config_and_restart("\"standard\"", Config),
  117:     Config;
  118: init_per_group(standard_keep_auth, Config) ->
  119:     Config1 = [{auth_methods, []} | Config],
  120:     modify_config_and_restart("\"standard\"", Config1),
  121:     case mongoose_helper:supports_sasl_module(cyrsasl_external) of
  122:         false -> {skip, "SASL External not supported"};
  123:         true -> Config1
  124:     end;
  125: init_per_group(registered, Config) ->
  126:     escalus:create_users(Config, [{bob, generate_user_tcp(Config, username("bob", Config))}]);
  127: init_per_group(use_common_name, Config) ->
  128:     modify_config_and_restart("\"standard\", \"common_name\"", Config),
  129:     Config;
  130: init_per_group(allow_just_user_identity, Config) ->
  131:     modify_config_and_restart("\"standard\", \"auth_id\"", Config),
  132:     Config;
  133: init_per_group(demo_verification_module, Config) ->
  134:     modify_config_and_restart("\"cyrsasl_external_verification\"", Config),
  135:     Config;
  136: init_per_group(self_signed_certs_allowed, Config) ->
  137:     modify_config_and_restart("\"standard\"", Config),
  138:     Config;
  139: init_per_group(self_signed_certs_not_allowed, Config) ->
  140:     modify_config_and_restart("\"standard\"", Config),
  141:     Config;
  142: init_per_group(_, Config) ->
  143:     Config.
  144: 
  145: modify_config_and_restart(CyrsaslExternalConfig, Config) ->
  146:     TLSModule = atom_to_list(escalus_config:get_config(tls_module, Config, just_tls)),
  147:     VerifyMode = escalus_config:get_config(verify_mode, Config, ""),
  148:     SSLOpts = case TLSModule of
  149:                   "just_tls" -> escalus_config:get_config(ssl_options, Config, "") ++ VerifyMode;
  150:                   "fast_tls" -> ""
  151:               end,
  152:     AuthMethods = escalus_config:get_config(auth_methods, Config,
  153:                                             [{auth_method, "pki"}, {auth_method_opts, false}]),
  154:     CACertFile = filename:join([path_helper:repo_dir(Config),
  155:                                 "tools", "ssl", "ca-clients", "cacert.pem"]),
  156:     NewConfigValues = [{tls_config, "tls.module = \"" ++ TLSModule ++ "\"\n"
  157:                                     "  tls.certfile = \"priv/ssl/fake_server.pem\"\n"
  158:                                     "  tls.cacertfile = \"" ++ CACertFile ++ "\""
  159:                                     ++ SSLOpts},
  160: 		       {https_config, "tls.certfile = \"priv/ssl/fake_cert.pem\"\n"
  161:                                       "  tls.keyfile = \"priv/ssl/fake_key.pem\"\n"
  162:                                       "  tls.password = \"\"\n"
  163:                                       "  tls.cacertfile = \"" ++ CACertFile ++ "\""
  164:                                       ++ VerifyMode},
  165:                        {cyrsasl_external, CyrsaslExternalConfig},
  166: 		       {sasl_mechanisms, "\"external\""} | AuthMethods],
  167:     ejabberd_node_utils:modify_config_file(NewConfigValues, Config),
  168:     ejabberd_node_utils:restart_application(mongooseim).
  169: 
  170: end_per_group(registered, Config) ->
  171:     escalus:delete_users(Config, [{bob, generate_user_tcp(Config, username("bob", Config))}]);
  172: end_per_group(_, _Config) ->
  173:     ok.
  174: 
  175: cert_more_xmpp_addrs_identity_correct(C) ->
  176:     User = username("kate", C),
  177:     %% More than one xmpp_addr and specified identity, common_name not used
  178:     UserSpec = [{requested_name, requested_name(User)} |
  179: 		generate_user_tcp(C, User)],
  180:     {ok, Client, _} = escalus_connection:start(UserSpec),
  181:     escalus_connection:stop(Client).
  182: 
  183: cert_one_xmpp_addr_identity_correct(C) ->
  184:     User = username("bob", C),
  185:     UserSpec = [{requested_name, requested_name(User)} |
  186:                 generate_user_tcp(C, User)],
  187:     cert_fails_to_authenticate(UserSpec).
  188: 
  189: cert_no_xmpp_addrs_fails(C) ->
  190:     User = username("john", C),
  191:     UserSpec = [{requested_name, requested_name(User)} |
  192:                 generate_user_tcp(C, User)],
  193:     cert_fails_to_authenticate(UserSpec).
  194: 
  195: cert_no_xmpp_addrs_just_use_identity(C) ->
  196:     User = username("not-mike", C),
  197:     UserSpec = [{requested_name, requested_name("mike")} |
  198: 		generate_user_tcp(C, User)],
  199:     {ok, Client, _} = escalus_connection:start(UserSpec),
  200:     escalus_connection:stop(Client).
  201: 
  202: cert_no_xmpp_addrs_no_identity(C) ->
  203:     User = username("john", C),
  204:     UserSpec = generate_user_tcp(C, User),
  205:     cert_fails_to_authenticate(UserSpec).
  206: 
  207: cert_more_xmpp_addrs_no_identity_fails(C) ->
  208:     User = username("not-alice", C),
  209:     UserSpec = generate_user_tcp(C, User),
  210:     cert_fails_to_authenticate(UserSpec).
  211: 
  212: cert_one_xmpp_addrs_no_identity(C) ->
  213:     User = username("bob", C),
  214:     UserSpec = generate_user_tcp(C, User),
  215:     {ok, Client, _} = escalus_connection:start(UserSpec),
  216:     escalus_connection:stop(Client).
  217: 
  218: cert_one_xmpp_addrs_no_identity_not_registered(C) ->
  219:     User = username("bob", C),
  220:     UserSpec = generate_user_tcp(C, User),
  221:     cert_fails_to_authenticate(UserSpec).
  222: 
  223: cert_with_cn_no_xmpp_addrs_no_identity(C) ->
  224:     User = username("john", C),
  225:     UserSpec = generate_user_tcp(C, User),
  226:     {ok, Client, _} = escalus_connection:start(UserSpec),
  227:     escalus_connection:stop(Client).
  228: 
  229: cert_with_jid_cn_no_xmpp_addrs_no_identity(C) ->
  230:     User = add_domain_str("john"),
  231:     UserSpec = generate_user_tcp(C, User),
  232:     {ok, Client, _} = escalus_connection:start(UserSpec),
  233:     escalus_connection:stop(Client).
  234: 
  235: cert_with_jid_cn_many_xmpp_addrs_no_identity(C) ->
  236:     User = add_domain_str("grace"),
  237:     UserSpec = generate_user_tcp(C, User),
  238:     {ok, Client, _} = escalus_connection:start(UserSpec),
  239:     escalus_connection:stop(Client).
  240: 
  241: cert_with_cn_no_xmpp_addrs_identity_correct(C) ->
  242:     User = username("john", C),
  243:     UserSpec = [{requested_name, requested_name(User)} |
  244:                 generate_user_tcp(C, User)],
  245:     {ok, Client, _} = escalus_connection:start(UserSpec),
  246:     escalus_connection:stop(Client).
  247: 
  248: cert_with_cn_no_xmpp_addrs_wrong_identity_fails(C) ->
  249:     User = username("not-mike", C),
  250:     UserSpec = [{requested_name, requested_name("mike")} |
  251:                 generate_user_tcp(C, User)],
  252:     cert_fails_to_authenticate(UserSpec).
  253: 
  254: cert_more_xmpp_addrs_wrong_identity_fails(C) ->
  255:     User = username("grace", C),
  256:     UserSpec = [{requested_name, requested_name(User)} |
  257:                 generate_user_tcp(C, User)],
  258:     cert_fails_to_authenticate(UserSpec).
  259: 
  260: cert_one_xmpp_addr_wrong_hostname(C) ->
  261:     User = username("bob", C),
  262:     UserSpec = [{requested_name, requested_name(User)} |
  263:                 generate_user_tcp(C, User)],
  264:     cert_fails_to_authenticate(UserSpec).
  265: 
  266: ca_signed_cert_is_allowed_with_ws(C) ->
  267:     UserSpec = generate_user(C, "bob", escalus_ws),
  268:     {ok, Client, _} = escalus_connection:start(UserSpec),
  269:     escalus_connection:stop(Client).
  270: 
  271: ca_signed_cert_is_allowed_with_bosh(C) ->
  272:     UserSpec = generate_user(C, "bob", escalus_bosh),
  273:     {ok, Client, _} = escalus_connection:start(UserSpec),
  274:     escalus_connection:stop(Client).
  275: 
  276: self_signed_cert_fails_to_authenticate_with_tls(C) ->
  277:     self_signed_cert_fails_to_authenticate(C, escalus_tcp).
  278: 
  279: self_signed_cert_fails_to_authenticate_with_ws(C) ->
  280:     self_signed_cert_fails_to_authenticate(C, escalus_ws).
  281: 
  282: self_signed_cert_fails_to_authenticate_with_bosh(C) ->
  283:     self_signed_cert_fails_to_authenticate(C, escalus_bosh).
  284: 
  285: cert_fails_to_authenticate(UserSpec) ->
  286:     Self = self(),
  287:     F = fun() ->
  288: 		{ok, Client, _} = escalus_connection:start(UserSpec),
  289: 		Self ! escalus_connected,
  290: 		escalus_connection:stop(Client)
  291: 	end,
  292:     receive_failed_to_authenticate(F).
  293: 
  294: self_signed_cert_fails_to_authenticate(C, EscalusTransport) ->
  295:     Self = self(),
  296:     F = fun() ->
  297: 		UserSpec = generate_user(C, "greg-self-signed", EscalusTransport),
  298: 		{ok, Client, _} = escalus_connection:start(UserSpec),
  299: 		Self ! escalus_connected,
  300: 		escalus_connection:stop(Client)
  301: 	end,
  302:     receive_failed_to_authenticate(F).
  303: 
  304: receive_failed_to_authenticate(F) ->
  305:     %% We spawn the process trying to connect because otherwise the testcase may crash
  306:     %% due linked process crash (client's process are started with start_link)
  307:     Pid = spawn(F),
  308:     MRef = erlang:monitor(process, Pid),
  309:     receive
  310:         {'DOWN', MRef, process, Pid, _Reason} ->
  311:             ok;
  312:         escalus_connected ->
  313:             ct:fail(authenticated_but_should_not)
  314:     after 10000 ->
  315:               ct:fail(timeout_waiting_for_authentication_error)
  316:     end.
  317: 
  318: self_signed_cert_is_allowed_with_tls(C) ->
  319:     self_signed_cert_is_allowed_with(escalus_tcp, C).
  320: 
  321: self_signed_cert_is_allowed_with_ws(C) ->
  322:     self_signed_cert_is_allowed_with(escalus_ws, C).
  323: 
  324: self_signed_cert_is_allowed_with_bosh(C) ->
  325:     self_signed_cert_is_allowed_with(escalus_bosh, C).
  326: 
  327: self_signed_cert_is_allowed_with(EscalusTransport, C) ->
  328:     UserSpec = generate_user(C, "bob-self-signed", EscalusTransport),
  329:     {ok, Client, _} = escalus_connection:start(UserSpec),
  330:     escalus_connection:stop(Client).
  331: 
  332: no_cert_fails_to_authenticate(_C) ->
  333:     UserSpec = [{username, <<"no_cert_user">>},
  334:                 {server, domain()},
  335:                 {host, <<"localhost">>},
  336:                 {port, ct:get_config({hosts, mim, c2s_port})},
  337:                 {password, <<"break_me">>},
  338:                 {resource, <<>>}, %% Allow the server to generate the resource
  339:                 {auth, {escalus_auth, auth_sasl_external}},
  340:                 {starttls, required},
  341:                 {ssl_opts, [{fail_if_no_peer_cert, false}, {verify, verify_none}]}],
  342: 
  343:     Result = escalus_connection:start(UserSpec),
  344:     ?assertMatch({error, {connection_step_failed, _, _}}, Result),
  345:     {error, {connection_step_failed, _Call, Details}} = Result,
  346:     ?assertMatch({auth_failed, _, #xmlel{name = <<"failure">>}}, Details),
  347:     ok.
  348: 
  349: generate_certs(C) ->
  350:     CA = [#{cn => "not-alice", xmpp_addrs => [add_domain_str("alice"), "alice@fed1"]},
  351:           #{cn => "kate", xmpp_addrs => [add_domain_str("kate"), "kate@fed1"]},
  352:           #{cn => "bob", xmpp_addrs => [add_domain_str("bob")]},
  353:           #{cn => "greg", xmpp_addrs => [add_domain_str("greg")]},
  354:           #{cn => "john", xmpp_addrs => undefined},
  355:           #{cn => add_domain_str("john"), xmpp_addrs => undefined},
  356:           #{cn => "not-mike", xmpp_addrs => undefined},
  357:           #{cn => "grace", xmpp_addrs => ["grace@fed1", "grace@reg1"]},
  358:           #{cn => add_domain_str("grace"), xmpp_addrs => ["grace@fed1", "grace@reg1"]}],
  359:     SelfSigned = [ M#{cn => CN ++ "-self-signed", signed => self, xmpp_addrs => replace_addrs(Addrs)}
  360:                    || M = #{ cn := CN , xmpp_addrs := Addrs} <- CA],
  361:     CertSpecs = CA ++ SelfSigned,
  362:     TemplateValues = #{"xmppOids" => xmpp_oids()},
  363:     Certs = [{maps:get(cn, CertSpec), generate_cert(C, CertSpec, TemplateValues)}
  364:              || CertSpec <- CertSpecs],
  365:     [{certs, maps:from_list(Certs)} | C].
  366: 
  367: generate_cert(C, #{cn := User} = CertSpec, BasicTemplateValues) ->
  368:     ConfigTemplate = filename:join(?config(mim_data_dir, C), "openssl-user.cnf"),
  369:     {ok, Template} = file:read_file(ConfigTemplate),
  370:     XMPPAddrs = maps:get(xmpp_addrs, CertSpec, undefined),
  371:     TemplateValues = maps:merge(BasicTemplateValues, prepare_template_values(User, XMPPAddrs)),
  372:     OpenSSLConfig = bbmustache:render(Template, TemplateValues),
  373:     UserConfig = filename:join(?config(priv_dir, C), User ++ ".cfg"),
  374:     ct:log("OpenSSL config: ~ts~n~ts", [UserConfig, OpenSSLConfig]),
  375:     file:write_file(UserConfig, OpenSSLConfig),
  376:     UserKey = filename:join(?config(priv_dir, C), User ++ "_key.pem"),
  377:     case maps:get(signed, CertSpec, ca) of
  378: 	ca ->
  379: 	    generate_ca_signed_cert(C, User, UserConfig, UserKey);
  380: 	self ->
  381: 	    generate_self_signed_cert(C, User, UserConfig, UserKey)
  382:     end.
  383: 
  384: generate_ca_signed_cert(C, User, UserConfig, UserKey ) ->
  385:     UserCsr = filename:join(?config(priv_dir, C), User ++ ".csr"),
  386:     Cmd = ["openssl req -config ", UserConfig, " -newkey rsa:2048 -sha256 -nodes -out ",
  387:            UserCsr, " -keyout ", UserKey, " -outform PEM"],
  388:     Out = os:cmd(Cmd),
  389:     ct:log("generate_ca_signed_cert 1:~nCmd ~p~nOut ~ts", [Cmd, Out]),
  390:     UserCert = filename:join(?config(priv_dir, C), User ++ "_cert.pem"),
  391:     SignCmd = filename:join(?config(mim_data_dir, C), "sign_cert.sh"),
  392:     Cmd2 = [SignCmd, " --req ", UserCsr, " --out ", UserCert],
  393:     SSLDir = filename:join([path_helper:repo_dir(C), "tools", "ssl"]),
  394:     OutLog = os:cmd("cd " ++ SSLDir ++ " && " ++ Cmd2),
  395:     ct:log("generate_ca_signed_cert 2:~nCmd ~p~nOut ~ts", [Cmd2, OutLog]),
  396:     #{key => UserKey,
  397:       cert => UserCert}.
  398: 
  399: generate_self_signed_cert(C, User, UserConfig, UserKey) ->
  400:     UserCert = filename:join(?config(priv_dir, C), User ++ "_self_signed_cert.pem"),
  401:     Cmd = ["openssl req -config ", UserConfig, " -newkey rsa:2048 -sha256 -nodes -out ",
  402:            UserCert, " -keyout ", UserKey, " -x509 -outform PEM -extensions client_req_extensions"],
  403:     OutLog = os:cmd(Cmd),
  404:     ct:log("generate_self_signed_cert:~nCmd ~p~nOut ~ts", [Cmd, OutLog]),
  405:     #{key => UserKey,
  406:       cert => UserCert}.
  407: 
  408: generate_user_tcp(C, User) ->
  409:     generate_user(C, User, escalus_tcp).
  410: 
  411: generate_user(C, User, Transport) ->
  412:     Certs = ?config(certs, C),
  413:     UserCert = maps:get(User, Certs),
  414:     Common = [{username, list_to_binary(User)},
  415:               {server, domain()},
  416:               {host, <<"localhost">>},
  417:               {password, <<"break_me">>},
  418:               {resource, <<>>}, %% Allow the server to generate the resource
  419:               {auth, {escalus_auth, auth_sasl_external}},
  420:               {transport, Transport},
  421:               {ssl_opts, [{verify, verify_none},
  422:                           {versions, ['tlsv1.2']},
  423:                           {certfile, maps:get(cert, UserCert)},
  424:                           {keyfile, maps:get(key, UserCert)}]}],
  425:     Common ++ transport_specific_options(Transport)
  426:     ++ [{port, ct:get_config({hosts, mim, c2s_port})}].
  427: 
  428: transport_specific_options(escalus_tcp) ->
  429:     [{starttls, required}];
  430: transport_specific_options(_) ->
  431:      [{port, ct:get_config({hosts, mim, cowboy_secure_port})},
  432:       {ssl, true}].
  433: 
  434: prepare_template_values(User, XMPPAddrsIn) ->
  435:     XMPPAddrs = maybe_prepare_xmpp_addresses(XMPPAddrsIn),
  436:     #{"cn" => User, "xmppAddrs" => XMPPAddrs}.
  437: 
  438: xmpp_oids() ->
  439:     case os:cmd("openssl list -objects | grep id-on-xmppAddr") of
  440:         "id-on-xmppAddr" ++ _ -> ""; % already defined in OpenSSL 3.*
  441:         _ -> "id-on-xmppAddr = 1.3.6.1.5.5.7.8.5\n"
  442:     end.
  443: 
  444: maybe_prepare_xmpp_addresses(undefined) ->
  445:     "";
  446: maybe_prepare_xmpp_addresses(Addrs) when is_list(Addrs) ->
  447:     AddrsWithSeq = lists:zip(Addrs, lists:seq(1, length(Addrs))),
  448:     Entries = [make_xmpp_addr_entry(Addr, I) || {Addr, I} <- AddrsWithSeq],
  449:     string:join(Entries, "\n").
  450: 
  451: make_xmpp_addr_entry(Addr, I) ->
  452:     % id-on-xmppAddr OID is specified in the openssl-user.cnf config file
  453:     "otherName." ++ integer_to_list(I) ++ " = id-on-xmppAddr;UTF8:" ++ Addr.
  454: 
  455: requested_name(User) ->
  456:     add_domain(list_to_binary(User)).
  457: 
  458: username(Name, Config) ->
  459:     case escalus_config:get_config(signed, Config, ca) of
  460:         self ->
  461:             Name ++ "-self-signed";
  462:         ca ->
  463:             Name
  464:     end.
  465: 
  466: replace_addrs(undefined) ->
  467:     undefined;
  468: replace_addrs(Addresses) ->
  469:     lists:map( fun(Addr) -> [User, Hostname] = binary:split(list_to_binary(Addr), <<"@">>),
  470:                             binary_to_list(<<User/binary, <<"-self-signed@">>/binary, Hostname/binary>>) end, Addresses).
  471: 
  472: -spec add_domain_str(User :: string()) -> string().
  473: add_domain_str(User) ->
  474:     User ++ "@" ++ binary:bin_to_list(domain()).
  475: 
  476: -spec add_domain(User :: binary()) -> binary().
  477: add_domain(User) ->
  478:     <<User/binary, "@", (domain())/binary>>.