summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scd/ChangeLog14
-rw-r--r--scd/apdu.c4
-rw-r--r--scd/app-nks.c4
-rw-r--r--scd/app-openpgp.c2
-rw-r--r--scd/app.c2
-rw-r--r--scd/card-dinsig.c4
-rw-r--r--scd/card-p15.c12
-rw-r--r--scd/card.c8
-rw-r--r--scd/ccid-driver.c14
-rw-r--r--scd/command.c6
-rw-r--r--scd/iso7816.c24
-rw-r--r--scd/iso7816.h1
-rw-r--r--scd/sc-investigate.c143
-rw-r--r--scd/scdaemon.h8
14 files changed, 200 insertions, 46 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 66ceebd22..a9296cbbb 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,17 @@
+2004-03-11 Werner Koch <wk@gnupg.org>
+
+ * scdaemon.h (out_of_core): Removed. Replaced callers by standard
+ gpg_error function.
+
+ * apdu.c, iso7816.c, ccid-driver.c [GNUPG_SCD_MAIN_HEADER]: Allow
+ to include a header defined by the compiler. This helps us to
+ reuse the source in other software.
+
+2004-03-10 Werner Koch <wk@gnupg.org>
+
+ * iso7816.c (iso7816_read_record): New arg SHORT_EF. Changed all
+ callers.
+
2004-02-18 Werner Koch <wk@gnupg.org>
* sc-investigate.c (main): Setup the used character set.
diff --git a/scd/apdu.c b/scd/apdu.c
index e5295f566..7843fd566 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -28,7 +28,9 @@
# include <opensc/opensc.h>
#endif
-#if GNUPG_MAJOR_VERSION == 1
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over
there. */
diff --git a/scd/app-nks.c b/scd/app-nks.c
index 2ceb537a2..a4b6e3a15 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -130,10 +130,10 @@ keygripstr_from_pk_file (int slot, int fid, char *r_gripstr)
err = iso7816_select_file (slot, fid, 0, NULL, NULL);
if (err)
return err;
- err = iso7816_read_record (slot, 1, 1, &buffer[0], &buflen[0]);
+ err = iso7816_read_record (slot, 1, 1, 0, &buffer[0], &buflen[0]);
if (err)
return err;
- err = iso7816_read_record (slot, 2, 1, &buffer[1], &buflen[1]);
+ err = iso7816_read_record (slot, 2, 1, 0, &buffer[1], &buflen[1]);
if (err)
{
xfree (buffer[0]);
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 021d6a52c..7782b8e1c 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -245,7 +245,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
n = 6 + 2 + mlen + 2 + elen;
p = buffer = xtrymalloc (3 + n);
if (!buffer)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
*p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */
diff --git a/scd/app.c b/scd/app.c
index 6ac18272b..d395fe559 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -47,7 +47,7 @@ select_application (ctrl_t ctrl, int slot, const char *name)
app = xtrycalloc (1, sizeof *app);
if (!app)
{
- rc = out_of_core ();
+ rc = gpg_error (gpg_err_code_from_errno (errno));
log_info ("error allocating context: %s\n", gpg_strerror (rc));
return NULL;
}
diff --git a/scd/card-dinsig.c b/scd/card-dinsig.c
index bb070d5f0..df09bfb57 100644
--- a/scd/card-dinsig.c
+++ b/scd/card-dinsig.c
@@ -141,7 +141,7 @@ dinsig_enum_keypairs (CARD card, int idx,
{
*keyid = xtrymalloc (17);
if (!*keyid)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
if (!idx)
strcpy (*keyid, "DINSIG-DF01.C000");
else
@@ -193,7 +193,7 @@ dinsig_read_cert (CARD card, const char *certidstr,
buf = xtrymalloc (file->size);
if (!buf)
{
- gpg_error_t tmperr = out_of_core ();
+ gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
sc_file_free (file);
return tmperr;
}
diff --git a/scd/card-p15.c b/scd/card-p15.c
index 239e75045..ae3ef148f 100644
--- a/scd/card-p15.c
+++ b/scd/card-p15.c
@@ -53,7 +53,7 @@ init_private_data (CARD card)
priv = xtrycalloc (1, sizeof *priv);
if (!priv)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
/* OpenSC (0.7.0) is a bit strange in that the get_objects functions
tries to be a bit too clever and implicitly does an enumeration
@@ -179,7 +179,7 @@ p15_enum_keypairs (CARD card, int idx,
*keyid = p = xtrymalloc (9+pinfo->id.len*2+1);
if (!*keyid)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
p = stpcpy (p, "P15-5015.");
for (i=0; i < pinfo->id.len; i++, p += 2)
sprintf (p, "%02X", pinfo->id.value[i]);
@@ -217,7 +217,7 @@ p15_enum_certs (CARD card, int idx, char **certid, int *type)
*certid = p = xtrymalloc (9+cinfo->id.len*2+1);
if (!*certid)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
p = stpcpy (p, "P15-5015.");
for (i=0; i < cinfo->id.len; i++, p += 2)
sprintf (p, "%02X", cinfo->id.value[i]);
@@ -304,7 +304,7 @@ p15_read_cert (CARD card, const char *certidstr,
*cert = xtrymalloc (certder->data_len);
if (!*cert)
{
- gpg_error_t tmperr = out_of_core ();
+ gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
sc_pkcs15_free_certificate (certder);
return tmperr;
}
@@ -400,7 +400,7 @@ p15_sign (CARD card, const char *keyidstr, int hashalgo,
outbuflen = 1024;
outbuf = xtrymalloc (outbuflen);
if (!outbuf)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
rc = sc_pkcs15_compute_signature (card->p15card, keyobj,
cryptflags,
@@ -462,7 +462,7 @@ p15_decipher (CARD card, const char *keyidstr,
outbuflen = indatalen < 256? 256 : indatalen;
outbuf = xtrymalloc (outbuflen);
if (!outbuf)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
rc = sc_pkcs15_decipher (card->p15card, keyobj,
0,
diff --git a/scd/card.c b/scd/card.c
index 53c89f3a4..8366dcb1c 100644
--- a/scd/card.c
+++ b/scd/card.c
@@ -108,7 +108,7 @@ card_open (CARD *rcard)
card = xtrycalloc (1, sizeof *card);
if (!card)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
card->reader = 0;
rc = sc_establish_context (&card->ctx, "scdaemon");
@@ -275,7 +275,7 @@ find_iccsn (const unsigned char *buffer, size_t length, char **serial)
*serial = p = xtrymalloc (2*n+1);
if (!*serial)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
for (; n; n--, p += 2, s++)
sprintf (p, "%02X", *s);
*p = 0;
@@ -389,7 +389,7 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
*serial = NULL;
p = xtrymalloc (strlen (efser) + 7);
if (!p)
- rc = out_of_core ();
+ rc = gpg_error (gpg_err_code_from_errno (errno));
else
{
strcpy (p, "FF0100");
@@ -405,7 +405,7 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
{
xfree (*serial);
*serial = NULL;
- rc = out_of_core ();
+ rc = gpg_error (gpg_err_code_from_errno (errno));
}
else
{
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index ca5620176..b398e3ce3 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -87,16 +87,24 @@
#define DRVNAME "ccid-driver: "
-#ifdef GNUPG_MAJOR_VERSION /* This source is used within GnuPG. */
+/* Depending on how this source is used we either define our error
+ output to go to stderr or to the jnlib based logging functions. We
+ use the latter when GNUPG_MAJOR_VERSION is defines or when both,
+ GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined.
+*/
+#if defined(GNUPG_MAJOR_VERSION) \
+ || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING))
-# if GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
+#if defined(GNUPG_SCD_MAIN_HEADER)
+# include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
# include "options.h"
# include "util.h"
# include "memory.h"
# include "cardglue.h"
# else /* This is the modularized GnuPG 1.9 or later. */
# include "scdaemon.h"
-# endif
+#endif
/* Disable all debugging output for now. */
#undef DBG_CARD_IO
diff --git a/scd/command.c b/scd/command.c
index d148ddb5a..4746e11b5 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -351,7 +351,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
buf = xtrymalloc (40 + 1 + strlen (certid) + 1);
if (!buf)
- rc = out_of_core ();
+ rc = gpg_error (gpg_err_code_from_errno (errno));
else
{
sprintf (buf, "%d %s", certtype, certid);
@@ -389,7 +389,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
buf = p = xtrymalloc (40 + 1 + strlen (keyid) + 1);
if (!buf)
- rc = out_of_core ();
+ rc = gpg_error (gpg_err_code_from_errno (errno));
else
{
int i;
@@ -577,7 +577,7 @@ pin_cb (void *opaque, const char *info, char **retstr)
rc = asprintf (&command, "NEEDPIN %s", info);
if (rc < 0)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
/* FIXME: Write an inquire function which returns the result in
secure memory */
diff --git a/scd/iso7816.c b/scd/iso7816.c
index 1ee9b55e7..d7d3c126b 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -24,7 +24,9 @@
#include <stdlib.h>
#include <string.h>
-#if GNUPG_MAJOR_VERSION == 1
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over
there. */
@@ -200,7 +202,7 @@ iso7816_change_reference_data (int slot, int chvno,
buf = xtrymalloc (oldchvlen + newchvlen);
if (!buf)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
if (oldchvlen)
memcpy (buf, oldchv, oldchvlen);
memcpy (buf+oldchvlen, newchv, newchvlen);
@@ -341,7 +343,8 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
/* We need to prepend the padding indicator. */
buf = xtrymalloc (datalen + 1);
if (!buf)
- return out_of_core ();
+ return gpg_error (gpg_err_code_from_errno (errno));
+
*buf = padind; /* Padding indicator. */
memcpy (buf+1, data, datalen);
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
@@ -550,11 +553,13 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
/* Perform a READ RECORD command. RECNO gives the record number to
read with 0 indicating the current record. RECCOUNT must be 1 (not
- all cards support reading of more than one record). The result is
- stored in a newly allocated buffer at the address passed by RESULT.
- Returns the length of this data at the address of RESULTLEN. */
+ all cards support reading of more than one record). SHORT_EF
+ should be 0 to read the current EF or contain a short EF. The
+ result is stored in a newly allocated buffer at the address passed
+ by RESULT. Returns the length of this data at the address of
+ RESULTLEN. */
gpg_error_t
-iso7816_read_record (int slot, int recno, int reccount,
+iso7816_read_record (int slot, int recno, int reccount, int short_ef,
unsigned char **result, size_t *resultlen)
{
int sw;
@@ -568,7 +573,8 @@ iso7816_read_record (int slot, int recno, int reccount,
/* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
we check for this limit. */
- if (recno < 0 || recno > 255 || reccount != 1)
+ if (recno < 0 || recno > 255 || reccount != 1
+ || short_ef < 0 || short_ef > 254 )
return gpg_error (GPG_ERR_INV_VALUE);
buffer = NULL;
@@ -577,7 +583,7 @@ iso7816_read_record (int slot, int recno, int reccount,
with an Le of 0. */
sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
recno,
- 0x04,
+ short_ef? short_ef : 0x04,
-1, NULL,
254, &buffer, &bufferlen);
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 937326b6d..8f2b150e6 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -67,6 +67,7 @@ gpg_error_t iso7816_get_challenge (int slot,
gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
+ int short_ef,
unsigned char **result, size_t *resultlen);
#endif /*ISO7816_H*/
diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c
index 6f8b330b1..3882e1dfd 100644
--- a/scd/sc-investigate.c
+++ b/scd/sc-investigate.c
@@ -50,8 +50,10 @@ enum cmd_and_opt_values
{
oInteractive = 'i',
oVerbose = 'v',
+ oQuiet = 'q',
oReaderPort = 500,
octapiDriver,
+
oDebug,
oDebugAll,
@@ -68,6 +70,7 @@ static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, "@Options:\n " },
{ oInteractive, "interactive", 0, "start in interactive explorer mode"},
+ { oQuiet, "quiet", 0, "quiet" },
{ oVerbose, "verbose", 0, "verbose" },
{ oReaderPort, "reader-port", 2, "|N|connect to reader at port N"},
{ octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"},
@@ -86,7 +89,7 @@ static ARGPARSE_OPTS opts[] = {
static void interactive_shell (int slot);
-
+static void dump_other_cards (int slot);
static const char *
my_strusage (int level)
@@ -168,6 +171,7 @@ main (int argc, char **argv )
switch (pargs.r_opt)
{
case oVerbose: opt.verbose++; break;
+ case oQuiet: opt.quiet++; break;
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
case oDebugAll: opt.debug = ~0; break;
case oReaderPort: reader_port = pargs.r.ret_str; break;
@@ -191,7 +195,7 @@ main (int argc, char **argv )
if (slot == -1)
exit (1);
- if (!gen_random)
+ if (!gen_random && !opt.quiet)
{
rc = atr_dump (slot, stdout);
if (rc)
@@ -210,12 +214,17 @@ main (int argc, char **argv )
rc = app_select_openpgp (&appbuf);
if (rc)
{
- log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ if (!opt.quiet)
+ log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
memset (&appbuf, 0, sizeof appbuf);
appbuf.slot = slot;
rc = app_select_dinsig (&appbuf);
if (rc)
- log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc));
+ {
+ if (!opt.quiet)
+ log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc));
+ dump_other_cards (slot);
+ }
else
{
appbuf.initialized = 1;
@@ -398,6 +407,7 @@ interactive_shell (int slot)
cmdAPP,
cmdREAD,
cmdREADREC,
+ cmdREADSHORTREC,
cmdDEBUG,
cmdVERIFY,
cmdCHANGEREF,
@@ -425,6 +435,7 @@ interactive_shell (int slot)
{ "rb" , cmdREAD, NULL },
{ "readrec", cmdREADREC, "read record(s)" },
{ "rr" , cmdREADREC, NULL },
+ { "rsr" , cmdREADSHORTREC, "readshortrec RECNO SHORT_EF" },
{ "verify" , cmdVERIFY, "verify CHVNO PIN" },
{ "ver" , cmdVERIFY, NULL },
{ "changeref", cmdCHANGEREF, "change reference data" },
@@ -559,7 +570,8 @@ interactive_shell (int slot)
for (i=1, err=0; !err; i++)
{
xfree (result); result = NULL;
- err = iso7816_read_record (slot, i, 1, &result, &resultlen);
+ err = iso7816_read_record (slot, i, 1, 0,
+ &result, &resultlen);
if (!err)
dump_buffer (result, resultlen);
}
@@ -568,13 +580,31 @@ interactive_shell (int slot)
}
else
{
- err = iso7816_read_record (slot, arg_number, 1,
+ err = iso7816_read_record (slot, arg_number, 1, 0,
&result, &resultlen);
if (!err)
dump_or_store_buffer (arg_string, result, resultlen);
}
break;
+ case cmdREADSHORTREC:
+ {
+ int short_ef;
+
+ short_ef = strtol (arg_next, NULL, 0);
+
+ if (short_ef < 1 || short_ef > 254)
+ printf ("error: short EF must be between 1 and 254\n");
+ else
+ {
+ err = iso7816_read_record (slot, arg_number, 1, short_ef,
+ &result, &resultlen);
+ if (!err)
+ dump_or_store_buffer (arg_string, result, resultlen);
+ }
+ }
+ break;
+
case cmdVERIFY:
if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
printf ("error: invalid CHVNO\n");
@@ -637,3 +667,104 @@ interactive_shell (int slot)
;
}
+
+
+/* Figure out whether the current card is a German Geldkarte and print
+ what we know about it. */
+static int
+dump_geldkarte (int slot)
+{
+ unsigned char *r = NULL;
+ size_t rlen;
+ const char *t;
+
+ if (iso7816_read_record (slot, 1, 1, 0xbc, &r, &rlen))
+ return -1;
+ /* We require that the record is at least 24 bytes, the first byte
+ is 0x67 and the filler byte is correct. */
+ if (rlen < 24 || *r != 0x67 || r[22])
+ return -1;
+
+ /* The short Bankleitzahl consists of 3 bytes at offset 1. */
+ switch (r[1])
+ {
+ case 0x21: t = "Oeffentlich-rechtliche oder private Bank"; break;
+ case 0x22: t = "Privat- oder Geschäftsbank"; break;
+ case 0x25: t = "Sparkasse"; break;
+ case 0x26:
+ case 0x29: t = "Genossenschaftsbank"; break;
+ default:
+ xfree (r);
+ return -1; /* Probably not a Geldkarte. */
+ }
+
+ printf ("KBLZ .....: %02X-%02X%02X (%s)\n", r[1], r[2], r[3], t);
+ printf ("Card-No ..: %02X%02X%02X%02X%02X\n", r[4], r[5], r[6], r[7], r[8]);
+
+/* Byte 10 enthält im linken Halbbyte eine Prüfziffer, die nach dem */
+/* Verfahren 'Luhn formula for computing modulus 10' über die Ziffern der */
+/* ersten 9 Byte berechnet ist. */
+
+/* Das rechte Halbbyte wird zu 'D' gesetzt. */
+
+/* Für die Berechnung der Luhn-Prüfziffer sind die folgenden Schritte */
+/* durchzuführen: */
+
+/* Schritt 1: Mit der rechtesten Ziffer beginnend ist einschließlich dieser */
+/* Ziffer jede übernächste Ziffer zu verdoppeln (mit 2 multiplizieren). */
+
+/* Schritt 2: Die einzelnen Ziffern der Produkte aus Schritt 1 und die bei */
+/* diesen Multiplikationen unberührt gebliebenen Ziffern sind zu addieren. */
+
+/* Schritt 3: Das Ergebnis der Addition aus Schritt 2 ist von dem auf die */
+/* nächst höhere Zahl mit der Einerstelle 0 aufgerundeten Ergebnis der */
+/* Addition aus Schritt 2 abzuziehen. Wenn das Ergebnis der Addition aus */
+/* Schritt 2 bereits eine Zahl mit der Einerstelle 0 ergibt (z.B. 30, 40, */
+/* usw.), ist die Prüfziffer 0. */
+
+/* Beispiel: Kartennummer ohne Prüfziffer: 992 839 871 */
+
+/* 9 9 2 8 3 9 8 7 1 */
+
+/* x 2 x 2 x 2 x 2 x 2 Schritt 1 */
+
+/* 18 4 6 16 2 */
+
+/* 1+8 +9 +4 +8 +6 +9 +1+6 +7 +2 = 61 Schritt 2 */
+
+/* 70-61 = 9 Schritt 3 */
+
+/* Prüfziffer zu 992 839 871 = 9 */
+
+
+ printf ("Expires at: %02X/%02X\n", r[11], r[10] );
+ printf ("Valid from: %02X.%02X.%02X\n", r[14], r[13], r[12]);
+ printf ("Country ..: %02X%02X\n", r[15], r[16]);
+ printf ("Currency .: %c%c%c\n", isascii (r[17])? r[17]:' ',
+ isascii (r[18])? r[18]:' ', isascii (r[19])? r[19]:' ');
+ printf ("Cur.-Mult : %s\n",
+ r[20] == 0x01? "0.01":
+ r[20] == 0x02? "0.1":
+ r[20] == 0x04? "1":
+ r[20] == 0x08? "10":
+ r[20] == 0x10? "100":
+ r[20] == 0x20? "1000": "?");
+ printf ("ZKA ChipID: %02X\n", r[21]);
+ printf ("OS version: %02X\n", r[23]);
+
+ xfree (r);
+ return 0;
+}
+
+
+
+/* Try to figure out the type of teh card and dump its contents. */
+static void
+dump_other_cards (int slot)
+{
+
+ if (!dump_geldkarte (slot))
+ return;
+
+}
+
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 2bbf271da..098738508 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -33,14 +33,6 @@
#include "../common/util.h"
#include "../common/errors.h"
-/* Convenience funcion to be used instead of returning the old
- GNUPG_Out_Of_Core. */
-static __inline__ gpg_error_t
-out_of_core (void)
-{
- return gpg_error (gpg_err_code_from_errno (errno));
-}
-
#define MAX_DIGEST_LEN 24