static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); write_version( a, ctb ); if ( enc->throw_keyid ) { write_32(a, 0 ); /* Don't tell Eve who can decrypt the message. */ write_32(a, 0 ); } else { write_32(a, enc->keyid[0] ); write_32(a, enc->keyid[1] ); } iobuf_put(a,enc->pubkey_algo ); n = pubkey_get_nenc( enc->pubkey_algo ); if ( !n ) write_fake_data( a, enc->data[0] ); for (i=0; i < n && !rc ; i++ ) rc = mpi_write(a, enc->data[i] ); if (!rc) { write_header(out, ctb, iobuf_get_temp_length(a) ); rc = iobuf_write_temp( out, a ); } iobuf_close(a); return rc; }
static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); if( !pk->version ) iobuf_put( a, 3 ); else iobuf_put( a, pk->version ); write_32(a, pk->timestamp ); if( pk->version < 4 ) { u16 ndays; if( pk->expiredate ) ndays = (u16)((pk->expiredate - pk->timestamp) / 86400L); else ndays = 0; write_16(a, ndays ); } iobuf_put(a, pk->pubkey_algo ); n = pubkey_get_npkey( pk->pubkey_algo ); if( !n ) write_fake_data( a, pk->pkey[0] ); for(i=0; i < n; i++ ) mpi_write(a, pk->pkey[i] ); write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes, 1 ); if( iobuf_write_temp( out, a ) ) rc = G10ERR_WRITE_FILE; iobuf_close(a); return rc; }
static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) { int rc = 0; IOBUF a = iobuf_temp(); assert( enc->version == 4 ); switch( enc->s2k.mode ) { case 0: case 1: case 3: break; default: log_bug("do_symkey_enc: s2k=%d\n", enc->s2k.mode ); } iobuf_put( a, enc->version ); iobuf_put( a, enc->cipher_algo ); iobuf_put( a, enc->s2k.mode ); iobuf_put( a, enc->s2k.hash_algo ); if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) { iobuf_write(a, enc->s2k.salt, 8 ); if( enc->s2k.mode == 3 ) iobuf_put(a, enc->s2k.count); } if( enc->seskeylen ) iobuf_write(a, enc->seskey, enc->seskeylen ); write_header(out, ctb, iobuf_get_temp_length(a) ); rc = iobuf_write_temp( out, a ); iobuf_close(a); return rc; }
/**************** * Make a hash value from the public key certificate */ void hash_public_key( MD_HANDLE md, PKT_public_key *pk ) { PACKET pkt; int rc = 0; int ctb; ulong pktlen; int c; IOBUF a = iobuf_temp(); #if 0 FILE *fp = fopen("dump.pk", "a"); int i=0; fprintf(fp, "\nHashing PK (v%d):\n", pk->version); #endif /* build the packet */ init_packet(&pkt); pkt.pkttype = PKT_PUBLIC_KEY; pkt.pkt.public_key = pk; if( (rc = build_packet( a, &pkt )) ) log_fatal("build public_key for hashing failed: %s\n", g10_errstr(rc)); if( !(pk->version == 3 && pk->pubkey_algo == 16) ) { /* skip the constructed header but don't do this for our very old * v3 ElG keys */ ctb = iobuf_get_noeof(a); pktlen = 0; if( (ctb & 0x40) ) { c = iobuf_get_noeof(a); if( c < 192 ) pktlen = c; else if( c < 224 ) { pktlen = (c - 192) * 256; c = iobuf_get_noeof(a); pktlen += c + 192; } else if( c == 255 ) { pktlen = iobuf_get_noeof(a) << 24; pktlen |= iobuf_get_noeof(a) << 16; pktlen |= iobuf_get_noeof(a) << 8; pktlen |= iobuf_get_noeof(a); } } else { int lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); for( ; lenbytes; lenbytes-- ) { pktlen <<= 8; pktlen |= iobuf_get_noeof(a); } } /* hash a header */ md_putc( md, 0x99 ); pktlen &= 0xffff; /* can't handle longer packets */ md_putc( md, pktlen >> 8 ); md_putc( md, pktlen & 0xff ); }
static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk ) { int rc = 0; int n, i; IOBUF a = iobuf_temp(); if ( !pk->version ) iobuf_put( a, 3 ); else iobuf_put( a, pk->version ); write_32(a, pk->timestamp ); if ( pk->version < 4 ) { u16 ndays; if ( pk->expiredate ) ndays = (u16)((pk->expiredate - pk->timestamp) / 86400L); else ndays = 0; write_16(a, ndays ); } iobuf_put (a, pk->pubkey_algo ); if ( pk->pubkey_algo == PUBKEY_ALGO_NTRU){ rc = sexp_write(a, pk->ntru_pkey); } else { n = pubkey_get_npkey ( pk->pubkey_algo ); if ( !n ) write_fake_data( a, pk->pkey[0] ); } if (!rc) { write_header2 (out, ctb, iobuf_get_temp_length(a), pk->hdrbytes); printf("write output\n"); rc = iobuf_write_temp ( out, a ); } printf("finished writing\n"); iobuf_close(a); return rc; }
/* Build a keyblock image from KEYBLOCK. Returns 0 on success and only then stores a new iobuf object at R_IOBUF and a signature status vecotor at R_SIGSTATUS. */ static gpg_error_t build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus) { gpg_error_t err; iobuf_t iobuf; kbnode_t kbctx, node; u32 n_sigs; u32 *sigstatus; *r_iobuf = NULL; *r_sigstatus = NULL; /* Allocate a vector for the signature cache. This is an array of u32 values with the first value giving the number of elements to follow and each element descriping the cache status of the signature. */ for (kbctx = NULL, n_sigs = 0; (node = walk_kbnode (keyblock, &kbctx, 0));) if (node->pkt->pkttype == PKT_SIGNATURE) n_sigs++; sigstatus = xtrycalloc (1+n_sigs, sizeof *sigstatus); if (!sigstatus) return gpg_error_from_syserror (); iobuf = iobuf_temp (); for (kbctx = NULL, n_sigs = 0; (node = walk_kbnode (keyblock, &kbctx, 0));) { /* Make sure to use only packets valid on a keyblock. */ switch (node->pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SIGNATURE: case PKT_USER_ID: case PKT_ATTRIBUTE: /* Note that we don't want the ring trust packets. They are not useful. */ break; default: continue; } err = build_packet (iobuf, node->pkt); if (err) { iobuf_close (iobuf); return err; } /* Build signature status vector. */ if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; n_sigs++; /* Fixme: Detect tye "missing key" status. */ if (sig->flags.checked) { if (sig->flags.valid) { if (!sig->expiredate) sigstatus[n_sigs] = 0xffffffff; else if (sig->expiredate < 0x1000000) sigstatus[n_sigs] = 0x10000000; else sigstatus[n_sigs] = sig->expiredate; } else sigstatus[n_sigs] = 0x00000002; /* Bad signature. */ } } } sigstatus[0] = n_sigs; *r_iobuf = iobuf; *r_sigstatus = sigstatus; return 0; }
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; }
int main (int argc, char *argv[]) { (void) argc; (void) argv; /* A simple test to make sure filters work. We use a static buffer and then add a filter in front of it that returns every other character. */ { char *content = "0123456789abcdefghijklm"; iobuf_t iobuf; int c; int n; int rc; iobuf = iobuf_temp_with_content (content, strlen (content)); rc = iobuf_push_filter (iobuf, every_other_filter, NULL); assert (rc == 0); n = 0; while ((c = iobuf_readbyte (iobuf)) != -1) { /* printf ("%d: %c\n", n + 1, (char) c); */ assert (content[2 * n + 1] == c); n ++; } /* printf ("Got EOF after reading %d bytes (content: %d)\n", */ /* n, strlen (content)); */ assert (n == strlen (content) / 2); iobuf_close (iobuf); } /* A simple test to check buffering. Make sure that when we add a filter to a pipeline, any buffered data gets processed by the */ { char *content = "0123456789abcdefghijklm"; iobuf_t iobuf; int c; int n; int rc; int i; iobuf = iobuf_temp_with_content (content, strlen (content)); n = 0; for (i = 0; i < 10; i ++) { c = iobuf_readbyte (iobuf); assert (content[i] == c); n ++; } rc = iobuf_push_filter (iobuf, every_other_filter, NULL); assert (rc == 0); while ((c = iobuf_readbyte (iobuf)) != -1) { /* printf ("%d: %c\n", n + 1, (char) c); */ assert (content[2 * (n - 5) + 1] == c); n ++; } assert (n == 10 + (strlen (content) - 10) / 2); iobuf_close (iobuf); } /* A simple test to check that iobuf_read_line works. */ { /* - 3 characters plus new line - 4 characters plus new line - 5 characters plus new line - 5 characters, no new line */ char *content = "abc\ndefg\nhijkl\nmnopq"; iobuf_t iobuf; byte *buffer; unsigned size; unsigned max_len; int n; iobuf = iobuf_temp_with_content (content, strlen(content)); /* We read a line with 3 characters plus a newline. If we allocate a buffer that is 5 bytes long, then no reallocation should be required. */ size = 5; buffer = malloc (size); assert (buffer); max_len = 100; n = iobuf_read_line (iobuf, &buffer, &size, &max_len); assert (n == 4); assert (strcmp (buffer, "abc\n") == 0); assert (size == 5); assert (max_len == 100); free (buffer); /* We now read a line with 4 characters plus a newline. This requires 6 bytes of storage. We pass a buffer that is 5 bytes large and we allow the buffer to be grown. */ size = 5; buffer = malloc (size); max_len = 100; n = iobuf_read_line (iobuf, &buffer, &size, &max_len); assert (n == 5); assert (strcmp (buffer, "defg\n") == 0); assert (size >= 6); /* The string shouldn't have been truncated (max_len == 0). */ assert (max_len == 100); free (buffer); /* We now read a line with 5 characters plus a newline. This requires 7 bytes of storage. We pass a buffer that is 5 bytes large and we don't allow the buffer to be grown. */ size = 5; buffer = malloc (size); max_len = 5; n = iobuf_read_line (iobuf, &buffer, &size, &max_len); assert (n == 4); /* Note: the string should still have a trailing \n. */ assert (strcmp (buffer, "hij\n") == 0); assert (size == 5); /* The string should have been truncated (max_len == 0). */ assert (max_len == 0); free (buffer); /* We now read a line with 6 characters without a newline. This requires 7 bytes of storage. We pass a NULL buffer and we don't allow the buffer to be grown larger than 5 bytes. */ size = 5; buffer = NULL; max_len = 5; n = iobuf_read_line (iobuf, &buffer, &size, &max_len); assert (n == 4); /* Note: the string should still have a trailing \n. */ assert (strcmp (buffer, "mno\n") == 0); assert (size == 5); /* The string should have been truncated (max_len == 0). */ assert (max_len == 0); free (buffer); iobuf_close (iobuf); } { /* - 10 characters, EOF - 17 characters, EOF */ char *content = "abcdefghijklmnopq"; char *content2 = "0123456789"; iobuf_t iobuf; int rc; int c; int n; int lastc = 0; struct content_filter_state *state; iobuf = iobuf_temp_with_content (content, strlen(content)); rc = iobuf_push_filter (iobuf, content_filter, state=content_filter_new (content2)); assert (rc == 0); n = 0; while (1) { c = iobuf_readbyte (iobuf); if (c == -1 && lastc == -1) { /* printf("Two EOFs in a row. Done.\n"); */ assert (n == 27); break; } lastc = c; if (c == -1) { /* printf("After %d bytes, got EOF.\n", n); */ assert (n == 10 || n == 27); } else { n ++; /* printf ("%d: '%c' (%d)\n", n, c, c); */ } } iobuf_close (iobuf); free (state); } /* Write some data to a temporary filter. Push a new filter. The already written data should not be processed by the new filter. */ { iobuf_t iobuf; int rc; char *content = "0123456789"; char *content2 = "abc"; char buffer[4096]; int n; iobuf = iobuf_temp (); assert (iobuf); rc = iobuf_write (iobuf, content, strlen (content)); assert (rc == 0); rc = iobuf_push_filter (iobuf, double_filter, NULL); assert (rc == 0); /* Include a NUL. */ rc = iobuf_write (iobuf, content2, strlen (content2) + 1); assert (rc == 0); n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer)); #if 0 printf ("Got %d bytes\n", n); printf ("buffer: `"); fwrite (buffer, n, 1, stdout); fputc ('\'', stdout); fputc ('\n', stdout); #endif assert (n == strlen (content) + 2 * (strlen (content2) + 1)); assert (strcmp (buffer, "0123456789aabbcc") == 0); iobuf_close (iobuf); } { iobuf_t iobuf; int rc; char *content = "0123456789"; int n; int c; char buffer[strlen (content)]; iobuf = iobuf_temp_with_content (content, strlen (content)); assert (iobuf); rc = iobuf_push_filter (iobuf, every_other_filter, NULL); assert (rc == 0); rc = iobuf_push_filter (iobuf, every_other_filter, NULL); assert (rc == 0); for (n = 0; (c = iobuf_get (iobuf)) != -1; n ++) { /* printf ("%d: `%c'\n", n, c); */ buffer[n] = c; } assert (n == 2); assert (buffer[0] == '3'); assert (buffer[1] == '7'); iobuf_close (iobuf); } return 0; }
/* Returns a keyrec (which must be freed) once a key is complete, and NULL otherwise. Call with a NULL keystring once key parsing is complete to return any unfinished keys. */ static struct keyrec * parse_keyrec(char *keystring) { /* FIXME: Remove the static and put the data into the parms we use for the caller anyway. */ static struct keyrec *work=NULL; struct keyrec *ret=NULL; char *record; int i; if(keystring==NULL) { if(work==NULL) return NULL; else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE) { xfree(work); return NULL; } else { ret=work; work=NULL; return ret; } } if(work==NULL) { work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } trim_trailing_ws (keystring, strlen (keystring)); if((record=strsep(&keystring,":"))==NULL) return ret; if(ascii_strcasecmp("pub",record)==0) { char *tok; gpg_error_t err; if(work->desc.mode) { ret=work; work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } if((tok=strsep(&keystring,":"))==NULL) return ret; err = classify_user_id (tok, &work->desc, 1); if (err || (work->desc.mode != KEYDB_SEARCH_MODE_SHORT_KID && work->desc.mode != KEYDB_SEARCH_MODE_LONG_KID && work->desc.mode != KEYDB_SEARCH_MODE_FPR16 && work->desc.mode != KEYDB_SEARCH_MODE_FPR20)) { work->desc.mode=KEYDB_SEARCH_MODE_NONE; return ret; } /* Note all items after this are optional. This allows us to have a pub line as simple as pub:keyid and nothing else. */ work->lines++; if((tok=strsep(&keystring,":"))==NULL) return ret; work->type=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; work->size=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; if(atoi(tok)<=0) work->createtime=0; else work->createtime=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; if(atoi(tok)<=0) work->expiretime=0; else { work->expiretime=atoi(tok); /* Force the 'e' flag on if this key is expired. */ if(work->expiretime<=make_timestamp()) work->flags|=4; } if((tok=strsep(&keystring,":"))==NULL) return ret; while(*tok) switch(*tok++) { case 'r': case 'R': work->flags|=1; break; case 'd': case 'D': work->flags|=2; break; case 'e': case 'E': work->flags|=4; break; } } else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode) { char *userid,*tok,*decoded; if((tok=strsep(&keystring,":"))==NULL) return ret; if(strlen(tok)==0) return ret; userid=tok; /* By definition, de-%-encoding is always smaller than the original string so we can decode in place. */ i=0; while(*tok) if(tok[0]=='%' && tok[1] && tok[2]) { int c; userid[i] = (c=hextobyte(&tok[1])) == -1 ? '?' : c; i++; tok+=3; } else userid[i++]=*tok++; /* We don't care about the other info provided in the uid: line since no keyserver supports marking userids with timestamps or revoked/expired/disabled yet. */ /* No need to check for control characters, as utf8_to_native does this for us. */ decoded=utf8_to_native(userid,i,0); if(strlen(decoded)>opt.screen_columns-10) decoded[opt.screen_columns-10]='\0'; iobuf_writestr(work->uidbuf,decoded); xfree(decoded); iobuf_writestr(work->uidbuf,"\n\t"); work->lines++; } /* Ignore any records other than "pri" and "uid" for easy future growth. */ return ret; }