/* * Write the mpi A to OUT. */ gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a) { int rc; if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; const void *p; p = gcry_mpi_get_opaque (a, &nbits); rc = iobuf_write (out, p, (nbits+7)/8); } else { char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */ size_t nbytes; nbytes = DIM(buffer); rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a ); if( !rc ) rc = iobuf_write( out, buffer, nbytes ); else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT ) { log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a)); /* The buffer was too small. We better tell the user about the MPI. */ rc = gpg_error (GPG_ERR_TOO_LARGE); } } return rc; }
/** * Output the given MPI value to the given buffer in * network byte order. * The MPI @a val may not be negative. * * @param buf where to output to * @param size number of bytes in @a buf * @param val value to write to @a buf */ void GNUNET_CRYPTO_mpi_print_unsigned (void *buf, size_t size, gcry_mpi_t val) { size_t rsize; if (gcry_mpi_get_flag (val, GCRYMPI_FLAG_OPAQUE)) { /* Store opaque MPIs left aligned into the buffer. */ unsigned int nbits; const void *p; p = gcry_mpi_get_opaque (val, &nbits); GNUNET_assert (p); rsize = (nbits+7)/8; if (rsize > size) rsize = size; memcpy (buf, p, rsize); if (rsize < size) memset (buf+rsize, 0, size - rsize); } else { /* Store regular MPIs as unsigned integers right aligned into the buffer. */ rsize = size; GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, buf, rsize, &rsize, val)); adjust (buf, rsize, size); } }
static void test_opaque (void) { gcry_mpi_t a; char *p; unsigned int nbits; p = gcry_xstrdup ("This is a test buffer"); a = gcry_mpi_set_opaque (NULL, p, 21*8+1); /* (a non byte aligned length) */ if (!gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) die ("opaque flag not set\n"); p = gcry_mpi_get_opaque (a, &nbits); if (!p) die ("gcry_mpi_get_opaque returned NULL\n"); if (nbits != 21*8+1) die ("gcry_mpi_get_opaque returned a changed bit size\n"); if (strcmp (p, "This is a test buffer")) die ("gcry_mpi_get_opaque returned a changed buffer\n"); if (debug) gcry_log_debugmpi ("mpi", a); gcry_mpi_release (a); p = gcry_xstrdup ("This is a test buffer"); a = gcry_mpi_set_opaque_copy (NULL, p, 21*8+1); gcry_free (p); if (!gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) die ("opaque flag not set\n"); p = gcry_mpi_get_opaque (a, &nbits); if (!p) die ("gcry_mpi_get_opaque returned NULL\n"); if (nbits != 21*8+1) die ("gcry_mpi_get_opaque returned a changed bit size\n"); if (strcmp (p, "This is a test buffer")) die ("gcry_mpi_get_opaque returned a changed buffer\n"); if (debug) gcry_log_debugmpi ("mpi", a); gcry_mpi_release (a); }
static gpg_error_t write_fake_data (IOBUF out, gcry_mpi_t a) { unsigned int n; void *p; if (!a) return 0; p = gcry_mpi_get_opaque ( a, &n); return iobuf_write (out, p, (n+7)/8 ); }
static void write_fake_data (IOBUF out, gcry_mpi_t a) { if (a) { unsigned int n; void *p; p = gcry_mpi_get_opaque ( a, &n ); iobuf_write (out, p, (n+7)/8 ); } }
/* * Write the mpi A to OUT. */ gpg_error_t gpg_mpi_write (iobuf_t out, gcry_mpi_t a) { int rc; if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; const unsigned char *p; unsigned char lenhdr[2]; /* gcry_log_debugmpi ("a", a); */ p = gcry_mpi_get_opaque (a, &nbits); if (p) { /* Strip leading zero bits. */ for (; nbits >= 8 && !*p; p++, nbits -= 8) ; if (nbits >= 8 && !(*p & 0x80)) if (--nbits >= 7 && !(*p & 0x40)) if (--nbits >= 6 && !(*p & 0x20)) if (--nbits >= 5 && !(*p & 0x10)) if (--nbits >= 4 && !(*p & 0x08)) if (--nbits >= 3 && !(*p & 0x04)) if (--nbits >= 2 && !(*p & 0x02)) if (--nbits >= 1 && !(*p & 0x01)) --nbits; } /* gcry_log_debug (" [%u bit]\n", nbits); */ /* gcry_log_debughex (" ", p, (nbits+7)/8); */ lenhdr[0] = nbits >> 8; lenhdr[1] = nbits; rc = iobuf_write (out, lenhdr, 2); if (!rc && p) rc = iobuf_write (out, p, (nbits+7)/8); } else {
static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) { int rc = 0; int i, nskey, npkey; IOBUF a = iobuf_temp(); /* Build in a self-enlarging buffer. */ /* Write the version number - if none is specified, use 3 */ if ( !sk->version ) iobuf_put ( a, 3 ); else iobuf_put ( a, sk->version ); write_32 (a, sk->timestamp ); /* v3 needs the expiration time. */ if ( sk->version < 4 ) { u16 ndays; if ( sk->expiredate ) ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L); else ndays = 0; write_16(a, ndays); } iobuf_put (a, sk->pubkey_algo ); /* Get number of secret and public parameters. They are held in one array first the public ones, then the secret ones. */ nskey = pubkey_get_nskey ( sk->pubkey_algo ); npkey = pubkey_get_npkey ( sk->pubkey_algo ); /* If we don't have any public parameters - which is the case if we don't know the algorithm used - the parameters are stored as one blob in a faked (opaque) MPI. */ if ( !npkey ) { write_fake_data( a, sk->skey[0] ); goto leave; } assert ( npkey < nskey ); /* Writing the public parameters is easy. */ for (i=0; i < npkey; i++ ) if ((rc = mpi_write (a, sk->skey[i]))) goto leave; /* Build the header for protected (encrypted) secret parameters. */ if ( sk->is_protected ) { if ( is_RSA(sk->pubkey_algo) && sk->version < 4 && !sk->protect.s2k.mode ) { /* The simple rfc1991 (v3) way. */ iobuf_put (a, sk->protect.algo ); iobuf_write (a, sk->protect.iv, sk->protect.ivlen ); } else { /* OpenPGP protection according to rfc2440. */ iobuf_put(a, sk->protect.sha1chk? 0xfe : 0xff ); iobuf_put(a, sk->protect.algo ); if ( sk->protect.s2k.mode >= 1000 ) { /* These modes are not possible in OpenPGP, we use them to implement our extensions, 101 can be seen as a private/experimental extension (this is not specified in rfc2440 but the same scheme is used for all other algorithm identifiers) */ iobuf_put(a, 101 ); iobuf_put(a, sk->protect.s2k.hash_algo ); iobuf_write(a, "GNU", 3 ); iobuf_put(a, sk->protect.s2k.mode - 1000 ); } else { iobuf_put(a, sk->protect.s2k.mode ); iobuf_put(a, sk->protect.s2k.hash_algo ); } if ( sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3 ) iobuf_write (a, sk->protect.s2k.salt, 8 ); if ( sk->protect.s2k.mode == 3 ) iobuf_put (a, sk->protect.s2k.count ); /* For our special modes 1001, 1002 we do not need an IV. */ if ( sk->protect.s2k.mode != 1001 && sk->protect.s2k.mode != 1002 ) iobuf_write (a, sk->protect.iv, sk->protect.ivlen ); } } else iobuf_put (a, 0 ); if ( sk->protect.s2k.mode == 1001 ) ; /* GnuPG extension - don't write a secret key at all. */ else if ( sk->protect.s2k.mode == 1002 ) { /* GnuPG extension - divert to OpenPGP smartcard. */ iobuf_put(a, sk->protect.ivlen ); /* Length of the serial number or 0 for no serial number. */ /* The serial number gets stored in the IV field. */ iobuf_write(a, sk->protect.iv, sk->protect.ivlen); } else if ( sk->is_protected && sk->version >= 4 ) { /* The secret key is protected - write it out as it is. */ byte *p; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[npkey], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[npkey], &ndatabits ); iobuf_write (a, p, (ndatabits+7)/8 ); } else if ( sk->is_protected ) { /* The secret key is protected the old v4 way. */ for ( ; i < nskey; i++ ) { byte *p; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); iobuf_write (a, p, (ndatabits+7)/8); } write_16(a, sk->csum ); } else { /* Non-protected key. */ for ( ; i < nskey; i++ ) if ( (rc = mpi_write (a, sk->skey[i]))) goto leave; write_16 (a, sk->csum ); } leave: if (!rc) { /* Build the header of the packet - which we must do after writing all the other stuff, so that we know the length of the packet */ write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes); /* And finally write it out the real stream */ rc = iobuf_write_temp( out, a ); } iobuf_close(a); /* Close the remporary buffer */ return rc; }
static int xxxx_do_check( PKT_secret_key *sk, const char *tryagain_text, int mode, int *canceled ) { gpg_error_t err; byte *buffer; u16 csum=0; int i, res; size_t nbytes; if( sk->is_protected ) { /* remove the protection */ DEK *dek = NULL; u32 keyid[4]; /* 4! because we need two of them */ gcry_cipher_hd_t cipher_hd=NULL; PKT_secret_key *save_sk; if( sk->protect.s2k.mode == 1001 ) { log_info(_("secret key parts are not available\n")); return GPG_ERR_UNUSABLE_SECKEY; } if( sk->protect.algo == CIPHER_ALGO_NONE ) BUG(); if( openpgp_cipher_test_algo( sk->protect.algo ) ) { log_info(_("protection algorithm %d%s is not supported\n"), sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" ); return GPG_ERR_CIPHER_ALGO; } if(gcry_md_test_algo (sk->protect.s2k.hash_algo)) { log_info(_("protection digest %d is not supported\n"), sk->protect.s2k.hash_algo); return GPG_ERR_DIGEST_ALGO; } keyid_from_sk( sk, keyid ); keyid[2] = keyid[3] = 0; if (!sk->flags.primary) { keyid[2] = sk->main_keyid[0]; keyid[3] = sk->main_keyid[1]; } dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo, &sk->protect.s2k, mode, tryagain_text, canceled ); if (!dek && canceled && *canceled) return GPG_ERR_CANCELED; err = openpgp_cipher_open (&cipher_hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB, (GCRY_CIPHER_SECURE | (sk->protect.algo >= 100 ? 0 : GCRY_CIPHER_ENABLE_SYNC))); if (err) log_fatal ("cipher open failed: %s\n", gpg_strerror (err) ); err = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen); if (err) log_fatal ("set key failed: %s\n", gpg_strerror (err) ); xfree(dek); save_sk = copy_secret_key( NULL, sk ); gcry_cipher_setiv ( cipher_hd, sk->protect.iv, sk->protect.ivlen ); csum = 0; if( sk->version >= 4 ) { int ndata; unsigned int ndatabits; byte *p, *data; u16 csumc = 0; i = pubkey_get_npkey(sk->pubkey_algo); assert ( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE )); p = gcry_mpi_get_opaque ( sk->skey[i], &ndatabits ); ndata = (ndatabits+7)/8; if ( ndata > 1 ) csumc = buf16_to_u16 (p+ndata-2); data = xmalloc_secure ( ndata ); gcry_cipher_decrypt ( cipher_hd, data, ndata, p, ndata ); gcry_mpi_release (sk->skey[i]); sk->skey[i] = NULL ; p = data; if (sk->protect.sha1chk) { /* This is the new SHA1 checksum method to detect tampering with the key as used by the Klima/Rosa attack */ sk->csum = 0; csum = 1; if( ndata < 20 ) log_error("not enough bytes for SHA-1 checksum\n"); else { gcry_md_hd_t h; if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1)) BUG(); /* Algo not available. */ gcry_md_write (h, data, ndata - 20); gcry_md_final (h); if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1), data + ndata - 20, 20) ) { /* Digest does match. We have to keep the old style checksum in sk->csum, so that the test used for unprotected keys does work. This test gets used when we are adding new keys. */ sk->csum = csum = checksum (data, ndata-20); } gcry_md_close (h); } } else { if( ndata < 2 ) { log_error("not enough bytes for checksum\n"); sk->csum = 0; csum = 1; } else { csum = checksum( data, ndata-2); sk->csum = data[ndata-2] << 8 | data[ndata-1]; if ( sk->csum != csum ) { /* This is a PGP 7.0.0 workaround */ sk->csum = csumc; /* take the encrypted one */ } } } /* Must check it here otherwise the mpi_read_xx would fail because the length may have an arbitrary value */ if( sk->csum == csum ) { for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { if ( gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, p, ndata, &nbytes)) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } ndata -= nbytes; p += nbytes; } /* Note: at this point ndata should be 2 for a simple checksum or 20 for the sha1 digest */ } xfree(data); } else { for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { byte *p; size_t ndata; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); ndata = (ndatabits+7)/8; assert (ndata >= 2); assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2); buffer = xmalloc_secure (ndata); gcry_cipher_sync (cipher_hd); buffer[0] = p[0]; buffer[1] = p[1]; gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2, p+2, ndata-2); csum += checksum (buffer, ndata); gcry_mpi_release (sk->skey[i]); err = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP, buffer, ndata, &ndata ); xfree (buffer); if (err) { /* Checksum was okay, but not correctly decrypted. */ sk->csum = 0; csum = 1; break; } /* csum += checksum_mpi (sk->skey[i]); */ } } gcry_cipher_close ( cipher_hd ); /* Now let's see whether we have used the correct passphrase. */ if( csum != sk->csum ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } /* The checksum may fail, so we also check the key itself. */ res = pk_check_secret_key ( sk->pubkey_algo, sk->skey ); if( res ) { copy_secret_key( sk, save_sk ); passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo ); free_secret_key( save_sk ); return gpg_error (GPG_ERR_BAD_PASSPHRASE); } free_secret_key( save_sk ); sk->is_protected = 0; } else { /* not protected, assume it is okay if the checksum is okay */ csum = 0; for(i=pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) { csum += checksum_mpi( sk->skey[i] ); } if( csum != sk->csum ) return GPG_ERR_CHECKSUM; } return 0; }
/* Compute an ECDSA signature. * Return the signature struct (r,s) from the message hash. The caller * must have allocated R and S. */ gpg_err_code_t _gcry_ecc_ecdsa_sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s, int flags, int hashalgo) { gpg_err_code_t err = 0; int extraloops = 0; gcry_mpi_t k, dr, sum, k_1, x; mpi_point_struct I; gcry_mpi_t hash; const void *abuf; unsigned int abits, qbits; mpi_ec_t ctx; if (DBG_CIPHER) log_mpidump ("ecdsa sign hash ", input ); qbits = mpi_get_nbits (skey->E.n); /* Convert the INPUT into an MPI if needed. */ if (mpi_is_opaque (input)) { abuf = gcry_mpi_get_opaque (input, &abits); err = gpg_err_code (gcry_mpi_scan (&hash, GCRYMPI_FMT_USG, abuf, (abits+7)/8, NULL)); if (err) return err; if (abits > qbits) gcry_mpi_rshift (hash, hash, abits - qbits); } else hash = input; k = NULL; dr = mpi_alloc (0); sum = mpi_alloc (0); k_1 = mpi_alloc (0); x = mpi_alloc (0); point_init (&I); ctx = _gcry_mpi_ec_p_internal_new (skey->E.model, skey->E.dialect, skey->E.p, skey->E.a, skey->E.b); /* Two loops to avoid R or S are zero. This is more of a joke than a real demand because the probability of them being zero is less than any hardware failure. Some specs however require it. */ do { do { mpi_free (k); k = NULL; if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo) { /* Use Pornin's method for deterministic DSA. If this flag is set, it is expected that HASH is an opaque MPI with the to be signed hash. That hash is also used as h1 from 3.2.a. */ if (!mpi_is_opaque (input)) { err = GPG_ERR_CONFLICT; goto leave; } abuf = gcry_mpi_get_opaque (input, &abits); err = _gcry_dsa_gen_rfc6979_k (&k, skey->E.n, skey->d, abuf, (abits+7)/8, hashalgo, extraloops); if (err) goto leave; extraloops++; } else k = _gcry_dsa_gen_k (skey->E.n, GCRY_STRONG_RANDOM); _gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx); if (_gcry_mpi_ec_get_affine (x, NULL, &I, ctx)) { if (DBG_CIPHER) log_debug ("ecc sign: Failed to get affine coordinates\n"); err = GPG_ERR_BAD_SIGNATURE; goto leave; } mpi_mod (r, x, skey->E.n); /* r = x mod n */ } while (!mpi_cmp_ui (r, 0)); mpi_mulm (dr, skey->d, r, skey->E.n); /* dr = d*r mod n */ mpi_addm (sum, hash, dr, skey->E.n); /* sum = hash + (d*r) mod n */ mpi_invm (k_1, k, skey->E.n); /* k_1 = k^(-1) mod n */ mpi_mulm (s, k_1, sum, skey->E.n); /* s = k^(-1)*(hash+(d*r)) mod n */ } while (!mpi_cmp_ui (s, 0)); if (DBG_CIPHER) { log_mpidump ("ecdsa sign result r ", r); log_mpidump ("ecdsa sign result s ", s); } leave: _gcry_mpi_ec_free (ctx); point_free (&I); mpi_free (x); mpi_free (k_1); mpi_free (sum); mpi_free (dr); mpi_free (k); if (hash != input) mpi_free (hash); return err; }
/**************** * Scan the provided buffer and return the S expression in our internal * format. Returns a newly allocated expression. If erroff is not NULL and * a parsing error has occurred, the offset into buffer will be returned. * If ARGFLAG is true, the function supports some printf like * expressions. * These are: * %m - MPI * %s - string (no autoswitch to secure allocation) * %d - integer stored as string (no autoswitch to secure allocation) * %b - memory buffer; this takes _two_ arguments: an integer with the * length of the buffer and a pointer to the buffer. * %S - Copy an gcry_sexp_t here. The S-expression needs to be a * regular one, starting with a parenthesis. * (no autoswitch to secure allocation) * all other format elements are currently not defined and return an error. * this includes the "%%" sequence becauce the percent sign is not an * allowed character. * FIXME: We should find a way to store the secure-MPIs not in the string * but as reference to somewhere - this can help us to save huge amounts * of secure memory. The problem is, that if only one element is secure, all * other elements are automagicaly copied to secure memory too, so the most * common operation gcry_sexp_cdr_mpi() will always return a secure MPI * regardless whether it is needed or not. */ static gcry_error_t vsexp_sscan (gcry_sexp_t *retsexp, size_t *erroff, const char *buffer, size_t length, int argflag, void **arg_list, va_list arg_ptr) { gcry_err_code_t err = 0; static const char tokenchars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789-./_:*+="; const char *p; size_t n; const char *digptr = NULL; const char *quoted = NULL; const char *tokenp = NULL; const char *hexfmt = NULL; const char *base64 = NULL; const char *disphint = NULL; const char *percent = NULL; int hexcount = 0; int quoted_esc = 0; int datalen = 0; size_t dummy_erroff; struct make_space_ctx c; int arg_counter = 0; int level = 0; if (!erroff) erroff = &dummy_erroff; /* Depending on whether ARG_LIST is non-zero or not, this macro gives us the next argument, either from the variable argument list as specified by ARG_PTR or from the argument array ARG_LIST. */ #define ARG_NEXT(storage, type) \ do \ { \ if (!arg_list) \ storage = va_arg (arg_ptr, type); \ else \ storage = *((type *) (arg_list[arg_counter++])); \ } \ while (0) /* The MAKE_SPACE macro is used before each store operation to ensure that the buffer is large enough. It requires a global context named C and jumps out to the label LEAVE on error! It also sets ERROFF using the variables BUFFER and P. */ #define MAKE_SPACE(n) do { \ gpg_err_code_t _ms_err = make_space (&c, (n)); \ if (_ms_err) \ { \ err = _ms_err; \ *erroff = p - buffer; \ goto leave; \ } \ } while (0) /* The STORE_LEN macro is used to store the length N at buffer P. */ #define STORE_LEN(p,n) do { \ DATALEN ashort = (n); \ memcpy ( (p), &ashort, sizeof(ashort) ); \ (p) += sizeof (ashort); \ } while (0) /* We assume that the internal representation takes less memory than the provided one. However, we add space for one extra datalen so that the code which does the ST_CLOSE can use MAKE_SPACE */ c.allocated = length + sizeof(DATALEN); if (buffer && length && gcry_is_secure (buffer)) c.sexp = gcry_malloc_secure (sizeof *c.sexp + c.allocated - 1); else c.sexp = gcry_malloc (sizeof *c.sexp + c.allocated - 1); if (!c.sexp) { err = gpg_err_code_from_errno (errno); *erroff = 0; goto leave; } c.pos = c.sexp->d; for (p = buffer, n = length; n; p++, n--) { if (tokenp && !hexfmt) { if (strchr (tokenchars, *p)) continue; else { datalen = p - tokenp; MAKE_SPACE (datalen); *c.pos++ = ST_DATA; STORE_LEN (c.pos, datalen); memcpy (c.pos, tokenp, datalen); c.pos += datalen; tokenp = NULL; } } if (quoted) { if (quoted_esc) { switch (*p) { case 'b': case 't': case 'v': case 'n': case 'f': case 'r': case '"': case '\'': case '\\': quoted_esc = 0; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': if (!((n > 2) && (p[1] >= '0') && (p[1] <= '7') && (p[2] >= '0') && (p[2] <= '7'))) { *erroff = p - buffer; /* Invalid octal value. */ err = GPG_ERR_SEXP_BAD_QUOTATION; goto leave; } p += 2; n -= 2; quoted_esc = 0; break; case 'x': if (!((n > 2) && hexdigitp (p+1) && hexdigitp (p+2))) { *erroff = p - buffer; /* Invalid hex value. */ err = GPG_ERR_SEXP_BAD_QUOTATION; goto leave; } p += 2; n -= 2; quoted_esc = 0; break; case '\r': /* ignore CR[,LF] */ if (n && (p[1] == '\n')) { p++; n--; } quoted_esc = 0; break; case '\n': /* ignore LF[,CR] */ if (n && (p[1] == '\r')) { p++; n--; } quoted_esc = 0; break; default: *erroff = p - buffer; /* Invalid quoted string escape. */ err = GPG_ERR_SEXP_BAD_QUOTATION; goto leave; } } else if (*p == '\\') quoted_esc = 1; else if (*p == '\"') { /* Keep it easy - we know that the unquoted string will never be larger. */ unsigned char *save; size_t len; quoted++; /* Skip leading quote. */ MAKE_SPACE (p - quoted); *c.pos++ = ST_DATA; save = c.pos; STORE_LEN (c.pos, 0); /* Will be fixed up later. */ len = unquote_string (quoted, p - quoted, c.pos); c.pos += len; STORE_LEN (save, len); quoted = NULL; } } else if (hexfmt) { if (isxdigit (*p)) hexcount++; else if (*p == '#') { if ((hexcount & 1)) { *erroff = p - buffer; err = GPG_ERR_SEXP_ODD_HEX_NUMBERS; goto leave; } datalen = hexcount / 2; MAKE_SPACE (datalen); *c.pos++ = ST_DATA; STORE_LEN (c.pos, datalen); for (hexfmt++; hexfmt < p; hexfmt++) { int tmpc; if (whitespacep (hexfmt)) continue; tmpc = hextonibble (*(const unsigned char*)hexfmt); for (hexfmt++; hexfmt < p && whitespacep (hexfmt); hexfmt++) ; if (hexfmt < p) { tmpc *= 16; tmpc += hextonibble (*(const unsigned char*)hexfmt); } *c.pos++ = tmpc; } hexfmt = NULL; } else if (!whitespacep (p)) { *erroff = p - buffer; err = GPG_ERR_SEXP_BAD_HEX_CHAR; goto leave; } } else if (base64) { if (*p == '|') base64 = NULL; } else if (digptr) { if (digitp (p)) ; else if (*p == ':') { datalen = atoi (digptr); /* FIXME: check for overflow. */ digptr = NULL; if (datalen > n - 1) { *erroff = p - buffer; /* Buffer too short. */ err = GPG_ERR_SEXP_STRING_TOO_LONG; goto leave; } /* Make a new list entry. */ MAKE_SPACE (datalen); *c.pos++ = ST_DATA; STORE_LEN (c.pos, datalen); memcpy (c.pos, p + 1, datalen); c.pos += datalen; n -= datalen; p += datalen; } else if (*p == '\"') { digptr = NULL; /* We ignore the optional length. */ quoted = p; quoted_esc = 0; } else if (*p == '#') { digptr = NULL; /* We ignore the optional length. */ hexfmt = p; hexcount = 0; } else if (*p == '|') { digptr = NULL; /* We ignore the optional length. */ base64 = p; } else { *erroff = p - buffer; err = GPG_ERR_SEXP_INV_LEN_SPEC; goto leave; } } else if (percent) { if (*p == 'm' || *p == 'M') { /* Insert an MPI. */ gcry_mpi_t m; size_t nm = 0; int mpifmt = *p == 'm'? GCRYMPI_FMT_STD: GCRYMPI_FMT_USG; ARG_NEXT (m, gcry_mpi_t); if (gcry_mpi_get_flag (m, GCRYMPI_FLAG_OPAQUE)) { void *mp; unsigned int nbits; mp = gcry_mpi_get_opaque (m, &nbits); nm = (nbits+7)/8; if (mp && nm) { MAKE_SPACE (nm); if (!gcry_is_secure (c.sexp->d) && gcry_mpi_get_flag (m, GCRYMPI_FLAG_SECURE)) { /* We have to switch to secure allocation. */ gcry_sexp_t newsexp; byte *newhead; newsexp = gcry_malloc_secure (sizeof *newsexp + c.allocated - 1); if (!newsexp) { err = gpg_err_code_from_errno (errno); goto leave; } newhead = newsexp->d; memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d)); c.pos = newhead + (c.pos - c.sexp->d); gcry_free (c.sexp); c.sexp = newsexp; } *c.pos++ = ST_DATA; STORE_LEN (c.pos, nm); memcpy (c.pos, mp, nm); c.pos += nm; } } else { if (gcry_mpi_print (mpifmt, NULL, 0, &nm, m)) BUG (); MAKE_SPACE (nm); if (!gcry_is_secure (c.sexp->d) && gcry_mpi_get_flag ( m, GCRYMPI_FLAG_SECURE)) { /* We have to switch to secure allocation. */ gcry_sexp_t newsexp; byte *newhead; newsexp = gcry_malloc_secure (sizeof *newsexp + c.allocated - 1); if (!newsexp) { err = gpg_err_code_from_errno (errno); goto leave; } newhead = newsexp->d; memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d)); c.pos = newhead + (c.pos - c.sexp->d); gcry_free (c.sexp); c.sexp = newsexp; } *c.pos++ = ST_DATA; STORE_LEN (c.pos, nm); if (gcry_mpi_print (mpifmt, c.pos, nm, &nm, m)) BUG (); c.pos += nm; } } else if (*p == 's') { /* Insert an string. */ const char *astr; size_t alen; ARG_NEXT (astr, const char *); alen = strlen (astr); MAKE_SPACE (alen); *c.pos++ = ST_DATA; STORE_LEN (c.pos, alen); memcpy (c.pos, astr, alen); c.pos += alen; } else if (*p == 'b') { /* Insert a memory buffer. */ const char *astr; int alen; ARG_NEXT (alen, int); ARG_NEXT (astr, const char *); MAKE_SPACE (alen); if (alen && !gcry_is_secure (c.sexp->d) && gcry_is_secure (astr)) { /* We have to switch to secure allocation. */ gcry_sexp_t newsexp; byte *newhead; newsexp = gcry_malloc_secure (sizeof *newsexp + c.allocated - 1); if (!newsexp) { err = gpg_err_code_from_errno (errno); goto leave; } newhead = newsexp->d; memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d)); c.pos = newhead + (c.pos - c.sexp->d); gcry_free (c.sexp); c.sexp = newsexp; } *c.pos++ = ST_DATA; STORE_LEN (c.pos, alen); memcpy (c.pos, astr, alen); c.pos += alen; } else if (*p == 'd') { /* Insert an integer as string. */ int aint; size_t alen; char buf[35]; ARG_NEXT (aint, int); sprintf (buf, "%d", aint); alen = strlen (buf); MAKE_SPACE (alen); *c.pos++ = ST_DATA; STORE_LEN (c.pos, alen); memcpy (c.pos, buf, alen); c.pos += alen; } else if (*p == 'u') { /* Insert an unsigned integer as string. */ unsigned int aint; size_t alen; char buf[35]; ARG_NEXT (aint, unsigned int); sprintf (buf, "%u", aint); alen = strlen (buf); MAKE_SPACE (alen); *c.pos++ = ST_DATA; STORE_LEN (c.pos, alen); memcpy (c.pos, buf, alen); c.pos += alen; } else if (*p == 'S') { /* Insert a gcry_sexp_t. */ gcry_sexp_t asexp; size_t alen, aoff; ARG_NEXT (asexp, gcry_sexp_t); alen = get_internal_buffer (asexp, &aoff); if (alen) { MAKE_SPACE (alen); memcpy (c.pos, asexp->d + aoff, alen); c.pos += alen; } } else { *erroff = p - buffer; /* Invalid format specifier. */ err = GPG_ERR_SEXP_INV_LEN_SPEC; goto leave; } percent = NULL; } else if (*p == '(') { if (disphint) { *erroff = p - buffer; /* Open display hint. */ err = GPG_ERR_SEXP_UNMATCHED_DH; goto leave; } MAKE_SPACE (0); *c.pos++ = ST_OPEN; level++; } else if (*p == ')') { /* Walk up. */ if (disphint) { *erroff = p - buffer; /* Open display hint. */ err = GPG_ERR_SEXP_UNMATCHED_DH; goto leave; } MAKE_SPACE (0); *c.pos++ = ST_CLOSE; level--; } else if (*p == '\"') { quoted = p; quoted_esc = 0; } else if (*p == '#') { hexfmt = p; hexcount = 0; } else if (*p == '|') base64 = p; else if (*p == '[') { if (disphint) { *erroff = p - buffer; /* Open display hint. */ err = GPG_ERR_SEXP_NESTED_DH; goto leave; } disphint = p; } else if (*p == ']') { if (!disphint) { *erroff = p - buffer; /* Open display hint. */ err = GPG_ERR_SEXP_UNMATCHED_DH; goto leave; } disphint = NULL; } else if (digitp (p)) { if (*p == '0') { /* A length may not begin with zero. */ *erroff = p - buffer; err = GPG_ERR_SEXP_ZERO_PREFIX; goto leave; } digptr = p; } else if (strchr (tokenchars, *p)) tokenp = p; else if (whitespacep (p)) ; else if (*p == '{') { /* fixme: handle rescanning: we can do this by saving our current state and start over at p+1 -- Hmmm. At this point here we are in a well defined state, so we don't need to save it. Great. */ *erroff = p - buffer; err = GPG_ERR_SEXP_UNEXPECTED_PUNC; goto leave; } else if (strchr ("&\\", *p)) { /* Reserved punctuation. */ *erroff = p - buffer; err = GPG_ERR_SEXP_UNEXPECTED_PUNC; goto leave; } else if (argflag && (*p == '%')) percent = p; else { /* Bad or unavailable. */ *erroff = p - buffer; err = GPG_ERR_SEXP_BAD_CHARACTER; goto leave; } } MAKE_SPACE (0); *c.pos++ = ST_STOP; if (level && !err) err = GPG_ERR_SEXP_UNMATCHED_PAREN; leave: if (err) { /* Error -> deallocate. */ if (c.sexp) { /* Extra paranoid wipe on error. */ if (gcry_is_secure (c.sexp)) wipememory (c.sexp, sizeof (struct gcry_sexp) + c.allocated - 1); gcry_free (c.sexp); } /* This might be expected by existing code... */ *retsexp = NULL; } else *retsexp = normalize (c.sexp); return gcry_error (err); #undef MAKE_SPACE #undef STORE_LEN }