diff options
author | Jon Spillett <jon.spillett@oracle.com> | 2021-02-17 17:56:36 +1000 |
---|---|---|
committer | Pauli <pauli@openssl.org> | 2021-04-30 09:15:50 +1000 |
commit | b536880c45722777df5ebe62897a6efcef757945 (patch) | |
tree | 015ad29f74586e3407079864fa686ffcde658fad /test | |
parent | d77ba503a2cf1c83098baca345327761b991d191 (diff) | |
download | openssl-b536880c45722777df5ebe62897a6efcef757945.tar.gz |
Add library context and property query support into the PKCS12 API
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14434)
Diffstat (limited to 'test')
-rw-r--r-- | test/helpers/pkcs12.c | 129 | ||||
-rw-r--r-- | test/helpers/pkcs12.h | 14 | ||||
-rw-r--r-- | test/pkcs12_format_test.c | 304 | ||||
-rw-r--r-- | test/recipes/80-test_pkcs12.t | 15 |
4 files changed, 436 insertions, 26 deletions
diff --git a/test/helpers/pkcs12.c b/test/helpers/pkcs12.c index 92ef7c378c..4d3abe06c7 100644 --- a/test/helpers/pkcs12.c +++ b/test/helpers/pkcs12.c @@ -22,7 +22,12 @@ #include "pkcs12.h" /* from the same directory */ /* Set this to > 0 write test data to file */ -int write_files = 0; +static int write_files = 0; + +static int legacy = 0; + +static OSSL_LIB_CTX *test_ctx = NULL; +static const char *test_propq = NULL; /* ------------------------------------------------------------------------- * Local function declarations @@ -41,6 +46,31 @@ static int check_attrs(const STACK_OF(X509_ATTRIBUTE) *bag_attrs, const PKCS12_A /* -------------------------------------------------------------------------- + * Global settings + */ + +void PKCS12_helper_set_write_files(int enable) +{ + write_files = enable; +} + +void PKCS12_helper_set_legacy(int enable) +{ + legacy = enable; +} + +void PKCS12_helper_set_libctx(OSSL_LIB_CTX *libctx) +{ + test_ctx = libctx; +} + +void PKCS12_helper_set_propq(const char *propq) +{ + test_propq = propq; +} + + +/* -------------------------------------------------------------------------- * Test data load functions */ @@ -116,6 +146,7 @@ void end_pkcs12_with_mac(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) static void generate_p12(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) { PKCS12 *p12; + EVP_MD *md = NULL; if (!pb->success) return; @@ -125,7 +156,10 @@ static void generate_p12(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) pb->success = 0; return; } - p12 = PKCS12_add_safes(pb->safes, 0); + if (legacy) + p12 = PKCS12_add_safes(pb->safes, 0); + else + p12 = PKCS12_add_safes_ex(pb->safes, 0, test_ctx, test_propq); if (!TEST_ptr(p12)) { pb->success = 0; goto err; @@ -133,8 +167,13 @@ static void generate_p12(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) sk_PKCS7_pop_free(pb->safes, PKCS7_free); if (mac != NULL) { + if (legacy) + md = (EVP_MD *)EVP_get_digestbynid(mac->nid); + else + md = EVP_MD_fetch(test_ctx, OBJ_nid2sn(mac->nid), test_propq); + if (!TEST_true(PKCS12_set_mac(p12, mac->pass, strlen(mac->pass), - NULL, 0, mac->iter, EVP_get_digestbynid(mac->nid)))) { + NULL, 0, mac->iter, md))) { pb->success = 0; goto err; } @@ -145,6 +184,8 @@ static void generate_p12(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) if (write_files) write_p12(p12, pb->filename); err: + if (!legacy && md != NULL) + EVP_MD_free(md); PKCS12_free(p12); } @@ -169,7 +210,13 @@ static PKCS12 *from_bio_p12(BIO *bio, const PKCS12_ENC *mac) { PKCS12 *p12 = NULL; - p12 = d2i_PKCS12_bio(bio, NULL); + /* Supply a p12 with library context/propq to the d2i decoder*/ + if (!legacy) { + p12 = PKCS12_init_ex(NID_pkcs7_data, test_ctx, test_propq); + if (!TEST_ptr(p12)) + goto err; + } + p12 = d2i_PKCS12_bio(bio, &p12); BIO_free(bio); if (!TEST_ptr(p12)) goto err; @@ -245,10 +292,18 @@ void end_contentinfo(PKCS12_BUILDER *pb) void end_contentinfo_encrypted(PKCS12_BUILDER *pb, const PKCS12_ENC *enc) { if (pb->success) { - if (pb->bags - && !TEST_true(PKCS12_add_safe(&pb->safes, pb->bags, enc->nid, enc->iter, enc->pass))) { - pb->success = 0; - return; + if (pb->bags) { + if (legacy) { + if (!TEST_true(PKCS12_add_safe(&pb->safes, pb->bags, enc->nid, enc->iter, enc->pass))) { + pb->success = 0; + return; + } + } else { + if (!TEST_true(PKCS12_add_safe_ex(&pb->safes, pb->bags, enc->nid, enc->iter, enc->pass, test_ctx, test_propq))) { + pb->success = 0; + return; + } + } } } sk_PKCS12_SAFEBAG_pop_free(pb->bags, PKCS12_SAFEBAG_free); @@ -259,9 +314,13 @@ void end_contentinfo_encrypted(PKCS12_BUILDER *pb, const PKCS12_ENC *enc) static STACK_OF(PKCS12_SAFEBAG) *decode_contentinfo(STACK_OF(PKCS7) *safes, int idx, const PKCS12_ENC *enc) { STACK_OF(PKCS12_SAFEBAG) *bags = NULL; + int bagnid; PKCS7 *p7 = sk_PKCS7_value(safes, idx); - int bagnid = OBJ_obj2nid(p7->type); + if (!TEST_ptr(p7)) + goto err; + + bagnid = OBJ_obj2nid(p7->type); if (enc) { if (!TEST_int_eq(bagnid, NID_pkcs7_encrypted)) goto err; @@ -370,7 +429,11 @@ void add_keybag(PKCS12_BUILDER *pb, const unsigned char *bytes, int len, return; } - bag = PKCS12_add_key(&pb->bags, pkey, 0 /*keytype*/, enc->iter, enc->nid, enc->pass); + if (legacy) + bag = PKCS12_add_key(&pb->bags, pkey, 0 /*keytype*/, enc->iter, enc->nid, enc->pass); + else + bag = PKCS12_add_key_ex(&pb->bags, pkey, 0 /*keytype*/, enc->iter, enc->nid, enc->pass, + test_ctx, test_propq); if (!TEST_ptr(bag)) { pb->success = 0; goto err; @@ -550,7 +613,11 @@ void check_keybag(PKCS12_BUILDER *pb, const unsigned char *bytes, int len, break; case NID_pkcs8ShroudedKeyBag: - if (!TEST_ptr(p8 = PKCS12_decrypt_skey(bag, enc->pass, strlen(enc->pass)))) { + if (legacy) + p8 = PKCS12_decrypt_skey(bag, enc->pass, strlen(enc->pass)); + else + p8 = PKCS12_decrypt_skey_ex(bag, enc->pass, strlen(enc->pass), test_ctx, test_propq); + if (!TEST_ptr(p8)) { pb->success = 0; goto err; } @@ -601,7 +668,12 @@ void check_secretbag(PKCS12_BUILDER *pb, int secret_nid, const char *secret, con void start_check_pkcs12(PKCS12_BUILDER *pb) { - PKCS12 *p12 = from_bio_p12(pb->p12bio, NULL); + PKCS12 *p12; + + if (!pb->success) + return; + + p12 = from_bio_p12(pb->p12bio, NULL); if (!TEST_ptr(p12)) { pb->success = 0; return; @@ -616,7 +688,12 @@ void start_check_pkcs12(PKCS12_BUILDER *pb) void start_check_pkcs12_with_mac(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) { - PKCS12 *p12 = from_bio_p12(pb->p12bio, mac); + PKCS12 *p12; + + if (!pb->success) + return; + + p12 = from_bio_p12(pb->p12bio, mac); if (!TEST_ptr(p12)) { pb->success = 0; return; @@ -631,7 +708,12 @@ void start_check_pkcs12_with_mac(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) void start_check_pkcs12_file(PKCS12_BUILDER *pb) { - PKCS12 *p12 = read_p12(pb->filename, NULL); + PKCS12 *p12; + + if (!pb->success) + return; + + p12 = read_p12(pb->filename, NULL); if (!TEST_ptr(p12)) { pb->success = 0; return; @@ -646,7 +728,12 @@ void start_check_pkcs12_file(PKCS12_BUILDER *pb) void start_check_pkcs12_file_with_mac(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) { - PKCS12 *p12 = read_p12(pb->filename, mac); + PKCS12 *p12; + + if (!pb->success) + return; + + p12 = read_p12(pb->filename, mac); if (!TEST_ptr(p12)) { pb->success = 0; return; @@ -661,12 +748,18 @@ void start_check_pkcs12_file_with_mac(PKCS12_BUILDER *pb, const PKCS12_ENC *mac) void end_check_pkcs12(PKCS12_BUILDER *pb) { + if (!pb->success) + return; + sk_PKCS7_pop_free(pb->safes, PKCS7_free); } void start_check_contentinfo(PKCS12_BUILDER *pb) { + if (!pb->success) + return; + pb->bag_idx = 0; pb->bags = decode_contentinfo(pb->safes, pb->safe_idx++, NULL); if (!TEST_ptr(pb->bags)) { @@ -678,6 +771,9 @@ void start_check_contentinfo(PKCS12_BUILDER *pb) void start_check_contentinfo_encrypted(PKCS12_BUILDER *pb, const PKCS12_ENC *enc) { + if (!pb->success) + return; + pb->bag_idx = 0; pb->bags = decode_contentinfo(pb->safes, pb->safe_idx++, enc); if (!TEST_ptr(pb->bags)) { @@ -690,6 +786,9 @@ void start_check_contentinfo_encrypted(PKCS12_BUILDER *pb, const PKCS12_ENC *enc void end_check_contentinfo(PKCS12_BUILDER *pb) { + if (!pb->success) + return; + if (!TEST_int_eq(sk_PKCS12_SAFEBAG_num(pb->bags), pb->bag_idx)) pb->success = 0; sk_PKCS12_SAFEBAG_pop_free(pb->bags, PKCS12_SAFEBAG_free); diff --git a/test/helpers/pkcs12.h b/test/helpers/pkcs12.h index 01e1cf1595..7805875806 100644 --- a/test/helpers/pkcs12.h +++ b/test/helpers/pkcs12.h @@ -20,8 +20,6 @@ #include "../testutil.h" -/* Set this to > 0 write test data to file */ -extern int write_files; /* ------------------------------------------------------------------------- * PKCS#12 Test structures @@ -36,9 +34,9 @@ typedef struct pkcs12_attr { /* Holds encryption parameters */ typedef struct pkcs12_enc { - int nid; - char *pass; - int iter; + int nid; + const char *pass; + int iter; } PKCS12_ENC; /* Set of variables required for constructing the PKCS#12 structure */ @@ -57,6 +55,12 @@ typedef struct pkcs12_builder { * PKCS#12 Test function declarations */ +/* Global settings */ +void PKCS12_helper_set_write_files(int enable); +void PKCS12_helper_set_legacy(int enable); +void PKCS12_helper_set_libctx(OSSL_LIB_CTX *libctx); +void PKCS12_helper_set_propq(const char *propq); + /* Allocate and initialise a PKCS#12 builder object */ PKCS12_BUILDER *new_pkcs12_builder(const char *filename); diff --git a/test/pkcs12_format_test.c b/test/pkcs12_format_test.c index 8b1448f19c..45eb24eca3 100644 --- a/test/pkcs12_format_test.c +++ b/test/pkcs12_format_test.c @@ -21,6 +21,12 @@ #include "testutil.h" #include "helpers/pkcs12.h" +static int default_libctx = 1; + +static OSSL_LIB_CTX *testctx = NULL; +static OSSL_PROVIDER *nullprov = NULL; +static OSSL_PROVIDER *deflprov = NULL; +static OSSL_PROVIDER *lgcyprov = NULL; /* -------------------------------------------------------------------------- * PKCS12 component test data @@ -200,7 +206,11 @@ static const PKCS12_ATTR ATTRS2[] = { }; static const PKCS12_ENC enc_default = { +#ifndef OPENSSL_NO_DES NID_pbe_WithSHA1And3_Key_TripleDES_CBC, +#else + NID_aes_128_cbc, +#endif "Password1", 1000 }; @@ -211,6 +221,79 @@ static const PKCS12_ENC mac_default = { 1000 }; +static const int enc_nids_all[] = { + /* NOTE: To use PBES2 we pass the desired cipher NID instead of NID_pbes2 */ + NID_aes_128_cbc, + NID_aes_256_cbc, +#ifndef OPENSSL_NO_DES + NID_des_ede3_cbc, + NID_des_cbc, +#endif + +#ifndef OPENSSL_NO_MD2 +# ifndef OPENSSL_NO_DES + NID_pbeWithMD2AndDES_CBC, +# endif +# ifndef OPENSSL_NO_RC2 + NID_pbeWithMD2AndRC2_CBC, +# endif +#endif + +#ifndef OPENSSL_NO_MD5 +# ifndef OPENSSL_NO_DES + NID_pbeWithMD5AndDES_CBC, +# endif +# ifndef OPENSSL_NO_RC2 + NID_pbeWithMD5AndRC2_CBC, +# endif +#endif +#ifndef OPENSSL_NO_DES + NID_pbeWithSHA1AndDES_CBC, +#endif +#ifndef OPENSSL_NO_RC2 + NID_pbe_WithSHA1And128BitRC2_CBC, + NID_pbe_WithSHA1And40BitRC2_CBC, + NID_pbeWithSHA1AndRC2_CBC, +#endif +#ifndef OPENSSL_NO_RC4 + NID_pbe_WithSHA1And128BitRC4, + NID_pbe_WithSHA1And40BitRC4, +#endif +#ifndef OPENSSL_NO_DES + NID_pbe_WithSHA1And2_Key_TripleDES_CBC, + NID_pbe_WithSHA1And3_Key_TripleDES_CBC, +#endif +}; + +static const int enc_nids_no_legacy[] = { + /* NOTE: To use PBES2 we pass the desired cipher NID instead of NID_pbes2 */ + NID_aes_128_cbc, + NID_aes_256_cbc, +#ifndef OPENSSL_NO_DES + NID_des_ede3_cbc, + NID_pbe_WithSHA1And2_Key_TripleDES_CBC, + NID_pbe_WithSHA1And3_Key_TripleDES_CBC, +#endif +}; + +static const int mac_nids[] = { + NID_sha1, + NID_md5, + NID_sha256, + NID_sha512, + NID_sha3_256, + NID_sha3_512 +}; + +static const int iters[] = { + 1, + 1000 +}; + +static const char *passwords[] = { + "Password1", + "", +}; /* -------------------------------------------------------------------------- * Local functions @@ -261,9 +344,79 @@ static int test_single_cert_no_attrs(void) return end_pkcs12_builder(pb); } +static int test_single_key(PKCS12_ENC *enc) +{ + char fname[80]; + PKCS12_BUILDER *pb; + + sprintf(fname, "1key_ciph-%s_iter-%d.p12", OBJ_nid2sn(enc->nid), enc->iter); + + pb = new_pkcs12_builder(fname); + + /* Generate/encode */ + start_pkcs12(pb); + + start_contentinfo(pb); + + add_keybag(pb, KEY1, sizeof(KEY1), NULL, enc); + + end_contentinfo(pb); + + end_pkcs12(pb); + + /* Read/decode */ + start_check_pkcs12(pb); + + start_check_contentinfo(pb); + + check_keybag(pb, KEY1, sizeof(KEY1), NULL, enc); + + end_check_contentinfo(pb); + + end_check_pkcs12(pb); + + return end_pkcs12_builder(pb); +} + +static int test_single_key_enc_alg(int z) +{ + PKCS12_ENC enc; + + if (lgcyprov == NULL) + enc.nid = enc_nids_no_legacy[z]; + else + enc.nid = enc_nids_all[z]; + enc.pass = enc_default.pass; + enc.iter = enc_default.iter; + + return test_single_key(&enc); +} + +static int test_single_key_enc_pass(int z) +{ + PKCS12_ENC enc; + + enc.nid = enc_default.nid; + enc.pass = passwords[z]; + enc.iter = enc_default.iter; + + return test_single_key(&enc); +} + +static int test_single_key_enc_iter(int z) +{ + PKCS12_ENC enc; + + enc.nid = enc_default.nid; + enc.pass = enc_default.pass; + enc.iter = iters[z]; + + return test_single_key(&enc); +} + static int test_single_key_with_attrs(void) { - PKCS12_BUILDER *pb = new_pkcs12_builder("1key.p12"); + PKCS12_BUILDER *pb = new_pkcs12_builder("1keyattrs.p12"); /* Generate/encode */ start_pkcs12(pb); @@ -290,6 +443,73 @@ static int test_single_key_with_attrs(void) return end_pkcs12_builder(pb); } +static int test_single_cert_mac(PKCS12_ENC *mac) +{ + char fname[80]; + PKCS12_BUILDER *pb; + + sprintf(fname, "1cert_mac-%s_iter-%d.p12", OBJ_nid2sn(mac->nid), mac->iter); + + pb = new_pkcs12_builder(fname); + + /* Generate/encode */ + start_pkcs12(pb); + + start_contentinfo(pb); + + add_certbag(pb, CERT1, sizeof(CERT1), NULL); + + end_contentinfo(pb); + + end_pkcs12_with_mac(pb, mac); + + /* Read/decode */ + start_check_pkcs12_with_mac(pb, mac); + + start_check_contentinfo(pb); + + check_certbag(pb, CERT1, sizeof(CERT1), NULL); + + end_check_contentinfo(pb); + + end_check_pkcs12(pb); + + return end_pkcs12_builder(pb); +} + +static int test_single_cert_mac_alg(int z) +{ + PKCS12_ENC mac; + + mac.nid = mac_nids[z]; + mac.pass = mac_default.pass; + mac.iter = mac_default.iter; + + return test_single_cert_mac(&mac); +} + +static int test_single_cert_mac_pass(int z) +{ + PKCS12_ENC mac; + + mac.nid = mac_default.nid; + mac.pass = passwords[z]; + mac.iter = mac_default.iter; + + return test_single_cert_mac(&mac); +} + +static int test_single_cert_mac_iter(int z) +{ + PKCS12_ENC mac; + + mac.nid = mac_default.nid; + mac.pass = mac_default.pass; + mac.iter = iters[z]; + + return test_single_cert_mac(&mac); +} + static int test_cert_key_with_attrs_and_mac(void) { PKCS12_BUILDER *pb = new_pkcs12_builder("1cert1key.p12"); @@ -430,11 +650,86 @@ static int test_multiple_contents(void) return end_pkcs12_builder(pb); } +typedef enum OPTION_choice { + OPT_ERR = -1, + OPT_EOF = 0, + OPT_WRITE, + OPT_LEGACY, + OPT_CONTEXT, + OPT_TEST_ENUM +} OPTION_CHOICE; + +const OPTIONS *test_get_options(void) +{ + static const OPTIONS options[] = { + OPT_TEST_OPTIONS_DEFAULT_USAGE, + { "write", OPT_WRITE, '-', "Write PKCS12 objects to file" }, + { "legacy", OPT_LEGACY, '-', "Test the legacy APIs" }, + { "context", OPT_CONTEXT, '-', "Explicitly use a non-default library context" }, + { NULL } + }; + return options; +} int setup_tests(void) { + OPTION_CHOICE o; + + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_WRITE: + PKCS12_helper_set_write_files(1); + break; + case OPT_LEGACY: + PKCS12_helper_set_legacy(1); + break; + case OPT_CONTEXT: + default_libctx = 0; + break; + case OPT_TEST_CASES: + break; + default: + return 0; + } + } + + if (!default_libctx) { + testctx = OSSL_LIB_CTX_new(); + if (!TEST_ptr(testctx)) + return 0; + nullprov = OSSL_PROVIDER_load(NULL, "null"); + if (!TEST_ptr(nullprov)) + return 0; + } + + deflprov = OSSL_PROVIDER_load(testctx, "default"); + if (!TEST_ptr(deflprov)) + return 0; + lgcyprov = OSSL_PROVIDER_load(testctx, "legacy"); + + PKCS12_helper_set_libctx(testctx); + + /* + * Verify that the default and fips providers in the default libctx are not + * available if we are using a standalone context + */ + if (!default_libctx) { + if (!TEST_false(OSSL_PROVIDER_available(NULL, "default")) + || !TEST_false(OSSL_PROVIDER_available(NULL, "fips"))) + return 0; + } + ADD_TEST(test_single_cert_no_attrs); + if (lgcyprov == NULL) + ADD_ALL_TESTS(test_single_key_enc_alg, OSSL_NELEM(enc_nids_no_legacy)); + else + ADD_ALL_TESTS(test_single_key_enc_alg, OSSL_NELEM(enc_nids_all)); + ADD_ALL_TESTS(test_single_key_enc_pass, OSSL_NELEM(passwords)); + ADD_ALL_TESTS(test_single_key_enc_iter, OSSL_NELEM(iters)); ADD_TEST(test_single_key_with_attrs); + ADD_ALL_TESTS(test_single_cert_mac_alg, OSSL_NELEM(mac_nids)); + ADD_ALL_TESTS(test_single_cert_mac_pass, OSSL_NELEM(passwords)); + ADD_ALL_TESTS(test_single_cert_mac_iter, OSSL_NELEM(iters)); ADD_TEST(test_cert_key_with_attrs_and_mac); ADD_TEST(test_cert_key_encrypted_content); ADD_TEST(test_single_secret_encrypted_content); @@ -442,3 +737,10 @@ int setup_tests(void) return 1; } +void cleanup_tests(void) +{ + OSSL_PROVIDER_unload(nullprov); + OSSL_PROVIDER_unload(deflprov); + OSSL_PROVIDER_unload(lgcyprov); + OSSL_LIB_CTX_free(testctx); +} diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t index 49eb2b5225..b259c1a335 100644 --- a/test/recipes/80-test_pkcs12.t +++ b/test/recipes/80-test_pkcs12.t @@ -16,9 +16,6 @@ use Encode; setup("test_pkcs12"); -plan skip_all => "The PKCS12 command line utility is not supported by this OpenSSL build" - if disabled("des"); - my $pass = "σύνθημα γνώρισμα"; my $savedcp; @@ -57,10 +54,14 @@ if (eval { require Win32::API; 1; }) { } $ENV{OPENSSL_WIN32_UTF8}=1; -plan tests => 5; +plan tests => 7; # Test different PKCS#12 formats ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats"); +# Test with legacy APIs +ok(run(test(["pkcs12_format_test", "-legacy"])), "test pkcs12 formats using legacy APIs"); +# Test with a non-default library context (and no loaded providers in the default context) +ok(run(test(["pkcs12_format_test", "-context"])), "test pkcs12 formats using a non-default library context"); # just see that we can read shibboleth.pfx protected with $pass ok(run(app(["openssl", "pkcs12", "-noout", @@ -82,13 +83,17 @@ ok(run(app(["openssl", "pkcs12", "-export", "-chain", "test_pkcs12_chain_untrusted"); # Test the -passcerts option -ok(run(app(["openssl", "pkcs12", "-export", +SKIP: { + skip "Skipping PKCS#12 test because DES is disabled in this build", 1 + if disabled("des"); + ok(run(app(["openssl", "pkcs12", "-export", "-in", srctop_file(@path, "ee-cert.pem"), "-certfile", srctop_file(@path, "v3-certs-TDES.p12"), "-passcerts", "pass:v3-certs", "-nokeys", "-passout", "pass:v3-certs", "-descert", "-out", $outfile2])), "test_pkcs12_passcerts"); +} SKIP: { skip "Skipping legacy PKCS#12 test because RC2 is disabled in this build", 1 |