From david at atsec.com Mon Oct 14 11:27:35 2024 From: david at atsec.com (David Sugar) Date: Mon, 14 Oct 2024 11:27:35 +0200 Subject: FIPS 140 service indicator revamp Message-ID: Hi, Libgcrypt implements a FIPS 140 service indicator to comply with the rules stipulated by FIPS 140-3. However, based on recent clarifications provided by NIST around the topic, the current implementation falls short of meeting these clarifications. The current service indicator "statically" returns the indicator whether an algorithm operates in a FIPS-compliant manner or not. This includes the indicator whether the cryptographic algorithm is an "approved" algorithm or not. The issue with this approach is that it is "static" in the sense that a caller asks libgcrypt whether algorithm X is approved or not. NIST clarified that such static behavior is not sufficient. NIST requests a "dynamic" indicator in the sense that during the processing of the actual request to perform a cryptographic operation, the indicator must be "generated" and "returned" to the caller. I.e. when the caller performs, say an RSA 1024 operation, that very API call is required to generate the indicator that the request was not FIPS compliant. Conversely, when the caller performs an RSA 2048 operation, that very API call must generate the indicator that it was an approved mechanism. The NIST requirement does not pre-scribe how exactly that indicator is to be implemented. This implies we have quite some freedom to implement it. This email offers a proposal to solve the service indicator non-compliance. The provided patch adjusts one cryptographic service to show the chosen approach. If we all agree, we will proceed in developing the patch series to cover the other services appropriately. The issue we have to solve is the following: the APIs defined by libcrypt return a status value with an integer field. That field is defined to be 0 for success and != 0 for all errors. With the presence of a FIPS service indicator, we must return a second return status which may not be equal to zero. Yet, we want consumers to not view the result of the FIPS service indicator as an error to ensure even in FIPS mode, non-approved services can be invoked without changing the caller. Thus, it is not possible to return the FIPS service indicator as part of the standard return status (e.g. by using the high bits in the integer field). Our solution we offer here is the use of the errno to convey the FIPS status indicator. As the errno is also used to communicate a real issue, we define the errno as a FIPS status indicator only if the API return code is zero, i.e. there is a successful API operation. I.e.: - if the API return code is 0 -> errno contains the FIPS service indicator - if the API return code is != 0 -> errno contains the "regular" error indicator (i.e. the patch will not touch the errno) To achieve that and not trample the errno during the libgcrypt operation, the patch suggests: 1. adding a new libgcrypt error code - this error code is only used internally 2. adding an instrumentation to each API call to mark the service as approved or non-approved at the very end of the operation if the operation was successful. In case of a non-approved service, the new libgcrypt error code is used. The placement at the end of the API call implies that (a) the API call performs all operations and (b) returns the service indicator. 3. in the interface layer of the visibility.c, the FIPS non-approved "error" is converted into an API return code of 0 and an errno of -EOPNOTSUPP. Otherwise the errno is set to 0. This implies that: 1. a caller even in FIPS mode will always have its API call succeeding (even if it is non-approved) 2. a caller can check the FIPS indicator if the API return code is 0 by checking the errno to contain -EOPNOTSUPP. If it contains this error code, the caller knows the serivce was non-approved. If it contains 0, the caller knows the service was approved. Question: The patch currently implements the logic that in case the new libgcrypt-internal error observed, the errno is set. This implies that for all non-approved services an appropriate instrumentation is required. Would it make sense to reverse the logic, i.e. return the libgcrypt-internal error if a FIPS-approved serivce is triggered? The visibility-layer would then set the errno to -EOPNOTSUPP if the libgcrypt-internal error is*not* seen. With this approach, we can have a white-list where we only need to instrument the API calls that we know are FIPS-approved. This would limit the amount of changes and with a white-list we many not forget non-approved serivces. Best regards David -- atsec information security GmbH, Ismaninger Str. 19, 81675 M?nchen, Germany Phone: +49-89-44249840 / Web: atsec.com HRB: 129439 (Amtsgericht M?nchen) Geschaeftsfuehrer: Staffan Persson, Dr. Michael Vogel -------------- next part -------------- --- libgcrypt/cipher/kdf.c 2024-10-10 11:13:04.000000000 +0200 +++ libgcrypt-fips2/cipher/kdf.c 2024-10-14 11:02:54.146310526 +0200 @@ -239,7 +239,11 @@ but certain KDF algorithm may use it differently. {SALT,SALTLEN} is a salt as needed by most KDF algorithms. ITERATIONS is a positive integer parameter to most KDFs. 0 is returned on success, - or an error code on failure. */ + or an error code on failure. + + FIPS: if fips_mode() is enabled, the function will return + GPG_ERR_FORBIDDEN if the function call violates one + or more fips requirements. */ gpg_err_code_t _gcry_kdf_derive (const void *passphrase, size_t passphraselen, int algo, int subalgo, @@ -285,19 +289,19 @@ { /* FIPS requires minimum passphrase length, see FIPS 140-3 IG D.N */ if (fips_mode () && passphraselen < 8) - return GPG_ERR_INV_VALUE; + return fips_not_compliant(); /* FIPS requires minimum salt length of 128 b (SP 800-132 sec. 5.1, p.6) */ if (fips_mode () && saltlen < 16) - return GPG_ERR_INV_VALUE; + return fips_not_compliant(); /* FIPS requires minimum iterations bound (SP 800-132 sec 5.2, p.6) */ if (fips_mode () && iterations < 1000) - return GPG_ERR_INV_VALUE; + return fips_not_compliant(); /* Check minimum key size */ if (fips_mode () && keysize < 14) - return GPG_ERR_INV_VALUE; + return fips_not_compliant(); ec = _gcry_kdf_pkdf2 (passphrase, passphraselen, subalgo, salt, saltlen, iterations, keysize, keybuffer); @@ -318,6 +322,10 @@ ec = GPG_ERR_UNKNOWN_ALGORITHM; break; } + + /* On success we verify that the kdf is allowed in fips_mode */ + if (ec == 0 && fips_mode () && !_gcry_fips_check_kdf_compliant(algo, subalgo)) + ec = fips_not_compliant(); leave: return ec; --- libgcrypt/src/fips.c 2024-10-10 11:13:04.000000000 +0200 +++ libgcrypt-fips2/src/fips.c 2024-10-14 10:58:54.243408712 +0200 @@ -1172,3 +1172,28 @@ abort (); /*NOTREACHED*/ } + +enum gcry_fips_kdf_valid_alg_idx + { + GCRY_FIPS_KDF_PBKDF2_IDX = 0, + /* This has to be the last element */ + GCRY_FIPS_KDF_TOTAL + }; + +int gcry_fips_kdf_valid_alg[GCRY_FIPS_KDF_TOTAL] = { + GCRY_KDF_PBKDF2, +}; + +/* This function should be called to ensure that the used kdf + is fips compliant. */ +int +_gcry_fips_check_kdf_compliant(int algo, int subalgo /* TODO: dead param */) +{ + for (int i = 0; i < GCRY_FIPS_KDF_TOTAL; i++) + { + if (gcry_fips_kdf_valid_alg[i] == algo) + return 1; + } + return 0; +} + --- libgcrypt/src/g10lib.h 2024-10-10 11:13:04.000000000 +0200 +++ libgcrypt-fips2/src/g10lib.h 2024-10-14 09:55:52.409417815 +0200 @@ -484,6 +484,9 @@ (!fips_mode () || _gcry_global_is_operational ())) #define fips_not_operational() (GPG_ERR_NOT_OPERATIONAL) +#define fips_not_compliant() (GPG_ERR_FORBIDDEN) + +int _gcry_fips_check_kdf_compliant(int algo, int subalgo); int _gcry_fips_test_operational (void); int _gcry_fips_test_error_or_operational (void); --- libgcrypt/src/visibility.c 2024-10-10 11:13:04.000000000 +0200 +++ libgcrypt-fips2/src/visibility.c 2024-10-14 11:01:26.000023647 +0200 @@ -20,6 +20,7 @@ #include #include +#include #define _GCRY_INCLUDED_BY_VISIBILITY_C #include "g10lib.h" @@ -1398,11 +1399,29 @@ unsigned long iterations, size_t keysize, void *keybuffer) { + gpg_err_code_t status; + if (!fips_is_operational ()) return gpg_error (fips_not_operational ()); - return gpg_error (_gcry_kdf_derive (passphrase, passphraselen, algo, hashalgo, - salt, saltlen, iterations, - keysize, keybuffer)); + status = _gcry_kdf_derive (passphrase, + passphraselen, algo, hashalgo, + salt, saltlen, iterations, + keysize, keybuffer); + /* + * A FIPS error is indicated by the errno EOPNOTSUPP, i.e., + * to check for success in fips_mode the caller must immediately + * check `errno == EOPNOTSUPP` and act upon it, even if the returned + * status code is success. + */ + if (status == fips_not_compliant()) + { + errno = EOPNOTSUPP; + status = 0; + } + else + errno = 0; + + return gpg_error (status); } gpg_error_t -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 840 bytes Desc: OpenPGP digital signature URL: From gniibe at fsij.org Thu Oct 24 03:34:31 2024 From: gniibe at fsij.org (NIIBE Yutaka) Date: Thu, 24 Oct 2024 10:34:31 +0900 Subject: FIPS 140 service indicator revamp In-Reply-To: References: Message-ID: <87sesmffmg.fsf@akagi.fsij.org> Hello, Thank you for your proposal with a patch. David Sugar wrote: > Libgcrypt implements a FIPS 140 service indicator to comply with the rules > stipulated by FIPS 140-3. However, based on recent clarifications provided by > NIST around the topic, the current implementation falls short of meeting these > clarifications. > > The current service indicator "statically" returns the indicator whether an > algorithm operates in a FIPS-compliant manner or not. This includes the > indicator whether the cryptographic algorithm is an "approved" algorithm or > not. > > The issue with this approach is that it is "static" in the sense that a caller > asks libgcrypt whether algorithm X is approved or not. NIST clarified that > such static behavior is not sufficient. NIST requests a "dynamic" indicator in > the sense that during the processing of the actual request to perform a > cryptographic operation, the indicator must be "generated" and "returned" to > the caller. I.e. when the caller performs, say an RSA 1024 operation, that > very API call is required to generate the indicator that the request was not > FIPS compliant. Conversely, when the caller performs an RSA 2048 operation, > that very API call must generate the indicator that it was an approved > mechanism. I see the situation. Let us improve. (Well, I don't have a skill for the better wording/expression in English. I use "us", meaning: gpg-team + you + all, including its users.) > Our solution we offer here is the use of the errno to convey the FIPS status > indicator. Could we ask you to reserve this decision, at this point? I can imagine the need for a thread local variable for the service indicator, but I think that overloading ERRNO might be difficult to handle and/or confusing for applications (library users). For this, firstly, I propose the API of following: void _gcry_thread_context_set_fsi (unsigned long fsi); unsigned long _gcry_thread_context_get_fsi (void); unsigned long gcry_thread_context_get_fsi (void); (Function name change/suggestion is welcome, here, fsi stands for FIPS service indicator.) Two functions are internal use for libgcrypt. The last function is exposed to libgcrypt users. Implementation may be the one with ERRNO actually later, but for now, let us hide and defer the implementation detail. It's true that libgcrypt code-base doesn't yet have thread specific context things; That's long-standing issue to be done. Since libgcrypt assumes C90+something (only) for build environment and tries to support various OS and compilers, it would be tricky to implement. For this particular problem, I created a separate ticket (and a branch gniibe/t7340): https://dev.gnupg.org/T7340 # So far, I found that use of pre-C99 non-standard __thread specifier # seems work well. Any feedback is welcome for this. > The provided patch adjusts one cryptographic service to show the chosen > approach. If we all agree, we will proceed in developing the patch series to > cover the other services appropriately. Thanks a lot for this constructive approach. > - if the API return code is 0 -> errno contains the FIPS service indicator > > - if the API return code is != 0 -> errno contains the "regular" error > indicator (i.e. the patch will not touch the errno) Currently, I don't know if this is good. My concern here is that this will be a fundamental API change for libgcrypt users; I'm afraid it requires changes for all/most applications. In my opinion, two points are important. * A function may be finished earlier, not completing the operation. In this case, the API return code should not be 0 (keeping the API). * Introducing use of gcry_thread_context_get_fsi (after a function call) makes sense for FIPS certified applications to make sure if the function call of libgcrypt does not violate FIPS (new API). * * * Well, may I ask for your patch also including a change of tests/t-kdf.c? By doing so, it will be more clear to see how the change of libgcrypt asks application changes. -- From wk at gnupg.org Thu Oct 24 10:58:07 2024 From: wk at gnupg.org (Werner Koch) Date: Thu, 24 Oct 2024 10:58:07 +0200 Subject: FIPS 140 service indicator revamp In-Reply-To: <87sesmffmg.fsf@akagi.fsij.org> (NIIBE Yutaka via Gcrypt-devel's message of "Thu, 24 Oct 2024 10:34:31 +0900") References: <87sesmffmg.fsf@akagi.fsij.org> Message-ID: <87jzdxzxls.fsf@jacob.g10code.de> On Thu, 24 Oct 2024 10:34, NIIBE Yutaka said: > For this, firstly, I propose the API of following: > > void _gcry_thread_context_set_fsi (unsigned long fsi); > unsigned long _gcry_thread_context_get_fsi (void); > unsigned long gcry_thread_context_get_fsi (void); I think we need to consider what to do with the older and FIPS approved 1.10 version. Adding a new function call extends the ABI but application need to explicit test for the existance of the new function. They can't just check the version number of Libgcrypt and conclude that the new function exists. We could step this aside by using gcry_control along with a macro to get the FIPS indictor. gcry_control would return an error for an unknown control code and the caller could test for this. Or we use symbol versioning tricks. Shalom-Salam, Werner -- The pioneers of a warless world are the youth that refuse military service. - A. Einstein -------------- next part -------------- A non-text attachment was scrubbed... Name: openpgp-digital-signature.asc Type: application/pgp-signature Size: 247 bytes Desc: not available URL: From david at atsec.com Thu Oct 24 14:21:26 2024 From: david at atsec.com (David Sugar) Date: Thu, 24 Oct 2024 14:21:26 +0200 Subject: FIPS 140 service indicator revamp In-Reply-To: <6497406.YeSz9MTgJO@tauon.atsec.com> References: <87sesmffmg.fsf@akagi.fsij.org> <6497406.YeSz9MTgJO@tauon.atsec.com> Message-ID: <002642d8-4401-4fdb-975e-a5350293db95@atsec.com> Hi NIIBE, thank you for your proposal and your opinions on the matter. I mostly share the same opinions as Stephan. > Could we ask you to reserve this decision, at this point? I can imagine > the need for a thread local variable for the service indicator, but I > think that overloading ERRNO might be difficult to handle and/or > confusing for applications (library users). I wouldn't say that it is difficult to handle but its definitely a little bit confusing as one would normally expect an errno to be set in conjunction with a error code and not success, i.e., by following this path one has to properly document the behavior. I also wouldn't say that errno is overloaded because at the point errno is set for FIPS, other errno values related to errors should have already been handled internally. > Well, may I ask for your patch also including a change of tests/t-kdf.c? > By doing so, it will be more clear to see how the change of libgcrypt > asks application changes. Sure, I've attached a diff. Best regards David On 24.10.24 11:08, Stephan Mueller wrote: > Am Donnerstag, 24. Oktober 2024, 03:34:31 Mitteleurop?ische Sommerzeit schrieb > NIIBE Yutaka via Gcrypt-devel: > > Hi NIIBE, > >> Hello, >> >> Thank you for your proposal with a patch. >> >> David Sugar wrote: >>> Libgcrypt implements a FIPS 140 service indicator to comply with the rules >>> stipulated by FIPS 140-3. However, based on recent clarifications provided >>> by NIST around the topic, the current implementation falls short of >>> meeting these clarifications. >>> >>> The current service indicator "statically" returns the indicator whether >>> an >>> algorithm operates in a FIPS-compliant manner or not. This includes the >>> indicator whether the cryptographic algorithm is an "approved" algorithm >>> or >>> not. >>> >>> The issue with this approach is that it is "static" in the sense that a >>> caller asks libgcrypt whether algorithm X is approved or not. NIST >>> clarified that such static behavior is not sufficient. NIST requests a >>> "dynamic" indicator in the sense that during the processing of the actual >>> request to perform a cryptographic operation, the indicator must be >>> "generated" and "returned" to the caller. I.e. when the caller performs, >>> say an RSA 1024 operation, that very API call is required to generate the >>> indicator that the request was not FIPS compliant. Conversely, when the >>> caller performs an RSA 2048 operation, that very API call must generate >>> the indicator that it was an approved mechanism. >> I see the situation. >> >> Let us improve. (Well, I don't have a skill for the better >> wording/expression in English. I use "us", meaning: gpg-team + you + >> all, including its users.) >> >>> Our solution we offer here is the use of the errno to convey the FIPS >>> status indicator. >> Could we ask you to reserve this decision, at this point? I can imagine >> the need for a thread local variable for the service indicator, but I >> think that overloading ERRNO might be difficult to handle and/or >> confusing for applications (library users). >> >> For this, firstly, I propose the API of following: >> >> void _gcry_thread_context_set_fsi (unsigned long fsi); >> unsigned long _gcry_thread_context_get_fsi (void); >> unsigned long gcry_thread_context_get_fsi (void); >> >> (Function name change/suggestion is welcome, here, fsi stands for FIPS >> service indicator.) >> >> Two functions are internal use for libgcrypt. The last function is >> exposed to libgcrypt users. >> >> Implementation may be the one with ERRNO actually later, but for now, >> let us hide and defer the implementation detail. >> >> It's true that libgcrypt code-base doesn't yet have thread specific >> context things; That's long-standing issue to be done. Since libgcrypt >> assumes C90+something (only) for build environment and tries to support >> various OS and compilers, it would be tricky to implement. For this >> particular problem, I created a separate ticket (and a branch >> gniibe/t7340): >> >> https://dev.gnupg.org/T7340 >> >> # So far, I found that use of pre-C99 non-standard __thread specifier >> # seems work well. Any feedback is welcome for this. > Thanks very much for sharing this suggestion. I see no issue with it from an > API / FIPS point of view. > > But since I am also maintaining a crypto lib (leancrypto), I am intrigued to > learn how such thread-local variables can be maintained. I think I have to > learn about the __thread specifier :-) >>> The provided patch adjusts one cryptographic service to show the chosen >>> approach. If we all agree, we will proceed in developing the patch series >>> to cover the other services appropriately. >> Thanks a lot for this constructive approach. >> >>> - if the API return code is 0 -> errno contains the FIPS service indicator >>> >>> - if the API return code is != 0 -> errno contains the "regular" error >>> indicator (i.e. the patch will not touch the errno) >> Currently, I don't know if this is good. My concern here is that this >> will be a fundamental API change for libgcrypt users; I'm afraid it >> requires changes for all/most applications. > Ok, but I do not see it this way, because the API (disregarding the errno in > the successful conclusion of the API) is unchanged. Only if the API concludes > successfully *and* a caller is interested in FIPS, it may want to consult th > errno. In a success case, usually no caller would consult the errno. >> In my opinion, two points are important. >> >> * A function may be finished earlier, not completing the operation. >> In this case, the API return code should not be 0 (keeping the API). > That case should not be affected by the suggestion, because the API returns > with a GPG error indicator. >> * Introducing use of gcry_thread_context_get_fsi (after a function call) >> makes sense for FIPS certified applications to make sure if the >> function call of libgcrypt does not violate FIPS (new API). > Ok sure, as said above, no issue from our side. >> * * * >> >> Well, may I ask for your patch also including a change of tests/t-kdf.c? >> By doing so, it will be more clear to see how the change of libgcrypt >> asks application changes. > I would like to ask my colleague, David, for that. > > > Ciao > Stephan > > -- atsec information security GmbH, Ismaninger Str. 19, 81675 M?nchen, Germany Phone: +49-89-44249840 / Web: atsec.com HRB: 129439 (Amtsgericht M?nchen) Geschaeftsfuehrer: Staffan Persson, Dr. Michael Vogel -------------- next part -------------- --- libgcrypt/tests/t-kdf.c 2024-10-10 11:13:04.000000000 +0200 +++ libgcrypt-fips2/tests/t-kdf.c 2024-10-14 13:11:57.305374486 +0200 @@ -26,6 +26,7 @@ #include #include #include +#include #include "stopwatch.h" #define PGM "t-kdf" @@ -1102,6 +1103,7 @@ gpg_error_t err; unsigned char outbuf[100]; int i; + int my_errno; for (tvidx=0; tvidx < DIM(tv); tvidx++) { @@ -1117,14 +1119,15 @@ GCRY_KDF_PBKDF2, tv[tvidx].hashalgo, tv[tvidx].salt, tv[tvidx].saltlen, tv[tvidx].c, tv[tvidx].dklen, outbuf); + my_errno = errno; if (in_fips_mode && tvidx > 7) { - if (!err) + if (my_errno != EOPNOTSUPP) fail ("pbkdf2 test %d unexpectedly passed in FIPS mode: %s\n", tvidx, gpg_strerror (err)); continue; } - if (err) + if (err || my_errno == EOPNOTSUPP) { if (in_fips_mode && (tv[tvidx].plen < 14 || tv[tvidx].dklen < 14)) { @@ -1926,6 +1929,145 @@ } } +static void +check_fips_gcry_kdf_derive(void) +{ + static struct { + const char *p; /* Passphrase. */ + size_t plen; /* Length of P. */ + int algo; + int subalgo; + const char *salt; + size_t saltlen; + unsigned long iterations; + int dklen; /* Requested key length. */ + const char *dk; /* Derived key. */ + int expect_error; + } tv[] = { + { + "passwordPASSWORDpassword", 24, + GCRY_KDF_PBKDF2, GCRY_MD_SHA1, + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, + 4096, + 25, + "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" + "\x4c\xf2\xf0\x70\x38", + 0 + }, + { + "pleaseletmein", 13, + GCRY_KDF_SCRYPT, 8, + "SodiumChloride", 14, + 1, + 64, + "\x70\x23\xbd\xcb\x3a\xfd\x73\x48\x46\x1c\x06\xcd\x81\xfd\x38\xeb" + "\xfd\xa8\xfb\xba\x90\x4f\x8e\x3e\xa9\xb5\x43\xf6\x54\x5d\xa1\xf2" + "\xd5\x43\x29\x55\x61\x3f\x0f\xcf\x62\xd4\x97\x05\x24\x2a\x9a\xf9" + "\xe6\x1e\x85\xdc\x0d\x65\x1e\x40\xdf\xcf\x01\x7b\x45\x57\x58\x87", + 1 /* forbidden because unallowed algo */ + }, + { + "passwor", 7, + GCRY_KDF_PBKDF2, GCRY_MD_SHA1, + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, + 4096, + 25, + "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" + "\x4c\xf2\xf0\x70\x38", /* this is wrong but we don't care because + it should fail anyway */ + 1 /* forbidden because passphrase len is too small */ + }, + { + "passwordPASSWORDpassword", 24, + GCRY_KDF_PBKDF2, GCRY_MD_SHA1, + "saltSALTsaltSAL", 15, + 4096, + 25, + "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" + "\x4c\xf2\xf0\x70\x38", /* this is wrong but we don't care because + it should fail anyway */ + 1 /* forbidden because salt len is too small */ + }, + { + "passwordPASSWORDpassword", 24, + GCRY_KDF_PBKDF2, GCRY_MD_SHA1, + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, + 999, + 25, + "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96" + "\x4c\xf2\xf0\x70\x38", /* this is wrong but we don't care because + it should fail anyway */ + 1 /* forbidden because too few iterations */ + }, + { + "passwordPASSWORDpassword", 24, + GCRY_KDF_PBKDF2, GCRY_MD_SHA1, + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, + 4096, + 13, + "\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8" + "\xd8\x36\x62", /* this is wrong but we don't care because + it should fail anyway */ + 1 /* forbidden because key size too small */ + }, + }; + + int tvidx; + gpg_error_t err; + unsigned char outbuf[100]; + int i; + int my_errno; + + for (tvidx=0; tvidx < DIM(tv); tvidx++) + { + if (verbose) + fprintf (stderr, "checking gcry_kdf_derive test vector %d algo %d for fips\n", + tvidx, tv[tvidx].algo); + assert (tv[tvidx].dklen <= sizeof outbuf); + err = gcry_kdf_derive (tv[tvidx].p, tv[tvidx].plen, + tv[tvidx].algo, tv[tvidx].subalgo, + tv[tvidx].salt, tv[tvidx].saltlen, + tv[tvidx].iterations, tv[tvidx].dklen, outbuf); + // Errno has to be fetched directly after the call + my_errno = errno; + + if (err) + { + fail ("gcry_kdf_derive test %d unexpectedly returned an error in FIPS mode: %s\n", + tvidx, gpg_strerror (err)); + continue; + } + else + { + if (my_errno == EOPNOTSUPP && tv[tvidx].expect_error == 0) + { + + fail ("gcry_kdf_derive test %d unexpectedly set errno '%s' in FIPS mode.\n", + tvidx, strerror(my_errno), strerror(EOPNOTSUPP)); + continue; + } + else if (my_errno != EOPNOTSUPP && tv[tvidx].expect_error) + { + fail ("gcry_kdf_derive test %d expected EOPNOTSUPP in FIPS mode\n", + tvidx); + continue; + } + + if (my_errno == 0 && memcmp (outbuf, tv[tvidx].dk, tv[tvidx].dklen)) + { + fail ("gcry_kdf_derive test %d failed: mismatch\n", tvidx); + fputs ("got:", stderr); + for (i=0; i < tv[tvidx].dklen; i++) + fprintf (stderr, " %02x", outbuf[i]); + putc ('\n', stderr); + } + } + } +} int main (int argc, char **argv) @@ -2009,6 +2151,8 @@ check_hkdf (); if (in_fips_mode) check_fips_indicators(); + if (in_fips_mode) + check_fips_gcry_kdf_derive(); } return error_count ? 1 : 0; -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 840 bytes Desc: OpenPGP digital signature URL: