summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2008-08-29 11:09:26 +0000
committerWerner Koch <wk@gnupg.org>2008-08-29 11:09:26 +0000
commit78a1f612bf65d3c1a445c43b456cb28e98a3a6ea (patch)
treef491a0c08ebd7ddb2de9ebb5736893bcce56bdb3
parent0f019fc357316cd1dca3454fc70f3223fe3e2e29 (diff)
downloadlibgcrypt-78a1f612bf65d3c1a445c43b456cb28e98a3a6ea.tar.gz
Changed the way the FIPS RNG is seeded.
FIPS cleanups. Documentation upodates.
-rw-r--r--cipher/ChangeLog7
-rw-r--r--cipher/cipher.c30
-rw-r--r--cipher/elgamal.c5
-rw-r--r--cipher/primegen.c3
-rw-r--r--doc/ChangeLog4
-rw-r--r--doc/Makefile.am24
-rw-r--r--doc/gcrypt.texi100
-rw-r--r--random/ChangeLog19
-rw-r--r--random/random-csprng.c12
-rw-r--r--random/random-daemon.c4
-rw-r--r--random/random-fips.c230
-rw-r--r--random/rndunix.c5
-rw-r--r--random/rndw32.c2
-rw-r--r--src/ChangeLog12
-rw-r--r--src/global.c65
-rw-r--r--src/hwfeatures.c5
-rw-r--r--tests/ChangeLog10
-rw-r--r--tests/Makefile.am7
-rw-r--r--tests/README9
-rw-r--r--tests/basic.c5
-rw-r--r--tests/pkbench.c110
-rw-r--r--tests/rsa-16k.key18
22 files changed, 511 insertions, 175 deletions
diff --git a/cipher/ChangeLog b/cipher/ChangeLog
index 60e76949..28fdf072 100644
--- a/cipher/ChangeLog
+++ b/cipher/ChangeLog
@@ -1,3 +1,10 @@
+2008-08-28 Werner Koch <wk@g10code.com>
+
+ * cipher.c (cipher_decrypt, cipher_encrypt): Return an error if
+ mode NONE is used.
+ (gcry_cipher_open): Allow mode NONE only with a debug flag set and
+ if not in FIPS mode.
+
2008-08-26 Werner Koch <wk@g10code.com>
* pubkey.c (pubkey_generate): Add arg KEYGEN_FLAGS.
diff --git a/cipher/cipher.c b/cipher/cipher.c
index e3284355..782ff182 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -731,7 +731,11 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
break;
case GCRY_CIPHER_MODE_NONE:
- /* FIXME: issue a warning when this mode is used */
+ /* This mode may be used for debbuging. It copies the main
+ text verbatim to the ciphertext. We do not allow this in
+ fips mode or if no debug flag has been set. */
+ if (fips_mode () || !_gcry_get_debug_flag (0))
+ err = GPG_ERR_INV_CIPHER_MODE;
break;
default:
@@ -1421,8 +1425,16 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf,
outbuf, (byte*)/*arggg*/inbuf, nbytes );
break;
case GCRY_CIPHER_MODE_NONE:
- if( inbuf != outbuf )
- memmove( outbuf, inbuf, nbytes );
+ if (fips_mode () || !_gcry_get_debug_flag (0))
+ {
+ fips_signal_error ("cipher mode NONE used");
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ }
+ else
+ {
+ if ( inbuf != outbuf )
+ memmove (outbuf, inbuf, nbytes);
+ }
break;
default:
log_fatal("cipher_encrypt: invalid mode %d\n", c->mode );
@@ -1512,8 +1524,16 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
outbuf, (byte*)/*arggg*/inbuf, nbytes );
break;
case GCRY_CIPHER_MODE_NONE:
- if( inbuf != outbuf )
- memmove( outbuf, inbuf, nbytes );
+ if (fips_mode () || !_gcry_get_debug_flag (0))
+ {
+ fips_signal_error ("cipher mode NONE used");
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ }
+ else
+ {
+ if (inbuf != outbuf)
+ memmove (outbuf, inbuf, nbytes);
+ }
break;
default:
log_fatal ("cipher_decrypt: invalid mode %d\n", c->mode );
diff --git a/cipher/elgamal.c b/cipher/elgamal.c
index 4a76e910..04ad6fa1 100644
--- a/cipher/elgamal.c
+++ b/cipher/elgamal.c
@@ -83,8 +83,9 @@ progress (int c)
/****************
- * Michael Wiener's table on subgroup sizes to match field sizes
- * (floating around somewhere - Fixme: need a reference)
+ * Michael Wiener's table on subgroup sizes to match field sizes.
+ * (floating around somewhere, probably based on the paper from
+ * Eurocrypt 96, page 332)
*/
static unsigned int
wiener_map( unsigned int n )
diff --git a/cipher/primegen.c b/cipher/primegen.c
index b9b31c6d..94348687 100644
--- a/cipher/primegen.c
+++ b/cipher/primegen.c
@@ -395,8 +395,7 @@ prime_generate_internal (int need_q_factor,
/* Make a pool of 3n+5 primes (this is an arbitrary value). We
require at least 30 primes for are useful selection process.
- FIXME: We need to do some reseacrh on the best formula for sizing
- the pool.
+ Fixme: We need to research the best formula for sizing the pool.
*/
m = n * 3 + 5;
if (need_q_factor) /* Need some more in this case. */
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 193ac2ea..36170c90 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,3 +1,7 @@
+2008-08-27 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (online): Take care of development versions.
+
2008-08-18 Werner Koch <wk@g10code.com>
* gcrypt.texi (Top): Remove the detailmenu.
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 5d356a04..7862110a 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -45,11 +45,27 @@ gcrypt_TEXINFOS = lgpl.texi gpl.texi libgcrypt-modules.fig fips-fsm.fig
fig2dev -L pdf `test -f '$<' || echo '$(srcdir)/'`$< $@
+# Make sure that gcrypt.texi is touched if any other source file has
+# been modified. This is required so that the version.texi magic
+# updates the release date.
+gnupg.texi : $(gcrypt_TEXINFOS)
+ touch $(srcdir)/gcrypt.texi
+
online: gcrypt.html gcrypt.pdf gcrypt.info
set -e; \
echo "Uploading current manuals to www.gnupg.org ..."; \
- user=werner ; dir="webspace/manuals/gcrypt-devel/" ; \
- (cd gcrypt.html && rsync -vr --exclude='.svn' . \
- $${user}@cvs.gnupg.org:$${dir} ); \
- rsync -v gcrypt.pdf gcrypt.info $${user}@cvs.gnupg.org:$${dir}
+ cp libgcrypt-modules.png gcrypt.html/; \
+ cp fips-fsm.png gcrypt.html/; \
+ user=werner ; dashdevel="" ; \
+ if echo "@PACKAGE_VERSION@" | grep -- "-svn" >/dev/null; then \
+ dashdevel="-devel" ; \
+ cp gcrypt.pdf gcrypt.html/; \
+ cp gcrypt.info gcrypt.html/; \
+ else \
+ rsync -v gcrypt.pdf gcrypt.info \
+ $${user}@cvs.gnupg.org:webspace/manuals/ ; \
+ fi ; \
+ cd gcrypt.html ; \
+ rsync -vr --exclude='.svn' . \
+ $${user}@cvs.gnupg.org:webspace/manuals/gcrypt$${dashdevel}/
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 11cb51eb..b3daa07e 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1540,8 +1540,9 @@ number.
@table @code
@item GCRY_CIPHER_MODE_NONE
-No mode specified, may be set later using other functions. The value
-of this constant is always 0.
+No mode specified. This should not be used. The only exception is that
+if Libgcrypt is not used in FIPS mode and if any debug flag has been
+set, this mode may be used to bypass the actual encryption.
@item GCRY_CIPHER_MODE_ECB
Electronic Codebook mode.
@@ -4621,14 +4622,95 @@ TBD.
@node FIPS Restrictions
@appendix Restrictions in FIPS mode
-If Libgcrypt is used FIPS mode these restrictions are effective:
+If Libgcrypt is used in FIPS mode these restrictions are effective:
@itemize
+@item
+The cryptographic algorithms are restricted to this list:
+
+@table @asis
+@item GCRY_CIPHER_3DES
+3 key EDE Triple-DES symmetric encryption.
+@item GCRY_CIPHER_AES128
+AES 128 bit symmetric encryption.
+@item GCRY_CIPHER_AES192
+AES 192 bit symmetric encryption.
+@item GCRY_CIPHER_AES256
+AES 256 bit symmetric encryption.
+@item GCRY_MD_SHA1
+SHA-1 message digest.
+@item GCRY_MD_SHA224
+SHA-224 message digest.
+@item GCRY_MD_SHA256
+SHA-256 message digest.
+@item GCRY_MD_SHA384
+SHA-384 message digest.
+@item GCRY_MD_SHA512
+SHA-512 message digest.
+@item GCRY_MD_SHA1,GCRY_MD_FLAG_HMAC
+HMAC using a SHA-1 message digest.
+@item GCRY_MD_SHA224,GCRY_MD_FLAG_HMAC
+HMAC using a SHA-224 message digest.
+@item GCRY_MD_SHA256,GCRY_MD_FLAG_HMAC
+HMAC using a SHA-256 message digest.
+@item GCRY_MD_SHA384,GCRY_MD_FLAG_HMAC
+HMAC using a SHA-384 message digest.
+@item GCRY_MD_SHA512,GCRY_MD_FLAG_HMAC
+HMAC using a SHA-512 message digest.
+@item GCRY_PK_RSA
+RSA encryption and signing.
+@item GCRY_PK_DSA
+DSA signing.
+@end table
+
+Note that the CRC algorithms are not considered cryptographic algorithms
+and thus are in addition available.
+
+@item
+RSA and DSA key generation refuses to create a key with a keysize of
+less than 1024 bits.
+
+@item
+The @code{transient-key} flag for RSA key generation is ignored.
+
+@item
+Support for the VIA Padlock engine is disabled.
@item
-It may only be used on systesm with a /dev/random device. Swicthing
-into FIPS mode on other systems will fail at runtime.
+FIPS mode may only be used on systems with a /dev/random device.
+Switching into FIPS mode on other systems will fail at runtime.
+@item
+Saving and loading a random seed file is not ignored.
+
+@item
+An X9.31 style random number generator is used in place of the
+large-pool-CSPRNG generator.
+
+@item
+The Alternative Public Key Interface (@code{gcry_ac_xxx}) is not
+supported and all API calls return an error.
+
+@item Registration of external modules is not supported.
+
+@item
+Message digest debugging is disabled.
+
+@item
+All debug output related to cryptographic data is suppressed.
+
+@item
+On-the-fly self-tests are not performed, instead of this self-tests are
+run before entering operational state.
+
+@item
+The function @code{gcry_set_allocation_handler} may not be used. If it
+is used Libgcrypt will enter the error state.
+
+@item
+A handler set by @code{gcry_set_outofcore_handler} is ignored.
+@item
+A handler set by @code{gcry_set_fatalerror_handler} is ignored.
@end itemize
@@ -4799,6 +4881,14 @@ self-tests to get to get back into operational state after an error.
@bye
+GCRYCTL_SET_RANDOM_DAEMON_SOCKET
+GCRYCTL_USE_RANDOM_DAEMON
+The random damon is still a bit experimental, thus we do not document
+them. Not ethat they should be used during initialization and that
+these functions are not really thread safe.
+
+
+
@c LocalWords: int HD
diff --git a/random/ChangeLog b/random/ChangeLog
index cd35870e..df3cac7a 100644
--- a/random/ChangeLog
+++ b/random/ChangeLog
@@ -1,3 +1,22 @@
+2008-08-29 Werner Koch <wk@g10code.com>
+
+ * random-fips.c (SEED_TTL): New.
+ (struct rng_context): Add USE_COUNTER, remove NEED_STRONG_ENTROPY.
+ (x931_aes_driver): Do re-seeding if required.
+ (x931_generate_key, x931_generate_seed): Factor common code out to ..
+ (get_entropy): .. new. Always use /dev/random.
+ (x931_generate_key): Seed key for nonce_context from std_rng_context.
+ (x931_reseed): New. Seed nonce context from std_rng_context.
+ (get_random): Use x931_reseed.
+ (_gcry_rngfips_selftest): Return an error if no /dev/radom support
+ has been compiled in.
+ (get_random): Remove locking.
+ (_gcry_rngfips_randomize, _gcry_rngfips_create_nonce): Lock here.
+
+2008-08-28 Werner Koch <wk@g10code.com>
+
+ * random-daemon.c (connect_to_socket): Use GPG_ERR_ENAMETOOLONG.
+
2008-08-25 Werner Koch <wk@g10code.com>
* random-fips.c (x931_aes): Take datetime_GT from an arg.
diff --git a/random/random-csprng.c b/random/random-csprng.c
index eda34f76..39c49a70 100644
--- a/random/random-csprng.c
+++ b/random/random-csprng.c
@@ -367,9 +367,9 @@ _gcry_rngcsprng_initialize (int full)
void
_gcry_rngcsprng_dump_stats (void)
{
- /* FIXME: don't we need proper locking here? -mo.
- Yes. However this is usually called during cleanup and thenwe _
- might_ run into problems. Needs to be checked. -wk */
+ /* In theory we would need to lock the stats here. However this
+ function is usually called during cleanup and then we _might_ run
+ into problems. */
log_info ("random usage: poolsize=%d mixed=%lu polls=%lu/%lu added=%lu/%lu\n"
" outmix=%lu getlvl1=%lu/%lu getlvl2=%lu/%lu%s\n",
@@ -422,7 +422,11 @@ _gcry_rngcsprng_use_daemon (int onoff)
#ifdef USE_RANDOM_DAEMON
int last;
- /* FIXME: This is not really thread safe. */
+ /* This is not really thread safe. However it is expected that this
+ function is being called during initialization and at that point
+ we are for other reasons not really thread safe. We do not want
+ to lock it because we might eventually decide that this function
+ may even be called prior to gcry_check_version. */
last = allow_daemon;
if (onoff != -1)
allow_daemon = onoff;
diff --git a/random/random-daemon.c b/random/random-daemon.c
index 0b79e5c5..2e03ba00 100644
--- a/random/random-daemon.c
+++ b/random/random-daemon.c
@@ -90,7 +90,7 @@ connect_to_socket (const char *socketname, int *sock)
if (strlen (socketname) + 1 >= sizeof (srvr_addr->sun_path))
{
log_error ("socket name `%s' too long\n", socketname);
- err = gcry_error (GPG_ERR_INTERNAL); /* FIXME? */
+ err = gcry_error (GPG_ERR_ENAMETOOLONG);
goto out;
}
strcpy (srvr_addr->sun_path, socketname);
@@ -285,7 +285,7 @@ call_daemon (const char *socketname,
break;
}
- /* if (1)*/ /* FIXME, verbose */
+ /* if (1)*/ /* Do this in verbose mode? */
/* log_info ("received response with %d bytes of data\n", buf[1]);*/
if (buf[1] < nbytes)
diff --git a/random/random-fips.c b/random/random-fips.c
index 57a65733..68f0ec40 100644
--- a/random/random-fips.c
+++ b/random/random-fips.c
@@ -21,8 +21,36 @@
The core of this deterministic random number generator is
implemented according to the document "NIST-Recommended Random
Number Generator Based on ANSI X9.31 Appendix A.2.4 Using the 3-Key
- Triple DES and AES Algorithms" (2005-01-31). This implementaion
+ Triple DES and AES Algorithms" (2005-01-31). This implementation
uses the AES variant.
+
+ There are 3 random context which map to the different levels of
+ random quality:
+
+ Generator Seed and Key Kernel entropy (init/reseed)
+ ------------------------------------------------------------
+ GCRY_VERY_STRONG_RANDOM /dev/random 256/128 bits
+ GCRY_STRONG_RANDOM /dev/random 256/128 bits
+ gcry_create_nonce GCRY_STRONG_RANDOM n/a
+
+ All random generators return their data in 128 bit blocks. If the
+ caller requested less bits, the extra bits are not used. The key
+ for each generator is only set once at the first time a generator
+ is used. The seed value is set with the key and again after 1000
+ (SEED_TTL) output blocks.
+
+ The GCRY_VERY_STRONG_RANDOM and GCRY_STRONG_RANDOM generators are
+ keyed and seeded from the /dev/random device. Thus these
+ generators may block until the kernel has collected enough entropy.
+
+ The gcry_create_nonce generator is keyed and seeded from the
+ GCRY_STRONG_RANDOM generator. It may also block if the
+ GCRY_STRONG_RANDOM generator has not yet been used before and thus
+ gets initialized on the first use by gcry_create_nonce. This
+ special treatment is justified by the weaker requirements for a
+ nonce generator and to save precious kernel entropy for use by the
+ real random generators.
+
*/
#include <config.h>
@@ -59,6 +87,11 @@ static int fips_rng_is_locked;
static unsigned char *tempvalue_for_x931_aes_driver;
+/* After having retrieved this number of blocks from the RNG, we want
+ to do a reseeding. */
+#define SEED_TTL 1000
+
+
/* The length of the key we use: 16 bytes (128 bit) for AES128. */
#define X931_AES_KEYLEN 16
/* A global buffer used to communicate between the x931_generate_key
@@ -83,10 +116,6 @@ struct rng_context
established. */
gcry_cipher_hd_t cipher_hd;
- /* If this flag is true, this context requires strong entropy;
- i.e. from /dev/random. */
- int need_strong_entropy:1;
-
/* If this flag is true, the SEED_V buffer below carries a valid
seed. */
int is_seeded:1;
@@ -96,6 +125,9 @@ struct rng_context
is available. */
int compare_value_valid:1;
+ /* A counter used to trigger re-seeding. */
+ unsigned int use_counter;
+
unsigned char guard_1[1];
/* The buffer containing the seed value V. */
@@ -140,6 +172,11 @@ static rng_context_t std_rng_context;
static rng_context_t strong_rng_context;
+/* --- Local prototypes --- */
+static void x931_reseed (rng_context_t rng_ctx);
+static void get_random (void *buffer, size_t length, rng_context_t rng_ctx);
+
+
/* --- Functions --- */
@@ -412,6 +449,13 @@ x931_aes_driver (unsigned char *output, size_t length, rng_context_t rng_ctx)
while (length)
{
+ /* We require a new seed after some time. */
+ if (rng_ctx->use_counter > SEED_TTL)
+ {
+ x931_reseed (rng_ctx);
+ rng_ctx->use_counter = 0;
+ }
+
/* Due to the design of the RNG, we always receive 16 bytes (128
bit) of random even if we require less. The extra bytes
returned are not used. Intheory we could save them for the
@@ -423,6 +467,7 @@ x931_aes_driver (unsigned char *output, size_t length, rng_context_t rng_ctx)
x931_aes (result_buffer,
datetime_DT, rng_ctx->seed_V, rng_ctx->cipher_hd,
intermediate_I, temp_buffer);
+ rng_ctx->use_counter++;
/* Do a basic check on the output to avoid a stuck generator. */
if (!rng_ctx->compare_value_valid)
@@ -455,9 +500,9 @@ x931_aes_driver (unsigned char *output, size_t length, rng_context_t rng_ctx)
/* Callback for x931_generate_key. Note that this callback uses the
- global ENTROPY_COLLECT_BUFFER which has been setup by
- x931_generate_key. ORIGIN is not used but required due to the
- emtropy gathering module. */
+ global ENTROPY_COLLECT_BUFFER which has been setup by get_entropy.
+ ORIGIN is not used but required due to the design of entropy
+ gathering module. */
static void
entropy_collect_cb (const void *buffer, size_t length,
enum random_origins origin)
@@ -476,15 +521,49 @@ entropy_collect_cb (const void *buffer, size_t length,
}
}
+
+/* Get NBYTES of entropy from the kernel device. The callers needs to
+ free the returned buffer. The function either succeeds or
+ terminates the process in case of a fatal error. */
+static void *
+get_entropy (size_t nbytes)
+{
+#if USE_RNDLINUX
+ void *result;
+
+ gcry_assert (!entropy_collect_buffer);
+ entropy_collect_buffer = gcry_xmalloc_secure (nbytes);
+ entropy_collect_buffer_size = nbytes;
+ entropy_collect_buffer_len = 0;
+ if (_gcry_rndlinux_gather_random (entropy_collect_cb, 0,
+ X931_AES_KEYLEN,
+ GCRY_VERY_STRONG_RANDOM) < 0
+ || entropy_collect_buffer_len != entropy_collect_buffer_size)
+ {
+ gcry_free (entropy_collect_buffer);
+ entropy_collect_buffer = NULL;
+ log_fatal ("error getting entropy data\n");
+ }
+ result = entropy_collect_buffer;
+ entropy_collect_buffer = NULL;
+ return result;
+#else
+ log_fatal ("/dev/random support is not compiled in\n");
+ return NULL; /* NOTREACHED */
+#endif
+}
+
+
/* Generate a key for use with x931_aes. The function returns a
handle to the cipher context readily prepared for ECB encryption.
- If VERY_STRONG is true the key is read from /dev/random, otherwise
- from /dev/urandom. On error NULL is returned. */
+ If FOR_NONCE is true, the key is retrieved by readong random from
+ the standard generator. On error NULL is returned. */
static gcry_cipher_hd_t
-x931_generate_key (int very_strong)
+x931_generate_key (int for_nonce)
{
gcry_cipher_hd_t hd;
gpg_error_t err;
+ void *buffer;
gcry_assert (fips_rng_is_locked);
@@ -498,34 +577,22 @@ x931_generate_key (int very_strong)
return NULL;
}
- /* Get a key from the entropy source. */
-#if USE_RNDLINUX
- gcry_assert (!entropy_collect_buffer);
- entropy_collect_buffer = gcry_xmalloc_secure (X931_AES_KEYLEN);
- entropy_collect_buffer_size = X931_AES_KEYLEN;
- entropy_collect_buffer_len = 0;
- if (_gcry_rndlinux_gather_random (entropy_collect_cb, 0, X931_AES_KEYLEN,
- (very_strong
- ? GCRY_VERY_STRONG_RANDOM
- : GCRY_STRONG_RANDOM)
- ) < 0
- || entropy_collect_buffer_len != entropy_collect_buffer_size)
+ /* Get a key from the standard RNG or from the entropy source. */
+ if (for_nonce)
{
- gcry_free (entropy_collect_buffer);
- entropy_collect_buffer = NULL;
- gcry_cipher_close (hd);
- log_fatal ("error getting entropy data for the RNG key\n");
+ buffer = gcry_xmalloc (X931_AES_KEYLEN);
+ get_random (buffer, X931_AES_KEYLEN, std_rng_context);
+ }
+ else
+ {
+ buffer = get_entropy (X931_AES_KEYLEN);
}
-#else
- log_fatal ("/dev/random support is not compiled in\n");
-#endif
/* Set the key and delete the buffer because the key is now part of
the cipher context. */
- err = gcry_cipher_setkey (hd, entropy_collect_buffer, X931_AES_KEYLEN);
- wipememory (entropy_collect_buffer, X931_AES_KEYLEN);
- gcry_free (entropy_collect_buffer);
- entropy_collect_buffer = NULL;
+ err = gcry_cipher_setkey (hd, buffer, X931_AES_KEYLEN);
+ wipememory (buffer, X931_AES_KEYLEN);
+ gcry_free (buffer);
if (err)
{
log_error ("error creating key for RNG: %s\n", gcry_strerror (err));
@@ -540,35 +607,43 @@ x931_generate_key (int very_strong)
/* Generate a key for use with x931_aes. The function copies a seed
of LENGTH bytes into SEED_BUFFER. LENGTH needs to by given as 16. */
static void
-x931_generate_seed (unsigned char *seed_buffer, size_t length, int very_strong)
+x931_generate_seed (unsigned char *seed_buffer, size_t length)
{
+ void *buffer;
+
gcry_assert (fips_rng_is_locked);
gcry_assert (length == 16);
- /* Get a seed from the entropy source. */
-#if USE_RNDLINUX
- gcry_assert (!entropy_collect_buffer);
- entropy_collect_buffer = gcry_xmalloc_secure (X931_AES_KEYLEN);
- entropy_collect_buffer_size = X931_AES_KEYLEN;
- entropy_collect_buffer_len = 0;
- if (_gcry_rndlinux_gather_random (entropy_collect_cb, 0, X931_AES_KEYLEN,
- (very_strong
- ? GCRY_VERY_STRONG_RANDOM
- : GCRY_STRONG_RANDOM)
- ) < 0
- || entropy_collect_buffer_len != entropy_collect_buffer_size)
+ buffer = get_entropy (X931_AES_KEYLEN);
+
+ memcpy (seed_buffer, buffer, X931_AES_KEYLEN);
+ wipememory (buffer, X931_AES_KEYLEN);
+ gcry_free (buffer);
+}
+
+
+
+/* Reseed a generator. This is also used for the initial seeding. */
+static void
+x931_reseed (rng_context_t rng_ctx)
+{
+ gcry_assert (fips_rng_is_locked);
+
+ if (rng_ctx == nonce_context)
{
- gcry_free (entropy_collect_buffer);
- entropy_collect_buffer = NULL;
- log_fatal ("error getting entropy data for the RNG seed\n");
+ /* The nonce context is special. It will be seeded using the
+ standard random generator. */
+ get_random (rng_ctx->seed_V, 16, std_rng_context);
+ rng_ctx->is_seeded = 1;
+ rng_ctx->seed_init_pid = getpid ();
+ }
+ else
+ {
+ /* The other two generators are seeded from /dev/random. */
+ x931_generate_seed (rng_ctx->seed_V, 16);
+ rng_ctx->is_seeded = 1;
+ rng_ctx->seed_init_pid = getpid ();
}
-#else
- log_fatal ("/dev/random support is not compiled in\n");
-#endif
- memcpy (seed_buffer, entropy_collect_buffer, X931_AES_KEYLEN);
- wipememory (entropy_collect_buffer, X931_AES_KEYLEN);
- gcry_free (entropy_collect_buffer);
- entropy_collect_buffer = NULL;
}
@@ -582,13 +657,15 @@ get_random (void *buffer, size_t length, rng_context_t rng_ctx)
gcry_assert (buffer);
gcry_assert (rng_ctx);
- lock_rng ();
check_guards (rng_ctx);
/* Initialize the cipher handle and thus setup the key if needed. */
if (!rng_ctx->cipher_hd)
{
- rng_ctx->cipher_hd = x931_generate_key (rng_ctx->need_strong_entropy);
+ if (rng_ctx == nonce_context)
+ rng_ctx->cipher_hd = x931_generate_key (1);
+ else
+ rng_ctx->cipher_hd = x931_generate_key (0);
if (!rng_ctx->cipher_hd)
goto bailout;
rng_ctx->key_init_pid = getpid ();
@@ -596,11 +673,7 @@ get_random (void *buffer, size_t length, rng_context_t rng_ctx)
/* Initialize the seed value if needed. */
if (!rng_ctx->is_seeded)
- {
- x931_generate_seed (rng_ctx->seed_V, 16, rng_ctx->need_strong_entropy);
- rng_ctx->is_seeded = 1;
- rng_ctx->seed_init_pid = getpid ();
- }
+ x931_reseed (rng_ctx);
if (rng_ctx->key_init_pid != getpid ()
|| rng_ctx->seed_init_pid != getpid ())
@@ -618,17 +691,17 @@ get_random (void *buffer, size_t length, rng_context_t rng_ctx)
goto bailout;
check_guards (rng_ctx);
- unlock_rng ();
return;
bailout:
- unlock_rng ();
log_fatal ("severe error getting random\n");
/*NOTREACHED*/
}
+/* --- Public Functions --- */
+
/* Initialize this random subsystem. If FULL is false, this function
merely calls the basic initialization of the module and does not do
anything more. Doing this is not really required but when running
@@ -658,7 +731,6 @@ _gcry_rngfips_initialize (int full)
setup_guards (std_rng_context);
strong_rng_context = gcry_xcalloc_secure (1, sizeof *strong_rng_context);
- strong_rng_context->need_strong_entropy = 1;
setup_guards (strong_rng_context);
}
else
@@ -713,10 +785,12 @@ _gcry_rngfips_randomize (void *buffer, size_t length,
{
_gcry_rngfips_initialize (1); /* Auto-initialize if needed. */
+ lock_rng ();
if (level == GCRY_VERY_STRONG_RANDOM)
get_random (buffer, length, strong_rng_context);
else
get_random (buffer, length, std_rng_context);
+ unlock_rng ();
}
@@ -726,7 +800,9 @@ _gcry_rngfips_create_nonce (void *buffer, size_t length)
{
_gcry_rngfips_initialize (1); /* Auto-initialize if needed. */
+ lock_rng ();
get_random (buffer, length, nonce_context);
+ unlock_rng ();
}
@@ -876,16 +952,24 @@ gcry_error_t
_gcry_rngfips_selftest (selftest_report_func_t report)
{
gcry_err_code_t ec;
- char buffer[8];
- /* Do a simple test using the public interface. This will also
- enforce full intialization of the RNG. We need to be fully
- initialized due to the global requirement of the
- tempvalue_for_x931_aes_driver stuff. */
- gcry_randomize (buffer, sizeof buffer, GCRY_STRONG_RANDOM);
+#if USE_RNDLINUX
+ {
+ char buffer[8];
+
+ /* Do a simple test using the public interface. This will also
+ enforce full intialization of the RNG. We need to be fully
+ initialized due to the global requirement of the
+ tempvalue_for_x931_aes_driver stuff. */
+ gcry_randomize (buffer, sizeof buffer, GCRY_STRONG_RANDOM);
+ }
ec = selftest_kat (report);
+#else /*!USE_RNDLINUX*/
+ report ("random", 0, "setup", "no support for /dev/random");
+ ec = GPG_ERR_SELFTEST_FAILED;
+#endif
return gpg_error (ec);
}
diff --git a/random/rndunix.c b/random/rndunix.c
index 0a04eb21..1faf9abc 100644
--- a/random/rndunix.c
+++ b/random/rndunix.c
@@ -81,9 +81,6 @@
=========
*/
-/* Fixme: We use plain mallocs here because it may be used as a module.
- * Should be changed. */
-
/* General includes */
#include <config.h>
@@ -717,7 +714,7 @@ start_gatherer( int pipefd )
}
- /* Set up the buffer */
+ /* Set up the buffer. Not ethat we use a plain standard malloc here. */
gather_buffer_size = GATHER_BUFSIZE;
gather_buffer = malloc( gather_buffer_size );
if( !gather_buffer ) {
diff --git a/random/rndw32.c b/random/rndw32.c
index 1952979d..c514019f 100644
--- a/random/rndw32.c
+++ b/random/rndw32.c
@@ -939,7 +939,7 @@ _gcry_rndw32_gather_random_fast (void (*add)(const void*, size_t,
directly by checking for CPUID capabilities, and fall back to QPC if
this isn't present. */
#ifdef __GNUC__
-/* FIXME: We need to implement the CPU feature tests first. */
+/* FIXME: We would need to implement the CPU feature tests first. */
/* if (cpu_has_feature_rdtsc) */
/* { */
/* uint32_t lo, hi; */
diff --git a/src/ChangeLog b/src/ChangeLog
index 578f5c89..d72ef544 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,9 +1,19 @@
+2008-08-28 Werner Koch <wk@g10code.com>
+
+ * hwfeatures.c (_gcry_detect_hw_features): Disable hardware
+ detection in FIPS mode.
+
2008-08-27 Werner Koch <wk@g10code.com>
* global.c (_gcry_vcontrol): Allow running selftests from error
state.
+ (gcry_set_outofcore_handler): Only print a warning if used in FIPS
+ mode.
+ (gcry_xmalloc, gcry_xrealloc, gcry_xmalloc_secure, gcry_xstrdup):
+ Ignore an outofcore handler in FIPS mode.
+
* fips.c (_gcry_fips_test_error_or_operational): New.
- (fips_new_state): Allow transtion from error into selftest.
+ (fips_new_state): Allow transition from error into selftest.
Disallow error to init.
2008-08-26 Werner Koch <wk@g10code.com>
diff --git a/src/global.c b/src/global.c
index 1d5314b3..3b32ec61 100644
--- a/src/global.c
+++ b/src/global.c
@@ -115,12 +115,11 @@ global_init (void)
return;
fail:
- /* FIXME: use `err'? */
BUG ();
}
-
+
/* Version number parsing. */
/* This function parses the first portion of the version number S and
@@ -599,7 +598,7 @@ gcry_set_outofcore_handler( int (*f)( void*, size_t, unsigned int ),
if (fips_mode () )
{
- fips_signal_error ("out of core handler used");
+ log_info ("out of core handler ignored in FIPS mode\n");
return;
}
@@ -780,13 +779,16 @@ gcry_strdup (const char *string)
void *
gcry_xmalloc( size_t n )
{
- void *p;
-
- while ( !(p = gcry_malloc( n )) ) {
- if( !outofcore_handler
- || !outofcore_handler( outofcore_handler_value, n, 0 ) ) {
- _gcry_fatal_error(gpg_err_code_from_errno (errno), NULL );
- }
+ void *p;
+
+ while ( !(p = gcry_malloc( n )) )
+ {
+ if ( fips_mode ()
+ || !outofcore_handler
+ || !outofcore_handler (outofcore_handler_value, n, 0) )
+ {
+ _gcry_fatal_error (gpg_err_code_from_errno (errno), NULL);
+ }
}
return p;
}
@@ -794,13 +796,16 @@ gcry_xmalloc( size_t n )
void *
gcry_xrealloc( void *a, size_t n )
{
- void *p;
-
- while ( !(p = gcry_realloc( a, n )) ) {
- if( !outofcore_handler
- || !outofcore_handler( outofcore_handler_value, n,
- gcry_is_secure(a)? 3:2 ) ) {
- _gcry_fatal_error(gpg_err_code_from_errno (errno), NULL );
+ void *p;
+
+ while ( !(p = gcry_realloc( a, n )) )
+ {
+ if ( fips_mode ()
+ || !outofcore_handler
+ || !outofcore_handler (outofcore_handler_value, n,
+ gcry_is_secure(a)? 3:2 ) )
+ {
+ _gcry_fatal_error (gpg_err_code_from_errno (errno), NULL );
}
}
return p;
@@ -809,16 +814,19 @@ gcry_xrealloc( void *a, size_t n )
void *
gcry_xmalloc_secure( size_t n )
{
- void *p;
-
- while ( !(p = gcry_malloc_secure( n )) ) {
- if( !outofcore_handler
- || !outofcore_handler( outofcore_handler_value, n, 1 ) ) {
- _gcry_fatal_error(gpg_err_code_from_errno (errno),
- _("out of core in secure memory"));
+ void *p;
+
+ while ( !(p = gcry_malloc_secure( n )) )
+ {
+ if ( fips_mode ()
+ || !outofcore_handler
+ || !outofcore_handler (outofcore_handler_value, n, 1) )
+ {
+ _gcry_fatal_error (gpg_err_code_from_errno (errno),
+ _("out of core in secure memory"));
}
}
- return p;
+ return p;
}
@@ -862,13 +870,14 @@ char *
gcry_xstrdup (const char *string)
{
char *p;
-
+
while ( !(p = gcry_strdup (string)) )
{
size_t n = strlen (string);
int is_sec = !!gcry_is_secure (string);
-
- if (!outofcore_handler
+
+ if (fips_mode ()
+ || !outofcore_handler
|| !outofcore_handler (outofcore_handler_value, n, is_sec) )
{
_gcry_fatal_error (gpg_err_code_from_errno (errno),
diff --git a/src/hwfeatures.c b/src/hwfeatures.c
index 4dd4e4f4..2621305b 100644
--- a/src/hwfeatures.c
+++ b/src/hwfeatures.c
@@ -145,7 +145,7 @@ detect_ia32_gnuc (void)
-/* Detect the available hardware features. This fucntion is called
+/* Detect the available hardware features. This function is called
once right at startup and we assume that no other threads are
running. */
void
@@ -153,6 +153,9 @@ _gcry_detect_hw_features (void)
{
hw_features = 0;
+ if (fips_mode ())
+ return; /* Hardware support is not to be evaluated. */
+
#if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4
#ifdef __GNUC__
detect_ia32_gnuc ();
diff --git a/tests/ChangeLog b/tests/ChangeLog
index aea5ce31..c08d544c 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,5 +1,15 @@
+2008-08-28 Werner Koch <wk@g10code.com>
+
+ * rsa-16k.key: New sample key.
+
2008-08-27 Werner Koch <wk@g10code.com>
+ * pkbench.c (read_file): New.
+ (process_key_pair_file): Replace mmap by read_file.
+ (main): Add a --fips option.
+ * Makefile.am (EXTRA_DIST): Remove.
+ (EXTRA_PROGRAMS): Add pkbench.
+
* basic.c (main): Extended FIPS self-test test.
2008-08-26 Werner Koch <wk@g10code.com>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4f6ab42a..6c39d219 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -38,9 +38,8 @@ AM_CFLAGS = $(GPG_ERROR_CFLAGS)
LDADD = ../src/libgcrypt.la $(DL_LIBS)
-EXTRA_PROGRAMS = testapi
+EXTRA_PROGRAMS = testapi pkbench
noinst_PROGRAMS = $(TESTS)
-# pkbench uses mmap for no good reason. Needs to be fixed. Code for
-# this can be found in libksba/tests.
-EXTRA_DIST = pkbench.c
+EXTRA_DIST = README rsa-16k.key
+
diff --git a/tests/README b/tests/README
new file mode 100644
index 00000000..53268902
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,9 @@
+Some notes about the tests.
+
+rsa-16k.key - A 16384 bit RSA key (public and privat), created 2008-08-28.
+ It took 91 minutes to create it on a 1500Mhz Pentium M.
+ pkpench showed these results:
+ encrypt: 80 ms
+ decrypt: 14370 ms
+ sign: 14110 ms
+ verify: 30 ms
diff --git a/tests/basic.c b/tests/basic.c
index 70a76fe3..aafd41c9 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1,5 +1,5 @@
/* basic.c - basic regression tests
- * Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
@@ -14,8 +14,7 @@
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
diff --git a/tests/pkbench.c b/tests/pkbench.c
index 1dbc28a6..67b94dc9 100644
--- a/tests/pkbench.c
+++ b/tests/pkbench.c
@@ -1,5 +1,5 @@
/* pkbench.c - Pubkey menchmarking
- * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2005, 2008 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
@@ -14,8 +14,7 @@
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
@@ -26,14 +25,14 @@
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
-#include <sys/mman.h>
#include <sys/stat.h>
#ifndef HAVE_W32_SYSTEM
-#include <sys/times.h>
+# include <sys/times.h>
#endif /*HAVE_W32_SYSTEM*/
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
+#include <errno.h>
#define PGM "pkbench"
@@ -88,12 +87,50 @@ show_sexp (const char *prefix, gcry_sexp_t a)
fputs (prefix, stderr);
size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
- buf = malloc (size);
- if (!buf)
- die ("out of core\n");
+ buf = gcry_xmalloc (size);
gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
fprintf (stderr, "%.*s", (int)size, buf);
+ gcry_free (buf);
+}
+
+
+static void *
+read_file (const char *fname, size_t *r_length)
+{
+ FILE *fp;
+ struct stat st;
+ char *buf;
+ size_t buflen;
+
+ fp = fopen (fname, "rb");
+ if (!fp)
+ {
+ fail ("can't open `%s': %s\n", fname, strerror (errno));
+ return NULL;
+ }
+
+ if (fstat (fileno(fp), &st))
+ {
+ fail ("can't stat `%s': %s\n", fname, strerror (errno));
+ fclose (fp);
+ return NULL;
+ }
+
+ buflen = st.st_size;
+ buf = gcry_xmalloc (buflen+1);
+ if (fread (buf, buflen, 1, fp) != 1)
+ {
+ fail ("error reading `%s': %s\n", fname, strerror (errno));
+ fclose (fp);
+ gcry_free (buf);
+ return NULL;
+ }
+ fclose (fp);
+
+ if (r_length)
+ *r_length = buflen;
+ return buf;
}
@@ -312,23 +349,16 @@ process_key_pair_file (const char *key_pair_file)
gcry_sexp_t key_secret_sexp = NULL;
gcry_sexp_t key_public_sexp = NULL;
struct context context = { NULL };
- struct stat statbuf;
- int key_pair_fd = -1;
- int ret = 0;
-
- ret = stat (key_pair_file, &statbuf);
- assert (! ret);
-
- key_pair_fd = open (key_pair_file, O_RDONLY);
- assert (key_pair_fd != -1);
+ size_t file_length;
- key_pair_buffer = mmap (NULL, statbuf.st_size, PROT_READ,
- MAP_PRIVATE, key_pair_fd, 0);
- assert (key_pair_buffer != MAP_FAILED);
+ key_pair_buffer = read_file (key_pair_file, &file_length);
+ if (!key_pair_buffer)
+ die ("failed to open `%s'\n", key_pair_file);
err = gcry_sexp_sscan (&key_pair_sexp, NULL,
- key_pair_buffer, statbuf.st_size);
- assert (! err);
+ key_pair_buffer, file_length);
+ if (err)
+ die ("gcry_sexp_sscan failed\n");
key_secret_sexp = gcry_sexp_find_token (key_pair_sexp, "private-key", 0);
assert (key_secret_sexp);
@@ -336,10 +366,6 @@ process_key_pair_file (const char *key_pair_file)
assert (key_public_sexp);
gcry_sexp_release (key_pair_sexp);
- ret = munmap (key_pair_buffer, statbuf.st_size);
- assert (! ret);
- ret = close (key_pair_fd);
- assert (! ret);
context_init (&context, key_secret_sexp, key_public_sexp);
@@ -348,6 +374,7 @@ process_key_pair_file (const char *key_pair_file)
printf ("\n");
context_destroy (&context);
+ gcry_free (key_pair_buffer);
}
@@ -380,13 +407,13 @@ generate_key (const char *algorithm, const char *key_size)
key_pair_buffer_size = gcry_sexp_sprint (key_pair, GCRYSEXP_FMT_ADVANCED,
NULL, 0);
- key_pair_buffer = malloc (key_pair_buffer_size);
- assert (key_pair_buffer);
+ key_pair_buffer = gcry_xmalloc (key_pair_buffer_size);
gcry_sexp_sprint (key_pair, GCRYSEXP_FMT_ADVANCED,
key_pair_buffer, key_pair_buffer_size);
printf ("%.*s", (int)key_pair_buffer_size, key_pair_buffer);
+ gcry_free (key_pair_buffer);
}
@@ -396,17 +423,11 @@ main (int argc, char **argv)
{
int last_argc = -1;
int genkey_mode = 0;
+ int fips_mode = 0;
if (argc)
{ argc--; argv++; }
- gcry_control (GCRYCTL_DISABLE_SECMEM);
- if (!gcry_check_version (GCRYPT_VERSION))
- {
- fprintf (stderr, PGM ": version mismatch\n");
- exit (1);
- }
-
while (argc && last_argc != argc )
{
last_argc = argc;
@@ -429,7 +450,7 @@ main (int argc, char **argv)
}
else if (!strcmp (*argv, "--verbose"))
{
- verbose = 1;
+ verbose++;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
@@ -442,8 +463,25 @@ main (int argc, char **argv)
genkey_mode = 1;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--fips"))
+ {
+ fips_mode = 1;
+ argc--; argv++;
+ }
}
+ gcry_control (GCRYCTL_SET_VERBOSITY, (int)verbose);
+
+ if (fips_mode)
+ gcry_control (GCRYCTL_FORCE_FIPS_MODE, 0);
+
+ gcry_control (GCRYCTL_DISABLE_SECMEM);
+ if (!gcry_check_version (GCRYPT_VERSION))
+ {
+ fprintf (stderr, PGM ": version mismatch\n");
+ exit (1);
+ }
+
if (genkey_mode)
{
/* No valuable keys are create, so we can speed up our RNG. */
diff --git a/tests/rsa-16k.key b/tests/rsa-16k.key
new file mode 100644
index 00000000..017915a2
--- /dev/null
+++ b/tests/rsa-16k.key
@@ -0,0 +1,18 @@
+(key-data
+ (public-key
+ (rsa
+ (n #00D6007A7AD47BB8D6B356E4F24DFAEE3A722FEE77F7E9547F866CB369C233E6CB3916D416973E3157B4DC1837E6D4C907D1063855735EAA857176A7DA3CA9F378FF7AE9EF227C193965F106F35DB2A833D2760CF9F2D041938CD310D9CE38EDD179C33EBC4963A02221D8000FDBF4BEB592CAB1ED1EEEC9D6916F27263C76DE70184F5399DE3B3862227346B1B3FBA306174D08BEC3675E2593CFD42159655B0BE1A2B69C2BA9F4F03B8C6BA505F6BFDFC6163D74F42A6D4908284D6879CECCF6512F9225612E3030ACF3663DFB77B41AFFCFC70BC11B224E14B397D25AC15E4E342B34B363056EA76CB0265DBD41F733C7FDE98B7C2340289E338CD31F993ABFACA6E83B54BCB50DD1DD11165C188C80EBDA190A11B6D8982CDA1B6B9D1631AA3EACC93040237831A52D15826A0D3E92C833D0234975C92A7236F902FA6703C89C7779765020C11F714B4C9D33B76CD466DC2BE9A102488B0635F31E6FBB9282E5139D32623E10ED9C295DA3B39F68227218EDF8C6FE9372F174AE1DE5BBE7B0AF09A869CAEDBEFE05458BFD43CF32F10F5C345A2E3D588C8C16B4DA8B44FA9539C679B81133A35498696F5D866E3B6A89811AEA7BFD1BF690EC329D87989CDADA7EAB106785D2D6661BD400D76C113E28F13FD883027E1CAC848B13750C7CCD530273C165BDFDA93E6F72897E97F003308704B95801F223EE89160786B1DE440BA9C1F371CBA37E5B09650CDB3AA1ABAA237AD15B89DCD03390A28308643E219490BEC83403F6A09B94F81D7BB391C121FC9028A6908E5B287AC79209B905B33724B1869A679CB347BF192D80D2D66CF1DAEFEBBF22CEDB8CEC010D6F8D86CD055ED71425DA72DF1C07A573E6F070235C378DAB5404ED004B4946CCDA4786ACBBF379A47CC36A049C50651CA4B1CEF03EE87DB6D2484C3D10AF71798A6AD1E20780814F79348D45BD1004880D2DEDEBD152694C80B9F93DF32F5930911DB379B4CBB9230CFC5FC126B9B77F074B9C82BDB4F12471B3FE92079525FD276293B63B978B55E039024EE688180D7C7C6C094B754AB9B652AC31812F2F7E45EF2B6D4478D7C6E5C8F3CB0A4D04A3E693D1DD4D8F894E910D9A999DDABE0427A1AB0C715C5A695A69140B20B9DA1195E6C9536B5DF24B4D45ED24D0F2C276E3CF48066EFB977C2B7096B02EB52309D916BD432347D72799BF9D76A03D54DE211460017C0E268BC9E23B415ABF46EB8B939B5A413EBD3F20E95F704E1F2CDCDF974A8743923DBC6D8363DC8948BE85EF1D368CD3EABDBE5B82648D2F676EB310D7B77465D3A14B86050463E43AC745F3781E7A6F582BD7B8AB22BC4EEECD2CB155E6E0B2604843E3906D47EBDA2C10B6D8BFCBB5722CE5394EB50721E90EFD28C63A62269C8C14593D69076D0F198D2BDCCB6D753CB81C4BED56A90E2DDBFFC0B9076C65F973B5EA3242E71E3CBBFE0976CFE22475F56726058D2D0CE3BD52AA940A0F559DD055BE9A6F50846902E02B70DB4FF5BED33762E10409D25ABDACF661BD9BA2A22212E02893A1625CA44850887B4B3A00D0AF63645E2EC42333035062090E8E7E63037C692FBA0B3FC7F3686FC2831F4DE2D4D82CF6FD6321D6621C8227715E3772EE8805911AA9E67083C511F17863C4D6F2C29E19CF329200024E539A7C5BF1A9D601AFF8DB7CFD75C6532488469E44BAC7266A3C127720E640328F9970B75509E292CCEC0B55A1F729456CB2804BE50451185F8CDA313C7D4DF6C1C67D6C411025A2BFFF06C5062470F97B17E75B4F81CD1FEC777465D684849809B4281B690D2A8FE5C4FA87DB00328630FC31BFDDA4641B29CB434147806A614E450E3E2B50317E3B4EE6262A2D4D0A8FE7530CEDFBCB5016C4D6E61C34E61AFA324871A9C75F9BC6BF6C92B95910C9D0FE049AEEF2E96E4C9E69E1FCE1F6CC687D533668F55367E2695197BE392A7FE66C4F88C0B1A9DEC6DFF682675855979DEA2A5644748DD882CE1F0D8FDA8530617BAA130AD9C16ABF8D76B5853104AD2E0C54C9639C3F6E1343AC94139621245EE8E12CA4366A6EC752BD9D1A0948CCC3626CEDB882BA4638115BBF55444DD4544EEC561F0E762C9989A9306D4749ABD47C31F40AD3F735FEEE6E1FDCEB626073CD5F76730B348103B041B9EEB941EFA61581DD9278802A2934C33FF0668C25CEF2546C44263A68919ECBB540B4A18E1867EA15C9F7A2853F55EFBB01C3D27D28579E030D0A771B754680FCD46B56EBD3431C24F202A343E20294076E56A09FA5F6C3E844DAF5BDCFBFF55CCC3FDDDB060FBC680BA520153098E57FC7741D77DFA8932F9028D8E0E66600974A41DAC5BBA4690407AC36EC206655ADCECC8AA0471601F67C3DF48B830585FA15C52061C4FF958453B1E75626120CDC0ADCE44743027FA4C59C1931E90726CD2BE240D0DC6D61CDE5165350D86FFF17260A823C0AE3467A597D774A67BE843951975E17BC1CB69DC8A0C7BFF799FB8FD2BDB37853D2EB28C9B7B8A2212FC73FDF2F21FF3FBCD798533FC4867739E48BA061B174BAC224064F3E867A1CF52E091FDD36871955FBEA90CD3D23B1BF0039930E0636080E6A36206ED5DD1CE4546EC0B0802BBEE2869DCCAEA01B8FC3A6392820180AA4D99AB67C57E8FD0874E7C54BBC7B9A2AA4D1EA4ADC1A2802DF908AF74F915AF98EEEBF822AC958CD0D9AF5A754AB2F4790225F18864A94734E526BDE497FF21F3392472D4F0E3B7E2EE97DDCA15060BF35A05E2593418809D3C9738C328EB4D44F35E6C913069096B0742809F55F01D06D40EB0476C34950FDAEF9BD2CC1F7653B4BCF1AA304963530C8F0C39697EAD32ADF464E3CAC931D33992B357A3A231FB978A56C3592A61411A5428C3549A991D811#)
+ (e #010001#)
+ )
+ )
+ (private-key
+ (rsa
+ (n #00D6007A7AD47BB8D6B356E4F24DFAEE3A722FEE77F7E9547F866CB369C233E6CB3916D416973E3157B4DC1837E6D4C907D1063855735EAA857176A7DA3CA9F378FF7AE9EF227C193965F106F35DB2A833D2760CF9F2D041938CD310D9CE38EDD179C33EBC4963A02221D8000FDBF4BEB592CAB1ED1EEEC9D6916F27263C76DE70184F5399DE3B3862227346B1B3FBA306174D08BEC3675E2593CFD42159655B0BE1A2B69C2BA9F4F03B8C6BA505F6BFDFC6163D74F42A6D4908284D6879CECCF6512F9225612E3030ACF3663DFB77B41AFFCFC70BC11B224E14B397D25AC15E4E342B34B363056EA76CB0265DBD41F733C7FDE98B7C2340289E338CD31F993ABFACA6E83B54BCB50DD1DD11165C188C80EBDA190A11B6D8982CDA1B6B9D1631AA3EACC93040237831A52D15826A0D3E92C833D0234975C92A7236F902FA6703C89C7779765020C11F714B4C9D33B76CD466DC2BE9A102488B0635F31E6FBB9282E5139D32623E10ED9C295DA3B39F68227218EDF8C6FE9372F174AE1DE5BBE7B0AF09A869CAEDBEFE05458BFD43CF32F10F5C345A2E3D588C8C16B4DA8B44FA9539C679B81133A35498696F5D866E3B6A89811AEA7BFD1BF690EC329D87989CDADA7EAB106785D2D6661BD400D76C113E28F13FD883027E1CAC848B13750C7CCD530273C165BDFDA93E6F72897E97F003308704B95801F223EE89160786B1DE440BA9C1F371CBA37E5B09650CDB3AA1ABAA237AD15B89DCD03390A28308643E219490BEC83403F6A09B94F81D7BB391C121FC9028A6908E5B287AC79209B905B33724B1869A679CB347BF192D80D2D66CF1DAEFEBBF22CEDB8CEC010D6F8D86CD055ED71425DA72DF1C07A573E6F070235C378DAB5404ED004B4946CCDA4786ACBBF379A47CC36A049C50651CA4B1CEF03EE87DB6D2484C3D10AF71798A6AD1E20780814F79348D45BD1004880D2DEDEBD152694C80B9F93DF32F5930911DB379B4CBB9230CFC5FC126B9B77F074B9C82BDB4F12471B3FE92079525FD276293B63B978B55E039024EE688180D7C7C6C094B754AB9B652AC31812F2F7E45EF2B6D4478D7C6E5C8F3CB0A4D04A3E693D1DD4D8F894E910D9A999DDABE0427A1AB0C715C5A695A69140B20B9DA1195E6C9536B5DF24B4D45ED24D0F2C276E3CF48066EFB977C2B7096B02EB52309D916BD432347D72799BF9D76A03D54DE211460017C0E268BC9E23B415ABF46EB8B939B5A413EBD3F20E95F704E1F2CDCDF974A8743923DBC6D8363DC8948BE85EF1D368CD3EABDBE5B82648D2F676EB310D7B77465D3A14B86050463E43AC745F3781E7A6F582BD7B8AB22BC4EEECD2CB155E6E0B2604843E3906D47EBDA2C10B6D8BFCBB5722CE5394EB50721E90EFD28C63A62269C8C14593D69076D0F198D2BDCCB6D753CB81C4BED56A90E2DDBFFC0B9076C65F973B5EA3242E71E3CBBFE0976CFE22475F56726058D2D0CE3BD52AA940A0F559DD055BE9A6F50846902E02B70DB4FF5BED33762E10409D25ABDACF661BD9BA2A22212E02893A1625CA44850887B4B3A00D0AF63645E2EC42333035062090E8E7E63037C692FBA0B3FC7F3686FC2831F4DE2D4D82CF6FD6321D6621C8227715E3772EE8805911AA9E67083C511F17863C4D6F2C29E19CF329200024E539A7C5BF1A9D601AFF8DB7CFD75C6532488469E44BAC7266A3C127720E640328F9970B75509E292CCEC0B55A1F729456CB2804BE50451185F8CDA313C7D4DF6C1C67D6C411025A2BFFF06C5062470F97B17E75B4F81CD1FEC777465D684849809B4281B690D2A8FE5C4FA87DB00328630FC31BFDDA4641B29CB434147806A614E450E3E2B50317E3B4EE6262A2D4D0A8FE7530CEDFBCB5016C4D6E61C34E61AFA324871A9C75F9BC6BF6C92B95910C9D0FE049AEEF2E96E4C9E69E1FCE1F6CC687D533668F55367E2695197BE392A7FE66C4F88C0B1A9DEC6DFF682675855979DEA2A5644748DD882CE1F0D8FDA8530617BAA130AD9C16ABF8D76B5853104AD2E0C54C9639C3F6E1343AC94139621245EE8E12CA4366A6EC752BD9D1A0948CCC3626CEDB882BA4638115BBF55444DD4544EEC561F0E762C9989A9306D4749ABD47C31F40AD3F735FEEE6E1FDCEB626073CD5F76730B348103B041B9EEB941EFA61581DD9278802A2934C33FF0668C25CEF2546C44263A68919ECBB540B4A18E1867EA15C9F7A2853F55EFBB01C3D27D28579E030D0A771B754680FCD46B56EBD3431C24F202A343E20294076E56A09FA5F6C3E844DAF5BDCFBFF55CCC3FDDDB060FBC680BA520153098E57FC7741D77DFA8932F9028D8E0E66600974A41DAC5BBA4690407AC36EC206655ADCECC8AA0471601F67C3DF48B830585FA15C52061C4FF958453B1E75626120CDC0ADCE44743027FA4C59C1931E90726CD2BE240D0DC6D61CDE5165350D86FFF17260A823C0AE3467A597D774A67BE843951975E17BC1CB69DC8A0C7BFF799FB8FD2BDB37853D2EB28C9B7B8A2212FC73FDF2F21FF3FBCD798533FC4867739E48BA061B174BAC224064F3E867A1CF52E091FDD36871955FBEA90CD3D23B1BF0039930E0636080E6A36206ED5DD1CE4546EC0B0802BBEE2869DCCAEA01B8FC3A6392820180AA4D99AB67C57E8FD0874E7C54BBC7B9A2AA4D1EA4ADC1A2802DF908AF74F915AF98EEEBF822AC958CD0D9AF5A754AB2F4790225F18864A94734E526BDE497FF21F3392472D4F0E3B7E2EE97DDCA15060BF35A05E2593418809D3C9738C328EB4D44F35E6C913069096B0742809F55F01D06D40EB0476C34950FDAEF9BD2CC1F7653B4BCF1AA304963530C8F0C39697EAD32ADF464E3CAC931D33992B357A3A231FB978A56C3592A61411A5428C3549A991D811#)
+ (e #010001#)
+ (d #0125A7ED14E014111AE2BD8FD81A69B0BDED886DBE477D9CC08C6B07F1F82BD5BD73797FF9FFFB0D2542BE97FD1DCE9FE30F516F117DB449B513C85EF779DC91DD57B6B2E1BDD077A1EBE148486C2ADC8FEAC7FE1BD40BDD45E6833B26FB75388D05293177EE12678B197B42EFD59A38985B4BB471A3761E41F1BA8AB3A2AFA5A241B999096B8A9809BC7C5DBB3BDF476049AE7671A47213C9922B7E4C1A5545BA92C555100DB00AB77254C8E1DFC283F3EDE901819B611CD5E551133D14E8FD18840F6331D29E2EED47118E7094C1E36E53DA2AA90133856A351367224B51F80C184A5C3C4CDA5CC822126B3DF696AE96BFB8B836FA56E4E8D7D8A545E5F668F23203AC6968BE0A8A0C3BEEAC0AC9CFAE994B8EA5E293A5B9817D49B89761528595BB99D83C2B1AA4054FA2FF1D1D4F8ADBC3E863D8F4BD8C76C38E057D81740FB4FB12BC3CF80AB510223934FE8D3FD461D17E9B4EA07380A7E5202DD93A40B1F2E6C2048160949A247AC9A3F962A4E2F4EE809F00C76AF8DA4737D1398E6A95BE4637C949A33492C9691B254EB239EA7B1EC0E2D4261A27183F90577F04B356FD10FDB5E23A4471068952930EAFF4EDC757ACE25781DBA807A0C153FEDDCCB55A08B774AB44AD2CC75BD319D4822BFC6AF24C9F837C72D1A615109882906ADC2B2C679630A6FAE363144B77A134F2856DF1D8E9A77AAEC08A72FD67C122BD280D591A6C4045D0497362FB91C8C38C00A457A0BBE8D625210E4BF55FC4041FBE0A1515B70EA98C4F4284B3C96C15DD21C8CC15305DB5BFA2C21EB9520C9ACD823F5E7ABCDA3993D89A7B561C101FEC08A8AE6621245CAA1406D7536FBC6E835692D2B1BB540B8F2B2EAB7A1406B2FE83873CDEDFB0A0E717A037CD3A6322AE0B6F5E36187646866A0D406F7F54007FEC9711311BCE87FC6B4F44C5D1F7BEDD2DED081F1439F38312FA27CB65665B1595F88713378797AB624C728CA6632653EC8E762A76E3E597AAD4C3C3FE41648AEB07FCB8BFE9C70A1818E4F1B19124BECD320E4CDA6A9FD02B0A422114B5DE31376549C3B5FE1A896F8FCB256B814BD100FFBF5359510D8FD243DB014DBCFA3036C857A41DCBAE29ECE25012AF0B88827A1B3F3FA6E75EDF1790B0CCD794D0E733315743EF50FA18E5E1A93DC5D1EB28F555C0A8541B729954EA1865C6FDBE810D153CB50C024E8E7A59D324C22B626A30F4AAA0FA46EDCA4CC42F4B2D033B147DA54A67D103633A88EBFFF0EFFB61AA98DD2B057700FAC0986A9FB27C2CC29EE30B699DE063C76A1E2863D13951C35C5ABD357555781D2A5E68B0BFACF2E11747006E0810DD0CF97D585318A3B0DF7A67465EC3761004AA9B7C141B4E5444D9EB649BBD94F983FCFAEC982D994C7A620DE2D8AEE012BFD22AA9322F3DD3BD5CE90C17660D18F8CB679B02BDABC0D4D0F876B0DD6D258E08DC50B35544A4BDBE5F75604493D5FDF98B7FB812475C7B7122DDEC512322E1855C31105397AF284B7C2B4DC315D2E8D5017BBD2400885D5EB6C321B5093EF98A14EB4C29DC2B7DF9565D9E23A2BE6A2E334CE3485A8677E463DD86F49E3B56D2974F7D930FFDD60BA54AB49AE9DCDC588A4FC7AFCDB89A7713C51FA97BFA8868EE207C3C0032FDB302E45DFA0DFD8A2DE4D50A959A424626CC92CE74F8A2627C3B20406D714BB64825FBAE1B44A2F7569E32A8CBB41DBA3D9115691B07C3951A2D1394BF06BB0690B710D438CCC4E5B92B0CF302109A60C836E3AB40E4D3579C2E1C77F432C62925A751D92A564440A563B3C373D9A46FFCB0EB478962450C11192FDE187718F9AEDFF61DFE39AE98714B1DD49C356593F23F2BADE0E753B25BDCB3D618E3CDC5C5D8F37974A449E6E2D21B46FA90435F399AFF98988DEA9048B8031E1AE2E4B2B7C3FBB7CA775B6058500DB8852FF7358251CBF12A3CA233719760D251939A29778D7B4BDE15B5244B8AB47555C18925222331443DD6B227282A101443F14851734796FF4E323B66ED8304E594973EDBCA57B9413BB83574673623CB9BA282F92FBE49BFC7D6D18644C1741DAFEE86B58A174AA1B576F2599CAA8F15C9AC513CCC3778EAB81D6EF90557E73E207E96BC2D83BD3FED3717B0E8E527AE27BD05B28962280099AD1AF8C8D0B0E668EDC73AB3BD7E21C9C9134BFBC837BBC8E68FE8DF48365491371D378CB768A24A956F0D625D47EDBBAB051E4B4EE5D59574A4F2C4371D491ED182CD945DDAF11EB17382A58F2AFB6B79DE3EA1636C67449340F77A1CF14DFEDCB42B18BFFB866DD007B9A606C7BAEA70F9C5EAC98CBB52ECE5932F2925ACEAAE412E6114090CB54145A751E430EF1CA3A418C4D76EA7D797FF882401FD21984F4FBC347C4BFBB5B2B946B65F4FE0C7B9E5E16CBC1B6612325A0C83A8740D5D885C443EB8D02289BFC72E3BD2925E599B863F117C8A32E81F4B6F2C0C5D8CEA6E75C03E564E3F8D1663B5F3D21D844B6F80FB982484809303019670115FF5CC5FF6681E1C9B9AB01EA719AECAB7A4C4F1044017449741C726A4D1D97D0FB0D390F37DE0F838038270AEDD5E058113252D4F8E91B1377FD24F528EEDF58AE575327BE17F4A9D68E24B39DDF1187C8ABCEB1A84AB6BAB0735F3756E512641E62D3B51DF0316D065949379DD06C07606A82126A129A2A70F91EF54096CFCBC3447B49D5DD5F7DC7AA9F3E86E8ECD581F51731DF726194CD3143CC1E608AE882EA8848CDFD9F3FF6282AC6D722C2D5F51F2652724FEB601E02E1F078E32B36892ED9E2DE0A637836A005B01CEFDADF90534049565E8B965224B3E7EFF5707F3DEB6BFDB8D8045549168CD1C81910E584978A555DE877CFBDCB8D7388D3081F9DD8067198562521FEE99C57E3401#)
+ (p #00E6BBCBEBFCA813CED7907F5FA73C4C2D3532AF7A7650C4A88563DF805D54D0CEA528347A60F703C8FFE91997505F8C238383E03C53DFD347D0E385A5CFFE4E2A944DD45ACBC481D54D7B22F4C59A2BFC8C686E527B907AD9C5ECE870D102550D8D4A02B404DAE7023CC8F0436DE5F50A7CE1F4E741147F676B5DC9437CB47727B93155AE5C1EBE236E9F436D723FF770104305A1460C21BE9361582CB107CAF036F0600BCC6D78DE73C1F44C25F377B9A65349E9B73C446E5C6281DCB68EC65AF684AB39F6C84CF96E4CD909B61FA2D8BBB69A7994B78865FFEACD3838F851C944039C2422B75AE4F9C2A1702B005641EB41CE9FB042757E86F6E9651428EBA4D908D60A99AD61E5D09E7A05C7E59A9615DE965200CFB75A228B1D5DB8D2C040A65D940D5516C9FC5069B94D2F1903EF6B07F70ECF3E8D720F74139A4647D79131835CF7F15EA839350A00BD9D733359503497BCE39A2497CACCAD41B0AE7A36E6F01FECA1B0B062F9C3232BD6F6734C97DB2EE7DE050370087210F8161B07237E712E29E0BA6B2B661EC22DAD0509D50EF75255D40B954D2B3694C30E5982EB2D15B72A8709BC4F9B6FEFC4E6F01FF04D128311209B4C4353AF43BDE58D631DB7E047D7B469CDE4A9176415F6F7B60BC129E0BA8363D77140DA0FC685DB76FCB968C8C58657DB86FC908E7D11F41D907CA367C17AD3EF81280721FD40E6AEDC118538AFE109B8FA818502982578BB2EAE3ACC3028EF79C4CAB575CC8473E05FF5D911186058BCA7F5269FAA66CBB68CE47EE0748F2A8E52EEB8475030D34E54C365572BA282226730ADA3BEF16D582A409F15AB89188F1737F7CA82EE0D96BD2B3A2153CDF6EE27FD04F4AEF4AA3FC6EB02E92EC21359864507EAD3E09D3117EEDD61AF34265412C362EE01B26B925BC74A2679A1C112C653AE88FD124220742A05DDCB1CDE7DF2342669FDF76E1C3F9BF8FE13633ADF33050FA491FA708A918ECA787FF074E90F9A0AA216ACB1160D22BE7A9817DEF5FA2E27FC906ACF5B5774A5DB069A66F5F1752D7521E1CE49F8888218BA24C97A92C287DAB8B08B4433EC1DDEB5C7A3D96956EBCC46A2DE2B95CACA91BECBCB15DCA20D13C977F50F6DC0D705DB78B597581F850E20E0EEB6EA9A7A9D2708A650A3CD0748EE1DDAEA559C80D2A00F2DBD29C3EF58C13CB0B84AFA30C508BC8AD71A4FC3E14690F16B52943013D7BB09F5627D7D1343DAED88198D5ADA23E0B94484E06A827FE49227540110ECEC9E354F87E27496B7FF5ED31521817C8B5B182129EE0C521C85A996BF5EAF1F87583BDFE69386EDCE63337252F58D37AEC742702D97F3B97CE0FD06EE60EE4380C2E48D7AAD49E26F76CE981237ADB617B0BB6D59DAE31B6335866E7F9670F8A25D9BC44D7C32602BA3D58002BD53473E3F781249AB17C1C78C496611#)
+ (q #00ED6FA36442A240865581B5C398C93F55AFD5386A801AF1D8BF8A9FF8AB9C578F97DA02AFC44180AB581A1E10A4D65B7D1E9C0B04D57FB1FD13B7ECF1AD7C0296F1F5D52C3223CB117E6C6BED1FDA701A7A4079FBD35B180D2295B216FEAB284F85594EDDDC179C9AAE6E6CA545D2FBCA308B0961B21A1B4A4E9DE27CF8D6922FC902C35313B05F0FFB5CC667E64E0706D5210D3919074B384CA5968359CBA5F4BCA096323AD48E2CDE9D25F08E2EF945A1DD46457F48B4BF8EBEC2DC3737CC09E333E30F17A3ACD19567A5C5D200E7A4303A97A893F0884ED3CD976B41C2BCEEF04D9FCE4F30218CC9AAE26FFC5749B10011B805ADA9C4857B691A7A820F2564DF5979F570F8D524DDE268F702891E9CCDBD9F821C2ECF27F60C795E743AC67CADB07D22D3A7BD322DA9C3E7A35AC6A35333F871FE6DE0162BA2A1F9565E411ADD424FF0727B2280BAFDDF522C272D3A2910EFE27F5590F8E0F6C38BEA1895A0893755F44BDF41FECF3BF3BA27C6D6F036A1AA70736DE71465FA78EA46F34341E05DE7AB37A5074B4E99E0BAB54D658629503E1242ACAFDE08721058F208D3F62FAE742D158CAE521994514CF0BEC580F075046319FBFC8E971F0FF5EED9E2AB7D194CD21ECCCB2E54D239AA3B0254F4AB1641E6E8E1512FF1D1ED579205F1807898828CFA24B80D230FA6C52C6A92348E1A069C239F7B6F2E7CC995BD3CD1B413FC86C626FE962C7EC3CF19FA193B3A732AF17E51E6B57CC263DE82A5C45CB9E37C2BAB44E88E5792FBBC40748F86134D221BF775E2BF57F98A884FCBA494718661A3FF73AAE04A6F5CDFB6F143D680D09BA4CCD6C6F186E92569B8F67A35B5796B9F2ED404F11C54ED290D7D7836501473F06D8A623D53AB586B644F3F5BDF8B0B670CA696A23B7B52319C91D2CA27FCDF421030CEA8A6B079FEC2E467BE0427AFCEA12648F3E12F09745166D20D3D1CD6965EABA2732469C077B3C4E44D3503D882937DA5139076144FAEAB75083BF4E16725BB9625A99DE92F6F226DE343F73E974577F8D6F5B57CFBEB60763627240A28C90A73BC7A9E47D74CBABB6486988C983A5A3DC91A4D8E353AE6C608499D21391E32D8EF3A030925959B52C1BC03F17AAE2FEE4E28B2B51889EC2A5FE587C60139C9CF29AF46555B089B54B5EF0BB3B92BCC0EA0E6E94F944FE8E9741DDA902F185E0D16876A10351AD22FE6ABD4378AD74A13CD2BD9696F0B59A069CD6582F92458A89B15648E833614598D6FDABAC026791CDB3FA87873CA86E3DF7187C9230AFFB1089EF83B8CB54856ADC234D07DF479514A8F47EC9903930E47F9E93AE1F96D7358F4208E19CCED8AEC063AFE8A31E39C921F7D7867D57E8F68B1494793E6DCD06F3DC68809BD455DBB44076D6AB64586E0A09A42CEBA2829A5F81BDEA5EDB23DC251D69C373D2E275201#)
+ (u #00EA2CBBEFBFFBD4BD3850584AEA315F88ED892F7398E5C4ECD17F8E4588B073DA32AC708DADC0E55417553FB4DC25130F42A9A04E435C63E1091744232D53FF98ABA450E3B91AF512631E28BF453BE4FCB9713112F890F368523FE175B0909385F0B404B3E6370FA6DB33490DC216CE3DE548FDDF68C81FE49BB9683C30FA6D1DE8B019A94683E508B720F2EDA20133325FD4644620D086182F1E8283215D2BCBBC1B302DEA714CE1E59FE8E996489018078F8CCDDAEF086EFDF82BA45DF424E539ABC9D61ABEC14346275AA9256031514AAC59FF40C7D1B4363AC7C74E8CC3854C9E57F6913C2CFC599E9DBB446D553482C9B531563A7CADD562D64151B3961FB52A0D542406D491F8090EBB737C388016F95918313C0EC987F701F2A25AE3F0CEF2B9F3460A9E48AEE382F01CB09B0A9372104FAC2EE692BB2B14E6FE376A29891687E157C40F09FB3283402E4D319C9791E7A06025C542B4411EEA71890D22E34E8038B3002AC7FB75A50ED29AAEAFF36588950A06A8D2139B0420673DCB37087E8196A034D0A5C78A824BCD0A74BBD7E08B04B8F08F473C09F6350508CEB476DEE1E41D0CF960CA3E87AE8489811577F7D49CB1EF885453F7087B8126FB99028B5771EC9E159040109102DEE175DAFA038EE7B62B96797E56E6361C37DFC42398020114765E28C3F3B4B6A4C33A86A995A0D5647068B7147552F4E6130866527D4833949E9F9204406F096735F33BFD1BB57734E15D0B4035A37CCA7C897C18162B12951A684F586F1B7FF041A85B7F44FAC125A80AC782AD3F4D7EC52C318EEA52CFA6AF09EBA50813B5BAC8367B1FF80A99DB8BDEC3E3842455A06D22DA99F0BE5B52330D1D5C0CCACB3661D703BE1D96E7832A159C8858E08CC23101FBC0DE783D3209A80A3ED4EBCF57661B01D84EBCFBE70A0EC921588B8CD9B9BF21918D86C3C97B0F6BBF4037E80C99A349A1A2B78F337CC4029415FF0DB54AC9A3A1DF7E07482DC9F04E638C9D5BBAAD32A627F2EF1DC3E17AEC365E416C703C449AA40104DEC358202F7F78CCF77115ADAD567CDAE6B4B2C81DA4FBE6A97BBF2A704389911E4A5B39C3C1F187101E53B3DF7A0CE05C4B7956F4ED31DD225B46036C5344B3CDB236E5B1A12E159008D106D1CF6C14C5F7335A4A5D80E008F0106F636EF750723B50511F37B3BA6FFBEB27A270828B9CB123D7F59EA0BE956C0D024C77AC06086460998F18610ECB94651DF47AB37DDDCDB9797203A4321CBC1E6E85EC64919EB74AC7E2F3C15FEB5DFCCFC2359D353C8B6B600152D4211A55477FF31026B34C10C5F1FC1A1DD1C1EF6A14B26CFD1AF70D6BAA4461B4387631E4DCFDFFAB118F710A8B8B2D12EEC4924751720B9AA9D94527B9F19E8B352222567F662FC6753AA4BE22C2A851F2378AD5EE5539C1E0F4DD90400DD7DC6F1EA675D9#)
+ )
+ )
+ )