1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2008-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: %% 21: -module(public_key_SUITE). 22: 23: -include_lib("common_test/include/ct.hrl"). 24: -include_lib("public_key/include/public_key.hrl"). 25: 26: %% Note: This directive should only be used in test suites. 27: -compile(export_all). 28: 29: -define(TIMEOUT, 120000). % 2 min 30: 31: 32: %%-------------------------------------------------------------------- 33: %% Common Test interface functions ----------------------------------- 34: %%-------------------------------------------------------------------- 35: 36: suite() -> [{ct_hooks,[ts_install_cth]}]. 37: 38: all() -> 39: [app, 40: {group, pem_decode_encode}, 41: {group, ssh_public_key_decode_encode}, 42: encrypt_decrypt, 43: {group, sign_verify}, 44: pkix, pkix_countryname, pkix_emailaddress, pkix_path_validation, 45: pkix_iso_rsa_oid, pkix_iso_dsa_oid]. 46: 47: groups() -> 48: [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem, 49: dh_pem, cert_pem, pkcs7_pem, pkcs10_pem]}, 50: {ssh_public_key_decode_encode, [], 51: [ssh_rsa_public_key, ssh_dsa_public_key, ssh_rfc4716_rsa_comment, 52: ssh_rfc4716_dsa_comment, ssh_rfc4716_rsa_subject, ssh_known_hosts, 53: ssh_auth_keys, ssh1_known_hosts, ssh1_auth_keys, ssh_openssh_public_key_with_comment, 54: ssh_openssh_public_key_long_header]}, 55: {sign_verify, [], [rsa_sign_verify, dsa_sign_verify]} 56: ]. 57: %%------------------------------------------------------------------- 58: init_per_suite(Config) -> 59: application:stop(crypto), 60: try crypto:start() of 61: ok -> 62: application:start(asn1), 63: Config 64: catch _:_ -> 65: {skip, "Crypto did not start"} 66: end. 67: 68: end_per_suite(_Config) -> 69: application:stop(asn1), 70: application:stop(crypto). 71: 72: %%------------------------------------------------------------------- 73: init_per_group(_GroupName, Config) -> 74: Config. 75: 76: end_per_group(_GroupName, Config) -> 77: Config. 78: %%------------------------------------------------------------------- 79: init_per_testcase(_TestCase, Config0) -> 80: Config = lists:keydelete(watchdog, 1, Config0), 81: Dog = ct:timetrap(?TIMEOUT), 82: [{watchdog, Dog} | Config]. 83: 84: 85: end_per_testcase(_TestCase, _Config) -> 86: ok. 87: %%-------------------------------------------------------------------- 88: %% Test Cases -------------------------------------------------------- 89: %%-------------------------------------------------------------------- 90: 91: app() -> 92: [{doc, "Test that the public_key app file is ok"}]. 93: app(Config) when is_list(Config) -> 94: ok = ?t:app_test(public_key). 95: 96: %%-------------------------------------------------------------------- 97: 98: dsa_pem() -> 99: [{doc, "DSA PEM-file decode/encode"}]. 100: dsa_pem(Config) when is_list(Config) -> 101: Datadir = ?config(data_dir, Config), 102: 103: [{'DSAPrivateKey', DerDSAKey, not_encrypted} = Entry0 ] = 104: erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")), 105: 106: DSAKey = public_key:der_decode('DSAPrivateKey', DerDSAKey), 107: 108: DSAKey = public_key:pem_entry_decode(Entry0), 109: 110: {ok, DSAPubPem} = file:read_file(filename:join(Datadir, "dsa_pub.pem")), 111: [{'SubjectPublicKeyInfo', _, _} = PubEntry0] = 112: public_key:pem_decode(DSAPubPem), 113: DSAPubKey = public_key:pem_entry_decode(PubEntry0), 114: true = check_entry_type(DSAPubKey, 'DSAPublicKey'), 115: PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', DSAPubKey), 116: DSAPubPemNoEndNewLines = strip_ending_newlines(DSAPubPem), 117: DSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])). 118: 119: %%-------------------------------------------------------------------- 120: 121: rsa_pem() -> 122: [{doc, "RSA PEM-file decode/encode"}]. 123: rsa_pem(Config) when is_list(Config) -> 124: Datadir = ?config(data_dir, Config), 125: [{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry0 ] = 126: erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")), 127: 128: RSAKey0 = public_key:der_decode('RSAPrivateKey', DerRSAKey), 129: 130: RSAKey0 = public_key:pem_entry_decode(Entry0), 131: 132: [{'RSAPrivateKey', _, {_,_}} = Entry1] = 133: erl_make_certs:pem_to_der(filename:join(Datadir, "rsa.pem")), 134: 135: true = check_entry_type(public_key:pem_entry_decode(Entry1, "abcd1234"), 136: 'RSAPrivateKey'), 137: 138: {ok, RSAPubPem} = file:read_file(filename:join(Datadir, "rsa_pub.pem")), 139: [{'SubjectPublicKeyInfo', _, _} = PubEntry0] = 140: public_key:pem_decode(RSAPubPem), 141: RSAPubKey = public_key:pem_entry_decode(PubEntry0), 142: true = check_entry_type(RSAPubKey, 'RSAPublicKey'), 143: PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey), 144: RSAPubPemNoEndNewLines = strip_ending_newlines(RSAPubPem), 145: RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])), 146: 147: {ok, RSARawPem} = file:read_file(filename:join(Datadir, "rsa_pub_key.pem")), 148: [{'RSAPublicKey', _, _} = PubEntry1] = 149: public_key:pem_decode(RSARawPem), 150: RSAPubKey = public_key:pem_entry_decode(PubEntry1), 151: RSARawPemNoEndNewLines = strip_ending_newlines(RSARawPem), 152: RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])). 153: 154: %%-------------------------------------------------------------------- 155: 156: encrypted_pem() -> 157: [{doc, "Encrypted PEM-file decode/encode"}]. 158: encrypted_pem(Config) when is_list(Config) -> 159: Datadir = ?config(data_dir, Config), 160: 161: [{'RSAPrivateKey', DerRSAKey, not_encrypted}] = 162: erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")), 163: 164: RSAKey = public_key:der_decode('RSAPrivateKey', DerRSAKey), 165: 166: Salt0 = crypto:rand_bytes(8), 167: Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey, 168: {{"DES-EDE3-CBC", Salt0}, "1234abcd"}), 169: RSAKey = public_key:pem_entry_decode(Entry0,"1234abcd"), 170: Des3KeyFile = filename:join(Datadir, "des3_client_key.pem"), 171: erl_make_certs:der_to_pem(Des3KeyFile, [Entry0]), 172: [{'RSAPrivateKey', _, {"DES-EDE3-CBC", Salt0}}] = 173: erl_make_certs:pem_to_der(Des3KeyFile), 174: 175: Salt1 = crypto:rand_bytes(8), 176: Entry1 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey, 177: {{"DES-CBC", Salt1}, "4567efgh"}), 178: DesKeyFile = filename:join(Datadir, "des_client_key.pem"), 179: erl_make_certs:der_to_pem(DesKeyFile, [Entry1]), 180: [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry2] = 181: erl_make_certs:pem_to_der(DesKeyFile), 182: true = check_entry_type(public_key:pem_entry_decode(Entry2, "4567efgh"), 183: 'RSAPrivateKey'). 184: 185: %%-------------------------------------------------------------------- 186: 187: dh_pem() -> 188: [{doc, "DH parametrs PEM-file decode/encode"}]. 189: dh_pem(Config) when is_list(Config) -> 190: Datadir = ?config(data_dir, Config), 191: [{'DHParameter', _DerDH, not_encrypted} = Entry] = 192: erl_make_certs:pem_to_der(filename:join(Datadir, "dh.pem")), 193: asn1_encode_decode(Entry). 194: 195: %%-------------------------------------------------------------------- 196: 197: pkcs10_pem() -> 198: [{doc, "PKCS-10 PEM-file decode/encode"}]. 199: pkcs10_pem(Config) when is_list(Config) -> 200: Datadir = ?config(data_dir, Config), 201: [{'CertificationRequest', _DerPKCS10, not_encrypted} = Entry] = 202: erl_make_certs:pem_to_der(filename:join(Datadir, "req.pem")), 203: asn1_encode_decode(Entry). 204: %%-------------------------------------------------------------------- 205: pkcs7_pem() -> 206: [{doc, "PKCS-7 PEM-file decode/encode"}]. 207: pkcs7_pem(Config) when is_list(Config) -> 208: Datadir = ?config(data_dir, Config), 209: [{'ContentInfo', _, not_encrypted} = Entry0] = 210: erl_make_certs:pem_to_der(filename:join(Datadir, "pkcs7_cert.pem")), 211: [{'ContentInfo', _, not_encrypted} = Entry1] = 212: erl_make_certs:pem_to_der(filename:join(Datadir, "pkcs7_ext.pem")), 213: asn1_encode_decode(Entry0), 214: asn1_encode_decode(Entry1). 215: 216: %%-------------------------------------------------------------------- 217: cert_pem() -> 218: [{doc, "Certificate PEM-file decode/encode"}]. 219: cert_pem(Config) when is_list(Config) -> 220: Datadir = ?config(data_dir, Config), 221: 222: [{'Certificate', _, not_encrypted} = Entry0] = 223: erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")), 224: 225: asn1_encode_decode(Entry0), 226: 227: [{'Certificate', _, not_encrypted} = Entry1, 228: {'Certificate', _, not_encrypted} = Entry2] = 229: erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")), 230: 231: asn1_encode_decode(Entry1), 232: asn1_encode_decode(Entry2). 233: 234: %%-------------------------------------------------------------------- 235: ssh_rsa_public_key() -> 236: [{doc, "ssh rsa public key decode/encode"}]. 237: ssh_rsa_public_key(Config) when is_list(Config) -> 238: Datadir = ?config(data_dir, Config), 239: 240: {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")), 241: [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, public_key), 242: [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, rfc4716_public_key), 243: 244: {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")), 245: [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, public_key), 246: [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, openssh_public_key), 247: 248: %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh 249: %% = RSARawOpenSsh as line breakpoints may differ 250: 251: EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), 252: EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), 253: 254: [{PubKey, Attributes1}] = 255: public_key:ssh_decode(EncodedSSh, public_key), 256: [{PubKey, Attributes2}] = 257: public_key:ssh_decode(EncodedOpenSsh, public_key). 258: 259: %%-------------------------------------------------------------------- 260: 261: ssh_dsa_public_key() -> 262: [{doc, "ssh dsa public key decode/encode"}]. 263: ssh_dsa_public_key(Config) when is_list(Config) -> 264: Datadir = ?config(data_dir, Config), 265: 266: {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")), 267: [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, public_key), 268: [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, rfc4716_public_key), 269: 270: {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")), 271: [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, public_key), 272: [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key), 273: 274: %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh 275: %% = DSARawOpenSsh as line breakpoints may differ 276: 277: EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key), 278: EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key), 279: 280: [{PubKey, Attributes1}] = 281: public_key:ssh_decode(EncodedSSh, public_key), 282: [{PubKey, Attributes2}] = 283: public_key:ssh_decode(EncodedOpenSsh, public_key). 284: 285: %%-------------------------------------------------------------------- 286: ssh_rfc4716_rsa_comment() -> 287: [{doc, "Test comment header and rsa key"}]. 288: ssh_rfc4716_rsa_comment(Config) when is_list(Config) -> 289: Datadir = ?config(data_dir, Config), 290: 291: {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")), 292: [{#'RSAPublicKey'{} = PubKey, Attributes}] = 293: public_key:ssh_decode(RSARawSsh2, public_key), 294: 295: Headers = proplists:get_value(headers, Attributes), 296: 297: Value = proplists:get_value("Comment", Headers, undefined), 298: true = Value =/= undefined, 299: RSARawSsh2 = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key). 300: 301: %%-------------------------------------------------------------------- 302: ssh_rfc4716_dsa_comment() -> 303: [{doc, "Test comment header and dsa key"}]. 304: ssh_rfc4716_dsa_comment(Config) when is_list(Config) -> 305: Datadir = ?config(data_dir, Config), 306: 307: {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")), 308: [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] = 309: public_key:ssh_decode(DSARawSsh2, public_key), 310: 311: Headers = proplists:get_value(headers, Attributes), 312: 313: Value = proplists:get_value("Comment", Headers, undefined), 314: true = Value =/= undefined, 315: 316: %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ 317: Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), 318: [{PubKey, Attributes}] = 319: public_key:ssh_decode(Encoded, public_key). 320: 321: %%-------------------------------------------------------------------- 322: ssh_rfc4716_rsa_subject() -> 323: [{doc, "Test another header value than comment"}]. 324: ssh_rfc4716_rsa_subject(Config) when is_list(Config) -> 325: Datadir = ?config(data_dir, Config), 326: 327: {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")), 328: [{#'RSAPublicKey'{} = PubKey, Attributes}] = 329: public_key:ssh_decode(RSARawSsh2, public_key), 330: 331: Headers = proplists:get_value(headers, Attributes), 332: 333: Value = proplists:get_value("Subject", Headers, undefined), 334: true = Value =/= undefined, 335: 336: %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ 337: Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key), 338: [{PubKey, Attributes}] = 339: public_key:ssh_decode(Encoded, public_key). 340: 341: %%-------------------------------------------------------------------- 342: ssh_known_hosts() -> 343: [{doc, "ssh known hosts file encode/decode"}]. 344: ssh_known_hosts(Config) when is_list(Config) -> 345: Datadir = ?config(data_dir, Config), 346: 347: {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")), 348: [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}, 349: {#'RSAPublicKey'{}, Attributes3}, {#'RSAPublicKey'{}, Attributes4}] = Decoded = 350: public_key:ssh_decode(SshKnownHosts, known_hosts), 351: 352: Comment1 = undefined, 353: Comment2 = "foo@bar.com", 354: Comment3 = "Comment with whitespaces", 355: Comment4 = "foo@bar.com Comment with whitespaces", 356: 357: Comment1 = proplists:get_value(comment, Attributes1, undefined), 358: Comment2 = proplists:get_value(comment, Attributes2), 359: Comment3 = proplists:get_value(comment, Attributes3), 360: Comment4 = proplists:get_value(comment, Attributes4), 361: 362: Value1 = proplists:get_value(hostnames, Attributes1, undefined), 363: Value2 = proplists:get_value(hostnames, Attributes2, undefined), 364: true = (Value1 =/= undefined) and (Value2 =/= undefined), 365: 366: Encoded = public_key:ssh_encode(Decoded, known_hosts), 367: Decoded = public_key:ssh_decode(Encoded, known_hosts). 368: 369: %%-------------------------------------------------------------------- 370: 371: ssh1_known_hosts() -> 372: [{doc, "ssh (ver 1) known hosts file encode/decode"}]. 373: ssh1_known_hosts(Config) when is_list(Config) -> 374: Datadir = ?config(data_dir, Config), 375: 376: {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")), 377: [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2},{#'RSAPublicKey'{}, Attributes3}] 378: = Decoded = public_key:ssh_decode(SshKnownHosts, known_hosts), 379: 380: Value1 = proplists:get_value(hostnames, Attributes1, undefined), 381: Value2 = proplists:get_value(hostnames, Attributes2, undefined), 382: true = (Value1 =/= undefined) and (Value2 =/= undefined), 383: 384: Comment ="dhopson@VMUbuntu-DSH comment with whitespaces", 385: Comment = proplists:get_value(comment, Attributes3), 386: 387: Encoded = public_key:ssh_encode(Decoded, known_hosts), 388: Decoded = public_key:ssh_decode(Encoded, known_hosts). 389: 390: %%-------------------------------------------------------------------- 391: ssh_auth_keys() -> 392: [{doc, "ssh authorized keys file encode/decode"}]. 393: ssh_auth_keys(Config) when is_list(Config) -> 394: Datadir = ?config(data_dir, Config), 395: 396: {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")), 397: [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, Attributes2}, 398: {#'RSAPublicKey'{}, Attributes3}, {{_, #'Dss-Parms'{}}, Attributes4} 399: ] = Decoded = 400: public_key:ssh_decode(SshAuthKeys, auth_keys), 401: 402: Value1 = proplists:get_value(options, Attributes1, undefined), 403: true = Value1 =/= undefined, 404: 405: Comment1 = Comment2 = "dhopson@VMUbuntu-DSH", 406: Comment3 = Comment4 ="dhopson@VMUbuntu-DSH comment with whitespaces", 407: 408: Comment1 = proplists:get_value(comment, Attributes1), 409: Comment2 = proplists:get_value(comment, Attributes2), 410: Comment3 = proplists:get_value(comment, Attributes3), 411: Comment4 = proplists:get_value(comment, Attributes4), 412: 413: Encoded = public_key:ssh_encode(Decoded, auth_keys), 414: Decoded = public_key:ssh_decode(Encoded, auth_keys). 415: 416: %%-------------------------------------------------------------------- 417: ssh1_auth_keys() -> 418: [{doc, "ssh (ver 1) authorized keys file encode/decode"}]. 419: ssh1_auth_keys(Config) when is_list(Config) -> 420: Datadir = ?config(data_dir, Config), 421: 422: {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")), 423: [{#'RSAPublicKey'{}, Attributes1}, 424: {#'RSAPublicKey'{}, Attributes2}, {#'RSAPublicKey'{}, Attributes3}, 425: {#'RSAPublicKey'{}, Attributes4}, {#'RSAPublicKey'{}, Attributes5}] = Decoded = 426: public_key:ssh_decode(SshAuthKeys, auth_keys), 427: 428: Value1 = proplists:get_value(bits, Attributes2, undefined), 429: Value2 = proplists:get_value(bits, Attributes3, undefined), 430: true = (Value1 =/= undefined) and (Value2 =/= undefined), 431: 432: Comment2 = Comment3 = "dhopson@VMUbuntu-DSH", 433: Comment4 = Comment5 ="dhopson@VMUbuntu-DSH comment with whitespaces", 434: 435: undefined = proplists:get_value(comment, Attributes1, undefined), 436: Comment2 = proplists:get_value(comment, Attributes2), 437: Comment3 = proplists:get_value(comment, Attributes3), 438: Comment4 = proplists:get_value(comment, Attributes4), 439: Comment5 = proplists:get_value(comment, Attributes5), 440: 441: Encoded = public_key:ssh_encode(Decoded, auth_keys), 442: Decoded = public_key:ssh_decode(Encoded, auth_keys). 443: 444: %%-------------------------------------------------------------------- 445: ssh_openssh_public_key_with_comment() -> 446: [{doc, "Test that emty lines and lines starting with # are ignored"}]. 447: ssh_openssh_public_key_with_comment(Config) when is_list(Config) -> 448: Datadir = ?config(data_dir, Config), 449: 450: {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")), 451: [{{_, #'Dss-Parms'{}}, _}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key). 452: 453: %%-------------------------------------------------------------------- 454: ssh_openssh_public_key_long_header() -> 455: [{doc, "Test that long headers are handled"}]. 456: ssh_openssh_public_key_long_header(Config) when is_list(Config) -> 457: Datadir = ?config(data_dir, Config), 458: 459: {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")), 460: [{#'RSAPublicKey'{}, _}] = Decoded = public_key:ssh_decode(RSARawOpenSsh, public_key), 461: 462: Encoded = public_key:ssh_encode(Decoded, rfc4716_public_key), 463: Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key). 464: 465: %%-------------------------------------------------------------------- 466: encrypt_decrypt() -> 467: [{doc, "Test public_key:encrypt_private and public_key:decrypt_public"}]. 468: encrypt_decrypt(Config) when is_list(Config) -> 469: {PrivateKey, _DerKey} = erl_make_certs:gen_rsa(64), 470: #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} = PrivateKey, 471: PublicKey = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}, 472: Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")), 473: RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey), 474: Msg = public_key:decrypt_public(RsaEncrypted, PublicKey), 475: Msg = public_key:decrypt_public(RsaEncrypted, PrivateKey), 476: RsaEncrypted2 = public_key:encrypt_public(Msg, PublicKey), 477: RsaEncrypted3 = public_key:encrypt_public(Msg, PrivateKey), 478: Msg = public_key:decrypt_private(RsaEncrypted2, PrivateKey), 479: Msg = public_key:decrypt_private(RsaEncrypted3, PrivateKey), 480: 481: ok. 482: 483: %%-------------------------------------------------------------------- 484: rsa_sign_verify() -> 485: [{doc, "Checks that we can sign and verify rsa signatures."}]. 486: rsa_sign_verify(Config) when is_list(Config) -> 487: Ca = {_, CaKey} = erl_make_certs:make_cert([]), 488: {Cert1, _} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]), 489: PrivateRSA = #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} = 490: public_key:pem_entry_decode(CaKey), 491: PublicRSA = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}, 492: true = public_key:pkix_verify(Cert1, PublicRSA), 493: 494: Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")), 495: RSASign = public_key:sign(Msg, sha, PrivateRSA), 496: true = public_key:verify(Msg, sha, RSASign, PublicRSA), 497: false = public_key:verify(<<1:8, Msg/binary>>, sha, RSASign, PublicRSA), 498: false = public_key:verify(Msg, sha, <<1:8, RSASign/binary>>, PublicRSA), 499: 500: RSASign1 = public_key:sign(Msg, md5, PrivateRSA), 501: true = public_key:verify(Msg, md5, RSASign1, PublicRSA). 502: 503: %%-------------------------------------------------------------------- 504: 505: dsa_sign_verify() -> 506: [{doc, "Checks that we can sign and verify dsa signatures."}]. 507: dsa_sign_verify(Config) when is_list(Config) -> 508: Ca = erl_make_certs:make_cert([]), 509: CertInfo = {_,CertKey1} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]), 510: {Cert2,_CertKey} = erl_make_certs:make_cert([{issuer, CertInfo}]), 511: 512: #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y, x=_X} = 513: public_key:pem_entry_decode(CertKey1), 514: true = public_key:pkix_verify(Cert2, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}), 515: 516: Datadir = ?config(data_dir, Config), 517: [DsaKey = {'DSAPrivateKey', _, _}] = 518: erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")), 519: DSAPrivateKey = public_key:pem_entry_decode(DsaKey), 520: #'DSAPrivateKey'{p=P1, q=Q1, g=G1, y=Y1, x=_X1} = DSAPrivateKey, 521: 522: Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")), 523: DSASign = public_key:sign(Msg, sha, DSAPrivateKey), 524: DSAPublicKey = Y1, 525: DSAParams = #'Dss-Parms'{p=P1, q=Q1, g=G1}, 526: true = public_key:verify(Msg, sha, DSASign, {DSAPublicKey, DSAParams}), 527: false = public_key:verify(<<1:8, Msg/binary>>, sha, DSASign, 528: {DSAPublicKey, DSAParams}), 529: false = public_key:verify(Msg, sha, <<1:8, DSASign/binary>>, 530: {DSAPublicKey, DSAParams}), 531: 532: Digest = crypto:hash(sha,Msg), 533: DigestSign = public_key:sign(Digest, none, DSAPrivateKey), 534: true = public_key:verify(Digest, none, DigestSign, {DSAPublicKey, DSAParams}), 535: <<_:8, RestDigest/binary>> = Digest, 536: false = public_key:verify(<<1:8, RestDigest/binary>>, none, DigestSign, 537: {DSAPublicKey, DSAParams}), 538: false = public_key:verify(Digest, none, <<1:8, DigestSign/binary>>, 539: {DSAPublicKey, DSAParams}). 540: 541: %%-------------------------------------------------------------------- 542: pkix() -> 543: [{doc, "Misc pkix tests not covered elsewhere"}]. 544: pkix(Config) when is_list(Config) -> 545: Datadir = ?config(data_dir, Config), 546: Certs0 = erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")), 547: Certs1 = erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")), 548: TestTransform = fun({'Certificate', CertDer, not_encrypted}) -> 549: PlainCert = public_key:pkix_decode_cert(CertDer, plain), 550: OtpCert = public_key:pkix_decode_cert(CertDer, otp), 551: CertDer = 552: public_key:pkix_encode('OTPCertificate', OtpCert, otp), 553: CertDer = 554: public_key:pkix_encode('Certificate', PlainCert, plain), 555: OTPTBS = OtpCert#'OTPCertificate'.tbsCertificate, 556: OTPSubj = OTPTBS#'OTPTBSCertificate'.subject, 557: DNEncoded = public_key:pkix_encode('Name', OTPSubj, otp), 558: PlainTBS = PlainCert#'Certificate'.tbsCertificate, 559: Subj2 = PlainTBS#'TBSCertificate'.subject, 560: DNEncoded = public_key:pkix_encode('Name', Subj2, plain), 561: 562: false = public_key:pkix_is_fixed_dh_cert(CertDer) 563: end, 564: [TestTransform(Cert) || Cert <- Certs0 ++ Certs1], 565: 566: true = public_key:pkix_is_self_signed(element(2,hd(Certs0))), 567: false = public_key:pkix_is_self_signed(element(2,hd(Certs1))), 568: 569: CaIds = [element(2, public_key:pkix_issuer_id(Cert, self)) || 570: {'Certificate', Cert, _} <- Certs0], 571: {ok, IssuerId = {_, _IssuerName}} = 572: public_key:pkix_issuer_id(element(2,hd(Certs1)), other), 573: 574: true = lists:member(IssuerId, CaIds), 575: 576: %% Should be normalized allready 577: TestStr = {rdnSequence, 578: [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"ERLANGCA"}}], 579: [{'AttributeTypeAndValue', {2,5,4,3},{printableString," erlang ca "}}]]}, 580: VerifyStr = {rdnSequence, 581: [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlangca"}}], 582: [{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlang ca"}}]]}, 583: VerifyStr = public_key:pkix_normalize_name(TestStr), 584: 585: ok. 586: 587: %%-------------------------------------------------------------------- 588: pkix_countryname() -> 589: [{doc, "Test workaround for certs that code x509countryname as utf8"}]. 590: pkix_countryname(Config) when is_list(Config) -> 591: Cert = incorrect_countryname_pkix_cert(), 592: OTPCert = public_key:pkix_decode_cert(Cert, otp), 593: TBSCert = OTPCert#'OTPCertificate'.tbsCertificate, 594: Issuer = TBSCert#'OTPTBSCertificate'.issuer, 595: Subj = TBSCert#'OTPTBSCertificate'.subject, 596: check_countryname(Issuer), 597: check_countryname(Subj). 598: 599: %%-------------------------------------------------------------------- 600: pkix_emailaddress() -> 601: [{doc, "Test workaround for certs that code emailAddress as utf8"}]. 602: pkix_emailaddress(Config) when is_list(Config) -> 603: Cert = incorrect_emailaddress_pkix_cert(), 604: OTPCert = public_key:pkix_decode_cert(Cert, otp), 605: TBSCert = OTPCert#'OTPCertificate'.tbsCertificate, 606: Issuer = TBSCert#'OTPTBSCertificate'.issuer, 607: Subj = TBSCert#'OTPTBSCertificate'.subject, 608: check_emailaddress(Issuer), 609: check_emailaddress(Subj). 610: 611: %%-------------------------------------------------------------------- 612: pkix_path_validation() -> 613: [{doc, "Test PKIX path validation"}]. 614: pkix_path_validation(Config) when is_list(Config) -> 615: CaK = {Trusted,_} = 616: erl_make_certs:make_cert([{key, dsa}, 617: {subject, [ 618: {name, "Public Key"}, 619: {?'id-at-name', {printableString, "public_key"}}, 620: {?'id-at-pseudonym', {printableString, "pubkey"}}, 621: {city, "Stockholm"}, 622: {country, "SE"}, 623: {org, "erlang"}, 624: {org_unit, "testing dep"} 625: ]} 626: ]), 627: ok = erl_make_certs:write_pem("./", "public_key_cacert", CaK), 628: 629: CertK1 = {Cert1, _} = erl_make_certs:make_cert([{issuer, CaK}]), 630: CertK2 = {Cert2,_} = erl_make_certs:make_cert([{issuer, CertK1}, 631: {digest, md5}, {extensions, false}]), 632: ok = erl_make_certs:write_pem("./", "public_key_cert", CertK2), 633: 634: {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1], []), 635: 636: {error, {bad_cert,invalid_issuer}} = 637: public_key:pkix_path_validation(Trusted, [Cert2], []), 638: 639: {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1, Cert2], []), 640: {error, issuer_not_found} = public_key:pkix_issuer_id(Cert2, other), 641: 642: CertK3 = {Cert3,_} = erl_make_certs:make_cert([{issuer, CertK1}, 643: {extensions, [{basic_constraints, false}]}]), 644: {Cert4,_} = erl_make_certs:make_cert([{issuer, CertK3}, {extensions, [{key_usage, undefined}]}]), 645: 646: {error, {bad_cert,missing_basic_constraint}} = 647: public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4], []), 648: 649: VerifyFunAndState0 = {fun(_,{bad_cert, missing_basic_constraint}, UserState) -> 650: {valid, UserState}; 651: (_,{bad_cert, _} = Reason, _) -> 652: {fail, Reason}; 653: (_,{extension, _}, UserState) -> 654: {unknown, UserState}; 655: (_, valid, UserState) -> 656: {valid, UserState}; 657: (_, valid_peer, UserState) -> 658: {valid, UserState} 659: end, []}, 660: {ok, _} = 661: public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4], 662: [{verify_fun, VerifyFunAndState0}]), 663: 664: {error, {bad_cert, unknown_ca}} = 665: public_key:pkix_path_validation(unknown_ca, [Cert1, Cert3, Cert4], []), 666: 667: VerifyFunAndState1 = 668: {fun(_,{bad_cert, unknown_ca}, UserState) -> 669: {valid, UserState}; 670: (_,{bad_cert, _} = Reason, _) -> 671: {fail, Reason}; 672: (_,{extension, _}, UserState) -> 673: {unknown, UserState}; 674: (_, valid, UserState) -> 675: {valid, UserState} 676: end, []}, 677: 678: {ok, _} = 679: public_key:pkix_path_validation(unknown_ca, [Cert1], [{verify_fun, 680: VerifyFunAndState1}]), 681: ok. 682: 683: %%-------------------------------------------------------------------- 684: pkix_iso_rsa_oid() -> 685: [{doc, "Test workaround for supporting certs that use ISO oids" 686: " 1.3.14.3.2.29 instead of PKIX/PKCS oid"}]. 687: pkix_iso_rsa_oid(Config) when is_list(Config) -> 688: Datadir = ?config(data_dir, Config), 689: {ok, PemCert} = file:read_file(filename:join(Datadir, "rsa_ISO.pem")), 690: [{_, Cert, _}] = public_key:pem_decode(PemCert), 691: OTPCert = public_key:pkix_decode_cert(Cert, otp), 692: SigAlg = OTPCert#'OTPCertificate'.signatureAlgorithm, 693: {_, rsa} = public_key:pkix_sign_types(SigAlg#'SignatureAlgorithm'.algorithm). 694: 695: %%-------------------------------------------------------------------- 696: pkix_iso_dsa_oid() -> 697: [{doc, "Test workaround for supporting certs that use ISO oids" 698: "1.3.14.3.2.27 instead of PKIX/PKCS oid"}]. 699: pkix_iso_dsa_oid(Config) when is_list(Config) -> 700: Datadir = ?config(data_dir, Config), 701: {ok, PemCert} = file:read_file(filename:join(Datadir, "dsa_ISO.pem")), 702: [{_, Cert, _}] = public_key:pem_decode(PemCert), 703: OTPCert = public_key:pkix_decode_cert(Cert, otp), 704: SigAlg = OTPCert#'OTPCertificate'.signatureAlgorithm, 705: {_, dsa} = public_key:pkix_sign_types(SigAlg#'SignatureAlgorithm'.algorithm). 706: 707: %%-------------------------------------------------------------------- 708: %% Internal functions ------------------------------------------------ 709: %%-------------------------------------------------------------------- 710: asn1_encode_decode({Asn1Type, Der, not_encrypted} = Entry) -> 711: Decoded = public_key:der_decode(Asn1Type, Der), 712: Decoded = public_key:pem_entry_decode(Entry), 713: Entry = public_key:pem_entry_encode(Asn1Type, Decoded), 714: ok. 715: 716: check_countryname({rdnSequence,DirName}) -> 717: do_check_countryname(DirName). 718: do_check_countryname([]) -> 719: ok; 720: do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName', 721: value = "US"}|_]) -> 722: ok; 723: do_check_countryname([#'AttributeTypeAndValue'{type = ?'id-at-countryName', 724: value = Value}|_]) -> 725: ct:fail({incorrect_country_name, Value}); 726: do_check_countryname([_| Rest]) -> 727: do_check_countryname(Rest). 728: 729: check_emailaddress({rdnSequence,DirName}) -> 730: do_check_emailaddress(DirName). 731: do_check_emailaddress([]) -> 732: ok; 733: do_check_emailaddress([#'AttributeTypeAndValue'{type = ?'id-emailAddress', 734: value = "invalid@email.com"}|_]) -> 735: ok; 736: do_check_emailaddress([#'AttributeTypeAndValue'{type = ?'id-emailAddress', 737: value = Value}|_]) -> 738: ct:fail({incorrect_email_address, Value}); 739: do_check_emailaddress([_| Rest]) -> 740: do_check_emailaddress(Rest). 741: 742: check_entry_type(#'DSAPrivateKey'{}, 'DSAPrivateKey') -> 743: true; 744: check_entry_type(#'RSAPrivateKey'{}, 'RSAPrivateKey') -> 745: true; 746: check_entry_type(#'RSAPublicKey'{}, 'RSAPublicKey') -> 747: true; 748: check_entry_type({_Int, #'Dss-Parms'{}}, 'DSAPublicKey') when is_integer(_Int) -> 749: true; 750: check_entry_type(#'DHParameter'{}, 'DHParameter') -> 751: true; 752: check_entry_type(#'Certificate'{}, 'Certificate') -> 753: true; 754: check_entry_type(_,_) -> 755: false. 756: 757: strip_ending_newlines(Bin) -> 758: string:strip(binary_to_list(Bin), right, 10). 759: 760: incorrect_countryname_pkix_cert() -> 761: <<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>. 762: 763: incorrect_emailaddress_pkix_cert() -> 764: <<48,130,3,74,48,130,2,50,2,9,0,133,49,203,25,198,156,252,230,48,13,6,9,42,134, 72,134,247,13,1,1,5,5,0,48,103,49,11,48,9,6,3,85,4,6,19,2,65,85,49,19,48,17, 6,3,85,4,8,12,10,83,111,109,101,45,83,116,97,116,101,49,33,48,31,6,3,85,4,10, 12,24,73,110,116,101,114,110,101,116,32,87,105,100,103,105,116,115,32,80,116, 121,32,76,116,100,49,32,48,30,6,9,42,134,72,134,247,13,1,9,1,12,17,105,110, 118,97,108,105,100,64,101,109,97,105,108,46,99,111,109,48,30,23,13,49,51,49, 49,48,55,50,48,53,54,49,56,90,23,13,49,52,49,49,48,55,50,48,53,54,49,56,90, 48,103,49,11,48,9,6,3,85,4,6,19,2,65,85,49,19,48,17,6,3,85,4,8,12,10,83,111, 109,101,45,83,116,97,116,101,49,33,48,31,6,3,85,4,10,12,24,73,110,116,101, 114,110,101,116,32,87,105,100,103,105,116,115,32,80,116,121,32,76,116,100,49, 32,48,30,6,9,42,134,72,134,247,13,1,9,1,12,17,105,110,118,97,108,105,100,64, 101,109,97,105,108,46,99,111,109,48,130,1,34,48,13,6,9,42,134,72,134,247,13, 1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,190,243,49,213,219,60,232,105, 1,127,126,9,130,15,60,190,78,100,148,235,246,223,21,91,238,200,251,84,55,212, 78,32,120,61,85,172,0,144,248,5,165,29,143,79,64,178,51,153,203,76,115,238, 192,49,173,37,121,203,89,62,157,13,181,166,30,112,154,40,202,140,104,211,157, 73,244,9,78,236,70,153,195,158,233,141,42,238,2,143,160,225,249,27,30,140, 151,176,43,211,87,114,164,108,69,47,39,195,123,185,179,219,28,218,122,53,83, 77,48,81,184,14,91,243,12,62,146,86,210,248,228,171,146,225,87,51,146,155, 116,112,238,212,36,111,58,41,67,27,6,61,61,3,84,150,126,214,121,57,38,12,87, 121,67,244,37,45,145,234,131,115,134,58,194,5,36,166,52,59,229,32,47,152,80, 237,190,58,182,248,98,7,165,198,211,5,31,231,152,116,31,108,71,218,64,188, 178,143,27,167,79,15,112,196,103,116,212,65,197,94,37,4,132,103,91,217,73, 223,207,185,7,153,221,240,232,31,44,102,108,82,83,56,242,210,214,74,71,246, 177,217,148,227,220,230,4,176,226,74,194,37,2,3,1,0,1,48,13,6,9,42,134,72, 134,247,13,1,1,5,5,0,3,130,1,1,0,89,247,141,154,173,123,123,203,143,85,28,79, 73,37,164,6,17,89,171,224,149,22,134,17,198,146,158,192,241,41,253,58,230, 133,71,189,43,66,123,88,15,242,119,227,249,99,137,61,200,54,161,0,177,167, 169,114,80,148,90,22,97,78,162,181,75,93,209,116,245,46,81,232,64,157,93,136, 52,57,229,113,197,218,113,93,42,161,213,104,205,137,30,144,183,58,10,98,47, 227,177,96,40,233,98,150,209,217,68,22,221,133,27,161,152,237,46,36,179,59, 172,97,134,194,205,101,137,71,192,57,153,20,114,27,173,233,166,45,56,0,61, 205,45,202,139,7,132,103,248,193,157,184,123,43,62,172,236,110,49,62,209,78, 249,83,219,133,1,213,143,73,174,16,113,143,189,41,84,60,128,222,30,177,104, 134,220,52,239,171,76,59,176,36,113,176,214,118,16,44,235,21,167,199,216,200, 76,219,142,248,13,70,145,205,216,230,226,148,97,223,216,179,68,209,222,63, 140,137,24,164,192,149,194,79,119,247,75,159,49,116,70,241,70,116,11,40,119, 176,157,36,160,102,140,255,34,248,25,231,136,59>>.