Mercurial > hg > octave-lyh > gnulib-hg
changeset 6348:55961a93440c
Add generic crypto module.
author | Simon Josefsson <simon@josefsson.org> |
---|---|
date | Sat, 08 Oct 2005 08:33:07 +0000 |
parents | 37dcafa40bf9 |
children | c195d0c75e25 |
files | lib/gc-gnulib.c lib/gc-libgcrypt.c lib/gc.h m4/gc.m4 modules/gc modules/gc-tests tests/test-gc.c |
diffstat | 7 files changed, 775 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/lib/gc-gnulib.c @@ -0,0 +1,153 @@ +/* gc-gl-common.c --- Common gnulib internal crypto interface functions + * Copyright (C) 2002, 2003, 2004, 2005 Simon Josefsson + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Note: This file is only built if GC uses internal functions. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> + +/* Get prototype. */ +#include <gc.h> + +/* For randomize. */ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include <string.h> + +int +gc_init (void) +{ + return 0; +} + +void +gc_done (void) +{ + return; +} + +/* Randomness. */ + +static int +randomize (int level, char *data, size_t datalen) +{ + int fd; + const char *device; + size_t len = 0; + int rc; + + switch (level) + { + case 0: + device = NAME_OF_NONCE_DEVICE; + break; + + case 1: + device = NAME_OF_PSEUDO_RANDOM_DEVICE; + break; + + default: + device = NAME_OF_RANDOM_DEVICE; + break; + } + + fd = open (device, O_RDONLY); + if (fd < 0) + return GC_RANDOM_ERROR; + + do + { + ssize_t tmp; + + tmp = read (fd, data, datalen); + + if (tmp < 0) + { + int save_errno = errno; + close (fd); + errno = save_errno; + return GC_RANDOM_ERROR; + } + + len += tmp; + } + while (len < datalen); + + rc = close (fd); + if (rc < 0) + return GC_RANDOM_ERROR; + + return GC_OK; +} + +int +gc_nonce (char *data, size_t datalen) +{ + return randomize (0, data, datalen); +} + +int +gc_pseudo_random (char *data, size_t datalen) +{ + return randomize (1, data, datalen); +} + +int +gc_random (char *data, size_t datalen) +{ + return randomize (2, data, datalen); +} + +/* Memory allocation. */ + +void +gc_set_allocators (gc_malloc_t func_malloc, + gc_malloc_t secure_malloc, + gc_secure_check_t secure_check, + gc_realloc_t func_realloc, gc_free_t func_free) +{ + return; +} + +#include "md5.h" + +int +gc_md5 (const void *in, size_t inlen, void *resbuf) +{ + md5_buffer (in, inlen, resbuf); + return 0; +} + +#include "hmac.h" + +int +gc_hmac_md5 (const void *key, size_t keylen, + const void *in, size_t inlen, char *resbuf) +{ + hmac_md5 (key, keylen, in, inlen, resbuf); + return 0; +}
new file mode 100644 --- /dev/null +++ b/lib/gc-libgcrypt.c @@ -0,0 +1,165 @@ +/* gc-libgcrypt.c --- Crypto wrappers around Libgcrypt for GC. + * Copyright (C) 2002, 2003, 2004, 2005 Simon Josefsson + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +/* Note: This file is only built if GC uses Libgcrypt. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* Get prototype. */ +#include "gc.h" + +/* Get libgcrypt API. */ +#include <gcrypt.h> + +#include <assert.h> + +/* Initialization. */ + +int +gc_init (void) +{ + gcry_error_t err; + + err = gcry_control (GCRYCTL_ANY_INITIALIZATION_P); + if (err == GPG_ERR_NO_ERROR) + { + if (gcry_check_version (GCRYPT_VERSION) == NULL) + return GC_INIT_ERROR; + + err = gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL, 0); + if (err != GPG_ERR_NO_ERROR) + return GC_INIT_ERROR; + } + + return GC_OK; +} + +void +gc_done (void) +{ + return; +} + +/* Randomness. */ + +int +gc_nonce (char *data, size_t datalen) +{ + gcry_create_nonce ((unsigned char *) data, datalen); + return GC_OK; +} + +int +gc_pseudo_random (char *data, size_t datalen) +{ + gcry_randomize ((unsigned char *) data, datalen, GCRY_STRONG_RANDOM); + return GC_OK; +} + +int +gc_random (char *data, size_t datalen) +{ + gcry_randomize ((unsigned char *) data, datalen, GCRY_VERY_STRONG_RANDOM); + return GC_OK; +} + +/* Memory allocation. */ + +void +gc_set_allocators (gc_malloc_t func_malloc, + gc_malloc_t secure_malloc, + gc_secure_check_t secure_check, + gc_realloc_t func_realloc, gc_free_t func_free) +{ + gcry_set_allocation_handler (func_malloc, secure_malloc, secure_check, + func_realloc, func_free); +} + +/* One-call interface. */ + +int +gc_md5 (const void *in, size_t inlen, void *resbuf) +{ + size_t outlen = gcry_md_get_algo_dlen (GCRY_MD_MD5); + gcry_md_hd_t hd; + gpg_error_t err; + unsigned char *p; + + assert (outlen == 16); + + err = gcry_md_open (&hd, GCRY_MD_MD5, 0); + if (err != GPG_ERR_NO_ERROR) + return GC_INVALID_HASH; + + gcry_md_write (hd, in, inlen); + + p = gcry_md_read (hd, GCRY_MD_MD5); + if (p == NULL) + { + gcry_md_close (mdh); + return GC_INVALID_HASH; + } + + memcpy (resbuf, p, outlen); + + gcry_md_close (hd); + + return GC_OK; +} + +int +gc_hmac_md5 (const void *key, size_t keylen, + const void *in, size_t inlen, char *resbuf) +{ + size_t hlen = gcry_md_get_algo_dlen (GCRY_MD_MD5); + gcry_md_hd_t mdh; + unsigned char *hash; + gpg_error_t err; + + assert (hlen == 16); + + err = gcry_md_open (&mdh, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC); + if (err != GPG_ERR_NO_ERROR) + return GC_INVALID_HASH; + + err = gcry_md_setkey (mdh, key, keylen); + if (err != GPG_ERR_NO_ERROR) + { + gcry_md_close (mdh); + return GC_INVALID_HASH; + } + + gcry_md_write (mdh, in, inlen); + + hash = gcry_md_read (mdh, GCRY_MD_MD5); + if (hash == NULL) + { + gcry_md_close (mdh); + return GC_INVALID_HASH; + } + + memcpy (resbuf, hash, hlen); + + gcry_md_close (mdh); + + return GC_OK; +}
new file mode 100644 --- /dev/null +++ b/lib/gc.h @@ -0,0 +1,203 @@ +/* gc.h --- Header file for implementation agnostic crypto wrapper API. + * Copyright (C) 2002, 2003, 2004, 2005 Simon Josefsson + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef GC_H +# define GC_H + +/* Get size_t. */ +# include <stddef.h> + +#define GC_MD5_DIGEST_SIZE 16 + +enum Gc_rc + { + GC_OK = 0, + GC_MALLOC_ERROR, + GC_INIT_ERROR, + GC_RANDOM_ERROR, + GC_INVALID_CIPHER, + GC_INVALID_HASH, + GC_PKCS5_INVALID_ITERATION_COUNT, + GC_PKCS5_INVALID_DERIVED_KEY_LENGTH, + GC_PKCS5_DERIVED_KEY_TOO_LONG + }; +typedef enum Gc_rc Gc_rc; + +extern int gc_init (void); +extern void gc_done (void); + +/* Memory allocation (avoid). */ +typedef void *(*gc_malloc_t) (size_t n); +typedef int (*gc_secure_check_t) (const void *); +typedef void *(*gc_realloc_t) (void *p, size_t n); +typedef void (*gc_free_t) (void *); +extern void gc_set_allocators (gc_malloc_t func_malloc, + gc_malloc_t secure_malloc, + gc_secure_check_t secure_check, + gc_realloc_t func_realloc, + gc_free_t func_free); + +/* One-call interface. */ +extern int gc_md5 (const void *in, size_t inlen, void *resbuf); +extern int gc_hmac_md5 (const void *key, size_t keylen, + const void *in, size_t inlen, + char *resbuf); + +/* + TODO: + + From: Simon Josefsson <jas@extundo.com> + Subject: Re: generic crypto + Newsgroups: gmane.comp.lib.gnulib.bugs + Cc: bug-gnulib@gnu.org + Date: Fri, 07 Oct 2005 12:50:57 +0200 + Mail-Copies-To: nobody + + Paul Eggert <eggert@CS.UCLA.EDU> writes: + + > Simon Josefsson <jas@extundo.com> writes: + > + >> * Perhaps the /dev/*random reading should be separated into a separate + >> module? It might be useful outside of the gc layer too. + > + > Absolutely. I've been meaning to do that for months (for a "shuffle" + > program I want to add to coreutils), but hadn't gotten around to it. + > It would have to be generalized a bit. I'd like to have the file + > descriptor cached, for example. + + I'll write a separate module for that part. + + I think we should even add a good PRNG that is re-seeded from + /dev/*random frequently. GnuTLS can need a lot of random data on a + big server, more than /dev/random can supply. And /dev/urandom might + not be strong enough. Further, the security of /dev/*random can also + be questionable. + + >> I'm also not sure about the names of those functions, they suggest + >> a more higher-level API than what is really offered (i.e., the + >> names "nonce" and "pseudo_random" and "random" imply certain + >> cryptographic properties). + > + > Could you expand a bit more on that? What is the relationship between + > nonce/pseudorandom/random and the /dev/ values you are using? + + There is none, that is the problem. + + Applications generally need different kind of "random" numbers. + Sometimes they just need some random data and doesn't care whether it + is possible for an attacker to compute the string (aka a "nonce"). + Sometimes they need data that is very difficult to compute (i.e., + computing it require inverting SHA1 or similar). Sometimes they need + data that is not possible to compute, i.e., it wants real entropy + collected over time on the system. Collecting the last kind of random + data is very expensive, so it must not be used too often. The second + kind of random data ("pseudo random") is typically generated by + seeding a good PRNG with a couple of hundred bytes of real entropy + from the "real random" data pool. The "nonce" is usually computed + using the PRNG as well, because PRNGs are usually fast. + + Pseudo-random data is typically used for session keys. Strong random + data is often used to generate long-term keys (e.g., private RSA + keys). + + Of course, there are many subtleties. There are several different + kind of nonce:s. Sometimes a nonce is just an ever-increasing + integer, starting from 0. Sometimes it is assumed to be unlikely to + be the same as previous nonces, but without a requirement that the + nonce is possible to guess. MD5(system clock) would thus suffice, if + it isn't called too often. You can guess what the next value will be, + but it will always be different. + + The problem is that /dev/*random doesn't offer any kind of semantic + guarantees. But applications need an API that make that promise. + + I think we should do this in several steps: + + 1) Write a module that can read from /dev/*random. + + 2) Add a module for a known-good PRNG suitable for random number + generation, that can be continuously re-seeded. + + 3) Add a high-level module that provide various different randomness + functions. One for nonces, perhaps even different kind of nonces, + one for pseudo random data, and one for strong random data. It is + not clear whether we can hope to achieve the last one in a portable + way. + + Further, it would be useful to allow users to provide their own + entropy source as a file, used to seed the PRNG or initialize the + strong randomness pool. This is used on embedded platforms that + doesn't have enough interrupts to hope to generate good random data. + + > For example, why not use OpenBSD's /dev/arandom? + + I don't trust ARC4. For example, recent cryptographic efforts + indicate that you must throw away the first 512 bytes generated from + the PRNG for it to be secure. I don't know whether OpenBSD do this. + Further, I recall some eprint paper on RC4 security that didn't + inspire confidence. + + While I trust the random devices in OpenBSD more than + Solaris/AIX/HPUX/etc, I think that since we need something better on + Solaris/AIX/HPUX we'd might as well use it on OpenBSD or even Linux + too. + + > Here is one thought. The user could specify a desired quality level + > range, and the implementation then would supply random data that is at + > least as good as the lower bound of the range. I.e., ihe + > implementation refuses to produce any random data if it can't generate + > data that is at least as good as the lower end of the range. The + > upper bound of the range is advice from the user not to be any more + > expensive than that, but the implementation can ignore the advice if + > it doesn't have anything cheaper. + + I'm not sure this is a good idea. Users can't really be expected to + understand this. Further, applications need many different kind of + random data. Selecting the randomness level for each by the user will + be too complicated. + + I think it is better if the application decide, from its cryptographic + requirement, what entropy quality it require, and call the proper API. + Meeting the implied semantic properties should be the job for gnulib. + + >> Perhaps gc_dev_random and gc_dev_urandom? + > + > To some extent. I'd rather insulate the user from the details of + > where the random numbers come from. On the other hand we need to + > provide a way for applications to specify a file that contains + > random bits, so that people can override the defaults. + + Agreed. + + This may require some thinking before it is finalized. Is it ok to + install the GC module as-is meanwhile? Then I can continue to add the + stuff that GnuTLS need, and then come back to re-working the + randomness module. That way, we have two different projects that use + the code. GnuTLS includes the same randomness code that was in GNU + SASL and that is in the current gc module. I feel much more + comfortable working in small steps at a time, rather then working on + this for a long time in gnulib and only later integrate the stuff in + GnuTLS. + + Thanks, + Simon + */ + +#endif /* GC_H */
new file mode 100644 --- /dev/null +++ b/m4/gc.m4 @@ -0,0 +1,102 @@ +# gc.m4 serial 1 +dnl Copyright (C) 2005 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_GC], +[ + AC_LIBSOURCES([gc.h, gc-gnulib.c, gc-libgcrypt.c]) + AC_ARG_WITH(libgcrypt, + AS_HELP_STRING([--with-libgcrypt], [use libgcrypt for low-level crypto]), + libgcrypt=$withval, libgcrypt=no) + if test "$libgcrypt" != no; then + AC_LIB_HAVE_LINKFLAGS([gcrypt]) + fi + if test "$ac_cv_libgcrypt" = yes; then + AC_CHECK_HEADER(gcrypt.h) + AC_LIBOBJ([gc-libgcrypt]) + else + AC_LIBOBJ([gc-gnulib]) + gl_MD5 + gl_MEMXOR + gl_HMAC_MD5 + + # Devices with randomness. + # FIXME: Are these the best defaults? + + case "${target}" in + *-openbsd*) + NAME_OF_RANDOM_DEVICE="/dev/srandom" + NAME_OF_PSEUDO_RANDOM_DEVICE="/dev/prandom" + NAME_OF_NONCE_DEVICE="/dev/urandom" + ;; + + *-netbsd*) + NAME_OF_RANDOM_DEVICE="/dev/srandom" + NAME_OF_PSEUDO_RANDOM_DEVICE="/dev/urandom" + NAME_OF_NONCE_DEVICE="/dev/urandom" + ;; + + *-solaris* | *-irix* | *-dec-osf* ) + NAME_OF_RANDOM_DEVICE="/dev/random" + NAME_OF_PSEUDO_RANDOM_DEVICE="/dev/random" + NAME_OF_NONCE_DEVICE="/dev/random" + ;; + + *) + NAME_OF_RANDOM_DEVICE="/dev/random" + NAME_OF_PSEUDO_RANDOM_DEVICE="/dev/urandom" + NAME_OF_NONCE_DEVICE="/dev/urandom" + ;; + esac + + AC_MSG_CHECKING([device with (strong) random data...]) + AC_ARG_ENABLE(random-device, + AC_HELP_STRING([--enable-random-device], + [device with (strong) randomness (for Nettle)]), + NAME_OF_RANDOM_DEVICE=$enableval) + AC_MSG_RESULT($NAME_OF_RANDOM_DEVICE) + + AC_MSG_CHECKING([device with pseudo random data...]) + AC_ARG_ENABLE(pseudo-random-device, + AC_HELP_STRING([--enable-pseudo-random-device], + [device with pseudo randomness (for Nettle)]), + NAME_OF_PSEUDO_RANDOM_DEVICE=$enableval) + AC_MSG_RESULT($NAME_OF_PSEUDO_RANDOM_DEVICE) + + AC_MSG_CHECKING([device with unpredictable data for nonces...]) + AC_ARG_ENABLE(nonce-device, + AC_HELP_STRING([--enable-nonce-device], + [device with unpredictable nonces (for Nettle)]), + NAME_OF_NONCE_DEVICE=$enableval) + AC_MSG_RESULT($NAME_OF_NONCE_DEVICE) + + if test "$cross_compiling" != yes; then + AC_CHECK_FILE($NAME_OF_RANDOM_DEVICE,, AC_MSG_ERROR([[ + *** Device for (strong) random data $NAME_OF_RANDOM_DEVICE does not exist + ]])) + AC_CHECK_FILE($NAME_OF_PSEUDO_RANDOM_DEVICE,, AC_MSG_ERROR([[ + *** Device for pseudo-random data $NAME_OF_PSEUDO_RANDOM_DEVICE does not exist + ]])) + AC_CHECK_FILE($NAME_OF_NONCE_DEVICE,, AC_MSG_ERROR([[ + *** Device for unpredictable nonces $NAME_OF_NONCE_DEVICE does not exist + ]])) + else + AC_MSG_NOTICE([[Cross compiling, assuming random devices exists...]]) + fi + + # FIXME: Open+read 42 bytes+close twice and compare data. Should differ. + + AC_DEFINE_UNQUOTED(NAME_OF_RANDOM_DEVICE, "$NAME_OF_RANDOM_DEVICE", + [defined to the name of the (strong) random device]) + AC_DEFINE_UNQUOTED(NAME_OF_PSEUDO_RANDOM_DEVICE, + "$NAME_OF_PSEUDO_RANDOM_DEVICE", + [defined to the name of the pseudo random device]) + AC_DEFINE_UNQUOTED(NAME_OF_NONCE_DEVICE, "$NAME_OF_NONCE_DEVICE", + [defined to the name of the unpredictable nonce device]) + fi +]) + +# Prerequisites of lib/gc.h +AC_DEFUN([gl_PREREQ_GC], [:])
new file mode 100644 --- /dev/null +++ b/modules/gc @@ -0,0 +1,37 @@ +Description: +Core files for generic crypto package + +Files: +lib/gc.h +lib/gc-libgcrypt.c +lib/gc-gnulib.c +m4/gc.m4 +lib/md5.h +lib/md5.c +m4/md5.m4 +m4/uint32_t.m4 +lib/hmac.h +lib/hmac-md5.c +m4/hmac-md5.m4 +lib/memxor.h +lib/memxor.c +m4/memxor.m4 + +Depends-on: +havelib +restrict + +configure.ac: +gl_GC + +Makefile.am: +lib_LIBADD += $(LIBGCRYPT) + +Include: +"gc.h" + +License: +LGPL + +Maintainer: +Simon Josefsson
new file mode 100644 --- /dev/null +++ b/modules/gc-tests @@ -0,0 +1,11 @@ +Files: +tests/test-gc.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-gc +noinst_PROGRAMS += test-gc +test_gc_SOURCES = test-gc.c
new file mode 100644 --- /dev/null +++ b/tests/test-gc.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005 Free Software Foundation + * Written by Simon Josefsson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include "gc.h" + +int +main (int argc, char *argv[]) +{ + + /* Test vectors from RFC 1321. */ + + { + char *in = "abcdefghijklmnopqrstuvwxyz"; + size_t inlen = strlen (in); + char *expect = + "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00\x7d\xfb\x49\x6c\xca\x67\xe1\x3b"; + char out[16]; + + /* MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b */ + + if (gc_md5 (in, inlen, out) != 0) + { + printf ("gc_md5 call failed\n"); + return 1; + } + + if (memcmp (out, expect, 16) != 0) + { + size_t i; + printf ("md5 1 missmatch. expected:\n"); + for (i = 0; i < 16; i++) + printf ("%02x ", expect[i] & 0xFF); + printf ("\ncomputed:\n"); + for (i = 0; i < 16; i++) + printf ("%02x ", out[i] & 0xFF); + printf ("\n"); + return 1; + } + } + + /* Test vectors from RFC 2104. */ + + { + char *key = + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"; + size_t key_len = 16; + char *data = "Hi There"; + size_t data_len = 8; + char *digest = + "\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d"; + char out[16]; + + /* + key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + key_len = 16 bytes + data = "Hi There" + data_len = 8 bytes + digest = 0x9294727a3638bb1c13f48ef8158bfc9d + */ + + if (gc_hmac_md5 (key, key_len, data, data_len, out) != 0) + { + printf ("call failure\n"); + return 1; + } + + if (memcmp (digest, out, 16) != 0) + { + size_t i; + printf ("hash 1 missmatch. expected:\n"); + for (i = 0; i < 16; i++) + printf ("%02x ", digest[i] & 0xFF); + printf ("\ncomputed:\n"); + for (i = 0; i < 16; i++) + printf ("%02x ", out[i] & 0xFF); + printf ("\n"); + return 1; + } + } + + return 0; +}