Package: libopencdk8
Version: 0.5.5-10

RSA signature-only keys are rejected as invalid, and so are signatures
signed using such keys.

Attached is the demonstrating code (ocdktest.c) and suitable test files.

It should return status 0 for both cases, and it doesn't. Correct behavior:

$ gcc ocdktest.c -lopencdk -lgcrypt
$ ./a.out rsa.key rsatest.gpg
Fingerprint: 2D76 9D3D F112 DF66 5BD9  2E61 F7C6 C0BB EE98 F5C8
$ ./a.out dsa.key dsatest.gpg
Fingerprint: 0B99 97AE 7A3E EED7 0C97  F088 A91F AE60 D3BE 8209

I have fixed the problem myself, see the attached pubkey.c file. Actually, I
have prepared a 0.5.5-11 package, too. Please let me know, if you need it.

It's not a debian-specific problem, and I have already notified the original
author. He has dropped the package a year ago, so you might want to
incorporate the changes before he does.

Being a security related package, I'd suggest to publish a security update
for older versions.

-- 
Daniel
#include <stdio.h>
#include <gcrypt.h>
#include <opencdk.h>

/* opencdk md.h */
cdk_md_hd_t cdk_md_open( int algo, unsigned int flags );
cdk_md_hd_t cdk_md_copy( cdk_md_hd_t md );
void cdk_md_write( cdk_md_hd_t md, const void *buf, size_t n );
void cdk_md_close( cdk_md_hd_t md );
void cdk_md_putc( cdk_md_hd_t md, int c );
int cdk_md_test_algo( int algo );
unsigned char * cdk_md_read( cdk_md_hd_t md, int algo );
int cdk_md_get_algo_dlen( int algo );
int cdk_md_final( cdk_md_hd_t md );
int cdk_md_get_algo( cdk_md_hd_t md );
int cdk_md_get_asnoid( int algo, unsigned char *buf, size_t *n );
int cdk_md_reset( cdk_md_hd_t md);

/* global variables */
cdk_stream_t str;
cdk_packet_t pkt;
cdk_packet_t p;
cdk_error_t err;
cdk_kbnode_t kbn;
unsigned char fpr[20];
cdk_md_hd_t md;

int main(int argc,char *argv[])
{
    unsigned int i;
    if(argc<2){
	fprintf(stderr,"Usage: %s <keyfile> <signed file>\n",argv[0]);
	return -1;
    }
    if(err=cdk_stream_open(argv[1],&str)){
	fprintf(stderr,"Cannot open file: %s (%d)\n",argv[1],err);
	return -1;
    }
    err=cdk_pkt_new(&pkt);
    if(!err) err=cdk_pkt_read(str,pkt);
    cdk_stream_close(str);
    if(err){
	fprintf(stderr,"No valid OpenPGP data in %s (%d)\n",argv[1],err);
	return -1;
    }
    kbn=cdk_kbnode_new(pkt);
    if(!kbn){
	fprintf(stderr,"Invalid OpenPGP key\n");
	return -1;
    }
    
    printf("Fingerprint: ");
    cdk_pk_get_fingerprint(pkt->pkt.public_key,fpr);
    for(i=0;i<20;i++){
	printf("%02X",fpr[i]);
	if(i&1) putchar(' ');
	if(i==9) putchar(' ');
    }
    putchar('\n');
    
    if(argc<3) return 0;
    
    if(err=cdk_stream_open(argv[2],&str)){
	fprintf(stderr,"Cannot open file: %s (%d)\n",argv[2],err);
	return -1;
    }
    err=cdk_pkt_new(&p);
    if(!err) cdk_pkt_read(str,p);
    if(err){
	fprintf(stderr,"No valid OpenPGP data in %s (%d)\n",argv[2],err);
	return -1;
    }
    if(p->pkttype!=CDK_PKT_ONEPASS_SIG){
	fprintf(stderr,"File %s is not a signed OpenPGP file\n",argv[2]);
	return -1;
    }
    md=cdk_md_open(p->pkt.onepass_sig->digest_algo,0);
    cdk_pkt_release(p);
    err=cdk_pkt_new(&p);
    if(!err) cdk_pkt_read(str,p);
    if(err){
	fprintf(stderr,"Invalid OpenPGP data in %s (%d)\n",argv[2],err);
	return -1;
    }
    if(p->pkttype!=CDK_PKT_LITERAL){
	fprintf(stderr,"File %s is an invalid signed OpenPGP file\n",argv[2]);
	return -1;
    }
    i=p->pkt.literal->len;
    while(i--) cdk_md_putc(md,cdk_stream_getc(p->pkt.literal->buf));
    cdk_pkt_release(p);
    err=cdk_pkt_new(&p);
    if(!err) cdk_pkt_read(str,p);
    if(err){
	fprintf(stderr,"Missing OpenPGP signature in %s (%d)\n",argv[2],err);
	return -1;
    }
    if(p->pkttype!=CDK_PKT_SIGNATURE){
	fprintf(stderr,"Invalid OpenPGP signature in %s (%d)\n",argv[2],p->pkttype);
	return -1;
    }
    cdk_md_putc(md,p->pkt.signature->sig_class);
    cdk_md_putc(md,(p->pkt.signature->timestamp>>24)&0xff);
    cdk_md_putc(md,(p->pkt.signature->timestamp>>16)&0xff);
    cdk_md_putc(md,(p->pkt.signature->timestamp>>8)&0xff);
    cdk_md_putc(md,p->pkt.signature->timestamp&0xff);
    if(err=cdk_pk_verify(pkt->pkt.public_key,p->pkt.signature,cdk_md_read(md,p->pkt.signature->digest_algo))){
	fprintf(stderr,"Bad signature (%d)\n",err);
	return -1;
    }
    return 0;
}

Attachment: rsa.key
Description: application/pgp-keys

Attachment: dsa.key
Description: application/pgp-keys

Attachment: rsatest.gpg
Description: Binary data

Attachment: dsatest.gpg
Description: Binary data

/* -*- Mode: C; c-file-style: "bsd" -*-
 * pubkey.c - Public key API
 *        Copyright (C) 2002, 2003 Timo Schulz
 *
 * This file is part of OpenCDK.
 *
 * OpenCDK 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 of the License, or
 * (at your option) any later version.
 *
 * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>

#include "opencdk.h"
#include "main.h"
#include "packet.h"
#include "cipher.h"


static gcry_mpi_t *
convert_to_gcrympi( cdk_mpi_t m[4], int ncount )
{
    gcry_mpi_t * d;
    size_t nbytes = 0;
    int i = 0, rc = 0;

    if( !m || ncount > 4 )
        return NULL;
    d = cdk_calloc( ncount, sizeof *d );
    if( !d )
        return NULL;
    for( i = 0; i < ncount; i++ ) {
        nbytes = m[i]->bytes + 2;
        if( gcry_mpi_scan( &d[i], GCRYMPI_FMT_PGP, m[i]->data, nbytes, &nbytes ) ) {
            rc = CDK_Gcry_Error;
            break;
	}
    }
    if( rc ) {
        _cdk_free_mpibuf( i, d );
        d = NULL;
    }
    return d;
}


static int
seckey_to_sexp( gcry_sexp_t * r_skey, cdk_pkt_seckey_t sk )
{
    gcry_sexp_t sexp = NULL;
    gcry_mpi_t * mpk = NULL, * msk = NULL;
    cdk_pkt_pubkey_t pk;
    const char * fmt = NULL;
    int ncount = 0, nscount = 0;
    int rc = 0;

    if( !r_skey || !sk || !sk->pk )
        return CDK_Inv_Value;

    pk = sk->pk;
    ncount = cdk_pk_get_npkey( pk->pubkey_algo );
    mpk = convert_to_gcrympi( pk->mpi, ncount );
    if( !mpk )
        return CDK_MPI_Error;
    nscount = cdk_pk_get_nskey( sk->pubkey_algo );
    msk = convert_to_gcrympi( sk->mpi, nscount );
    if( !msk )
        rc = CDK_MPI_Error;
    if( !rc && is_RSA( sk->pubkey_algo ) ) {
        fmt = "(private-key(openpgp-rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, mpk[0], mpk[1],
                             msk[0], msk[1], msk[2], msk[3] ) )
            rc = CDK_Gcry_Error;
    }
    else if( !rc && is_ELG( sk->pubkey_algo ) ) {
        fmt = "(private-key(openpgp-elg(p%m)(g%m)(y%m)(x%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, mpk[0], mpk[1],
                             mpk[2], msk[0] ) )
            rc = CDK_Gcry_Error;
    }
    else if( !rc && is_DSA( sk->pubkey_algo ) ) {
        fmt = "(private-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)(x%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, mpk[0], mpk[1], mpk[2],
                             mpk[3], msk[0] ) )
            rc = CDK_Gcry_Error;
    }
    else
        rc = CDK_Inv_Algo;

    _cdk_free_mpibuf( ncount, mpk );
    _cdk_free_mpibuf( nscount, msk );
    *r_skey = sexp;
    return rc;
}


static int
pubkey_to_sexp( gcry_sexp_t * r_key, cdk_pkt_pubkey_t pk )
{
    gcry_sexp_t sexp = NULL;
    gcry_mpi_t * m;
    const char * fmt = NULL;
    int ncount = 0;
    int rc = 0;

    if( !r_key || !pk )
        return CDK_Inv_Value;

    ncount = cdk_pk_get_npkey( pk->pubkey_algo );
    m = convert_to_gcrympi( pk->mpi, ncount );
    if( !m )
        return CDK_MPI_Error;
    if( is_RSA( pk->pubkey_algo ) ) {
        fmt = "(public-key(openpgp-rsa(n%m)(e%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
            rc = CDK_Gcry_Error;
    }
    else if( is_ELG( pk->pubkey_algo ) ) {
        fmt = "(public-key(openpgp-elg(p%m)(g%m)(y%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1], m[2] ) )
            rc = CDK_Gcry_Error;
    }
    else if( is_DSA( pk->pubkey_algo ) ) {
        fmt = "(public-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1], m[2], m[3] ) )
            rc = CDK_Gcry_Error;
    }
    else
        rc = CDK_Inv_Algo;
    _cdk_free_mpibuf( ncount, m );
    *r_key = sexp;
    return rc;
}


static int
enckey_to_sexp( gcry_sexp_t * r_sexp, gcry_mpi_t esk )
{
    gcry_sexp_t sexp = NULL;
    int rc = 0;

    if( !r_sexp || !esk )
        return CDK_Inv_Value;
    if( gcry_sexp_build( &sexp, NULL, "%m", esk ) )
        rc = CDK_Gcry_Error;
    *r_sexp = sexp;
    return rc;
}


static int
digest_to_sexp( gcry_sexp_t * r_md, int algo, const byte * md, size_t mdlen )
{
    gcry_sexp_t sexp = NULL;
    gcry_mpi_t m = NULL;
    size_t nbytes = 0;
    int rc = 0;

    if( !r_md || !md )
        return CDK_Inv_Value;
    nbytes = mdlen ? mdlen : cdk_md_get_algo_dlen( algo );
    if( !nbytes )
        return CDK_Inv_Algo;
    if( gcry_mpi_scan( &m, GCRYMPI_FMT_USG, md, nbytes, &nbytes ) )
        return CDK_Gcry_Error;
    if( gcry_sexp_build( &sexp, NULL, "%m", m ) )
        rc = CDK_Gcry_Error;
    if( !rc )
        *r_md = sexp;
    gcry_mpi_release( m );
    return rc;
}


static int
sexp_to_bitmpi( gcry_sexp_t sexp, const char * val, cdk_mpi_t * ret_buf )
{
    gcry_sexp_t list = NULL;
    gcry_mpi_t m = NULL;
    cdk_mpi_t buf = NULL;
    size_t nbits = 0, nbytes = 0;
    int rc = 0;

    if( !sexp || !val || !ret_buf )
        return CDK_Inv_Value;

    list = gcry_sexp_find_token( sexp, val, 0 );
    if( !list )
        return CDK_Gcry_Error;
    m = gcry_sexp_nth_mpi( list, 1, 0 );
    if( !m ) {
        gcry_sexp_release( list );
        return CDK_Gcry_Error;
    }
    nbits = gcry_mpi_get_nbits( m );
    nbytes = (nbits + 7) / 8;
    buf = cdk_calloc( 1, sizeof *buf + nbytes );
    if( !buf ) {
        rc = CDK_Out_Of_Core;
        goto leave;
    }
    buf->data[0] = nbits >> 8;
    buf->data[1] = nbits;
    if( gcry_mpi_print( GCRYMPI_FMT_USG, NULL, nbytes, &nbytes, m ) )
        rc = CDK_Gcry_Error;
    else
        if( gcry_mpi_print( GCRYMPI_FMT_USG, buf->data + 2, nbytes, &nbytes, m ) )
            rc = CDK_Gcry_Error;
    if( !rc ) {
        buf->bytes = nbytes;
        buf->bits = nbits;
        *ret_buf = buf;
    }

 leave:
    gcry_mpi_release( m );
    gcry_sexp_release( list );
    return rc;
}


static int
sexp_to_sig( cdk_pkt_signature_t sig, gcry_sexp_t sexp)
{
    int rc = 0;

    if( !sig || !sexp )
        return CDK_Inv_Value;

    if( is_RSA( sig->pubkey_algo ) )
        return sexp_to_bitmpi( sexp, "s", &sig->mpi[0] );
    else if( is_DSA( sig->pubkey_algo) || is_ELG( sig->pubkey_algo ) ) {
        rc = sexp_to_bitmpi( sexp, "r", &sig->mpi[0] );
        if( !rc )
            rc = sexp_to_bitmpi( sexp, "s", &sig->mpi[1] );
        return rc;
    }
    return CDK_Inv_Algo;
}


static int
sig_to_sexp( gcry_sexp_t * r_sig, cdk_pkt_signature_t sig )
{
    gcry_sexp_t sexp = NULL;
    gcry_mpi_t * m;
    const char * fmt;
    int ncount = 0;
    int rc = 0;

    if( !r_sig || !sig )
        return CDK_Inv_Value;

    ncount = cdk_pk_get_nsig( sig->pubkey_algo );
    m = convert_to_gcrympi( sig->mpi, ncount );
    if( !m )
        return CDK_MPI_Error;
    if( is_RSA( sig->pubkey_algo ) ) {
        fmt = "(sig-val(openpgp-rsa(s%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0] ) )
            rc = CDK_Gcry_Error;
    }
    else if( is_ELG( sig->pubkey_algo ) ) {
        fmt = "(sig-val(openpgp-elg(r%m)(s%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
            rc = CDK_Gcry_Error;
    }
    else if( is_DSA( sig->pubkey_algo ) ) {
        fmt = "(sig-val(openpgp-dsa(r%m)(s%m)))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
            rc = CDK_Gcry_Error;
    }
    else
        rc = CDK_Inv_Algo;
    _cdk_free_mpibuf( ncount, m );
    *r_sig = sexp;
    return rc;
}


static int
sexp_to_pubenc( cdk_pkt_pubkey_enc_t enc, gcry_sexp_t sexp)
{
    int rc;

    if( !sexp || !enc )
        return CDK_Inv_Value;

    if( is_RSA( enc->pubkey_algo) )
        return sexp_to_bitmpi( sexp, "a", &enc->mpi[0] );
    else if( is_ELG( enc->pubkey_algo) ) {
        rc = sexp_to_bitmpi( sexp, "a", &enc->mpi[0] );
        if( !rc )
            rc = sexp_to_bitmpi( sexp, "b", &enc->mpi[1] );
        return rc;
    }
    return CDK_Inv_Algo;
}


static int
pubenc_to_sexp( gcry_sexp_t * r_sexp, cdk_pkt_pubkey_enc_t enc)
{
    gcry_sexp_t sexp = NULL;
    gcry_mpi_t * m;
    const char * fmt;
    int ncount;
    int rc = 0;

    if( !r_sexp || !enc )
        return CDK_Inv_Value;

    ncount = cdk_pk_get_nenc( enc->pubkey_algo );
    m = convert_to_gcrympi( enc->mpi, ncount );
    if( !m )
        return CDK_MPI_Error;
    if( is_RSA( enc->pubkey_algo ) ) {
        fmt = "(enc-val(openpgp-rsa((a%m))))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0] ) )
            rc = CDK_Gcry_Error;
    }
    else if( is_ELG( enc->pubkey_algo ) ) {
        fmt = "(enc-val(openpgp-elg((a%m)(b%m))))";
        if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
            rc = CDK_Gcry_Error;
    }
    else
        rc = CDK_Inv_Algo;
    _cdk_free_mpibuf( ncount, m );
    *r_sexp = sexp;
    return rc;
}


static int
is_unprotected( cdk_pkt_seckey_t sk )
{
    if( sk->is_protected && !sk->mpi[0] )
        return 0;
    return 1;
}


/**
 * cdk_pk_encrypt:
 * @pk: the public key
 * @pke: the public key encrypted packet
 * @esk: the actual session key
 *
 * Encrypt the session key in @esk and write its encrypted content
 * into the @pke struct.
 **/
cdk_error_t
cdk_pk_encrypt( cdk_pkt_pubkey_t pk, cdk_pkt_pubkey_enc_t pke,
                cdk_sesskey_t esk )
{
    gcry_sexp_t s_data = NULL, s_pkey = NULL, s_ciph = NULL;
    int rc;

    if( !pk || !esk || !pke )
        return CDK_Inv_Value;

    if( !KEY_CAN_ENCRYPT( pk->pubkey_algo ) )
        return CDK_Inv_Algo;

    rc = enckey_to_sexp( &s_data, esk->a );
    if( !rc )
        rc = pubkey_to_sexp( &s_pkey, pk );
    if( !rc )
        rc = gcry_pk_encrypt( &s_ciph, s_data, s_pkey );
    if( !rc )
        rc = sexp_to_pubenc( pke, s_ciph );

    gcry_sexp_release( s_data );
    gcry_sexp_release( s_pkey );
    gcry_sexp_release( s_ciph );
    return rc;
}


/**
 * cdk_pk_decrypt:
 * @sk: the secret key
 * @pke: public key encrypted packet
 * @r_sk: the object to store the plain session key
 *
 * Decrypt the encrypted session key from @pke into @r_sk.
 **/
cdk_error_t
cdk_pk_decrypt( cdk_pkt_seckey_t sk, cdk_pkt_pubkey_enc_t pke,
                cdk_sesskey_t * r_sk )
{
    gcry_sexp_t s_data = NULL, s_skey = NULL, s_plain = NULL;
    int rc;

    if( !sk || !r_sk || !pke )
        return CDK_Inv_Value;

    if( !is_unprotected( sk ) )
        return CDK_Inv_Mode;
  
    rc = seckey_to_sexp( &s_skey, sk );
    if( !rc )
        rc = pubenc_to_sexp( &s_data, pke );
    if( !rc && gcry_pk_decrypt( &s_plain, s_data, s_skey ) )
        rc = CDK_Gcry_Error;
    if( !rc ) {
        rc = cdk_sesskey_new( r_sk );
        if( !rc )
            (*r_sk)->a = gcry_sexp_nth_mpi( s_plain, 0, 0 );
    }

    gcry_sexp_release( s_data );
    gcry_sexp_release( s_skey );
    gcry_sexp_release( s_plain );
    return rc;
}


/**
 * cdk_pk_sign:
 * @sk: secret key
 * @sig: signature
 * @md: the message digest
 *
 * Sign the message digest from @md and write the result into @sig.
 **/
cdk_error_t
cdk_pk_sign( cdk_pkt_seckey_t sk, cdk_pkt_signature_t sig, const byte * md )
{
    gcry_sexp_t s_skey = NULL, s_sig = NULL, s_hash = NULL;
    byte * encmd = NULL;
    size_t enclen = 0;
    int nbits, rc;

    if( !sk || !sk->pk || !sig || !md )
        return CDK_Inv_Value;

    if( !is_unprotected( sk ) )
        return CDK_Inv_Mode;
  
    if( !KEY_CAN_SIGN( sig->pubkey_algo ) )
        return CDK_Inv_Algo;

    nbits = cdk_pk_get_nbits( sk->pk );
    rc = _cdk_digest_encode_pkcs1( &encmd, &enclen, sk->pk->pubkey_algo, md,
                                   sig->digest_algo, nbits );
    if( !rc )
        rc = seckey_to_sexp( &s_skey, sk );
    if( !rc )
        rc = digest_to_sexp( &s_hash, sig->digest_algo, encmd, enclen );
    if( !rc && gcry_pk_sign( &s_sig, s_hash, s_skey ) )
        rc = CDK_Gcry_Error;
    if( !rc )
        rc = sexp_to_sig( sig, s_sig );
    sig->digest_start[0] = md[0];
    sig->digest_start[1] = md[1];

    gcry_sexp_release( s_skey );
    gcry_sexp_release( s_hash );
    gcry_sexp_release( s_sig );
    cdk_free( encmd );
    return rc;
}


/**
 * cdk_pk_verify:
 * @pk: the public key
 * @sig: signature
 * @md: the message digest
 *
 * Verify the signature in @sig and compare it with the message digest in @md.
 **/
cdk_error_t
cdk_pk_verify( cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md)
{
    gcry_sexp_t s_pkey = NULL, s_sig = NULL, s_hash = NULL;
    byte * encmd = NULL;
    size_t enclen = 0;
    int nbits, rc;

    if( !pk || !sig || !md )
        return CDK_Inv_Value;

    nbits = cdk_pk_get_nbits( pk );
    rc = pubkey_to_sexp( &s_pkey, pk );
    if( !rc )
        rc = sig_to_sexp( &s_sig, sig );
    if( !rc )
        rc = _cdk_digest_encode_pkcs1( &encmd, &enclen, pk->pubkey_algo, md,
                                       sig->digest_algo, nbits );
    if( !rc )
        rc = digest_to_sexp( &s_hash, sig->digest_algo, encmd, enclen );
    if( !rc && gcry_pk_verify( s_sig, s_hash, s_pkey ) )
        rc = CDK_Bad_Sig;

    gcry_sexp_release( s_sig );
    gcry_sexp_release( s_hash );
    gcry_sexp_release( s_pkey );
    cdk_free( encmd );
    return rc;
}


int
cdk_pk_get_nbits( cdk_pkt_pubkey_t pk )
{
    if( !pk || !pk->mpi[0] )
        return 0;
    return pk->mpi[0]->bits;
}


int
cdk_pk_get_npkey( int algo )
{
    size_t bytes;

    if (algo == 16)
        algo = 20; /* XXX: libgcrypt returns 0 for 16 */
    if ((algo == 3) || (algo == 2))
        algo = 1;  /* XXX: same thing for RSA */
    if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &bytes))
    	return 0;
    
    return bytes;
}


int
cdk_pk_get_nskey( int algo )
{  
size_t bytes;

    if (algo == 16)
        algo = 20; /* XXX: libgcrypt returns 0 for 16 */
    if ((algo == 3) || (algo == 2))
        algo = 1;  /* XXX: same thing for RSA */
    if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &bytes))
    	return 0;
    
    bytes -= cdk_pk_get_npkey( algo );
    return bytes;
}


int
cdk_pk_get_nsig( int algo )
{
size_t bytes;
    if (algo == 3)
        algo = 1;
    if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &bytes))
    	return 0;
    return bytes;
}


int
cdk_pk_get_nenc( int algo )
{
size_t bytes;

    if (algo == 2)
        algo = 1;
    if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &bytes))
    	return 0;
    return bytes;
}


int
_cdk_pk_algo_usage( int algo )
{
    int usage = 0;

    switch( algo ) {
    case CDK_PK_RSA  : usage = PK_USAGE_SIGN | PK_USAGE_ENCR; break;
    case CDK_PK_RSA_E: usage = PK_USAGE_ENCR; break;
    case CDK_PK_RSA_S: usage = PK_USAGE_SIGN; break;
    case CDK_PK_ELG  : usage = PK_USAGE_SIGN | PK_USAGE_ENCR; break;
    case CDK_PK_ELG_E: usage = PK_USAGE_ENCR; break;
    case CDK_PK_DSA  : usage = PK_USAGE_SIGN; break; 
    }
    return usage;  
}


int
_cdk_pk_test_algo( int algo, unsigned int usage_flags )
{
    size_t n = usage_flags;
  
    if( algo < 0 || algo > 110 )
        return GPG_ERR_INV_PARAMETER;
    return gcry_pk_algo_info( algo, GCRYCTL_TEST_ALGO, NULL, &n );    
}


static int
read_mpi( cdk_mpi_t a, byte * buf, size_t * r_count, size_t * r_nbits )
{
    if( !a || !buf || !r_count )
        return CDK_Inv_Value;
  
    if( a->bytes + 2 > *r_count )
        return CDK_General_Error;
    *r_count = a->bytes + 2;
    memcpy( buf, a->data, *r_count );
    if( r_nbits )
        *r_nbits = a->bits;
    return 0;
}

  
cdk_error_t
cdk_pk_get_mpi( cdk_pkt_pubkey_t pk, int idx,
                byte * buf, size_t * r_count, size_t * r_nbits )
{
    if( !pk || idx < 0 || !r_count )
        return CDK_Inv_Value;
    if( idx > cdk_pk_get_npkey( pk->pubkey_algo ) )
        return CDK_Inv_Value;
    return read_mpi( pk->mpi[idx], buf, r_count, r_nbits );
}


cdk_error_t
cdk_sk_get_mpi( cdk_pkt_seckey_t sk, int idx,
                byte * buf, size_t * r_count, size_t * r_nbits)
{
    if( !sk || idx < 0 || !r_count)
        return CDK_Inv_Value;
    if( idx > cdk_pk_get_nskey( sk->pubkey_algo ) )
        return CDK_Inv_Value;
    return read_mpi( sk->mpi[idx], buf, r_count, r_nbits );
}


static u16
checksum_mpi( cdk_mpi_t m )
{
    int i;
    u16 chksum = 0;

    if( !m )
        return 0;
    for( i = 0; i < m->bytes + 2; i++)
        chksum += m->data[i];
    return chksum;
}


cdk_error_t
cdk_sk_unprotect( cdk_pkt_seckey_t sk, const char * pw )
{
    cdk_cipher_hd_t hd;
    cdk_dek_t dek = NULL;
    cdk_mpi_t a;
    u16 chksum = 0;
    size_t ndata, nbits;
    int j, i, dlen, pos = 0, nskey;
    int rc;
    byte * data = NULL;

    if( !sk )
        return CDK_Inv_Value;

    nskey = cdk_pk_get_nskey( sk->pubkey_algo );
    if( sk->is_protected ) {
        rc = cdk_dek_from_passphrase( &dek, sk->protect.algo,
                                      sk->protect.s2k, 0, pw );
        if( rc )
            return rc;
        hd = cdk_cipher_open( sk->protect.algo, 1,
                              dek->key, dek->keylen,
                              sk->protect.iv, sk->protect.ivlen );
        if( !hd ) {
            cdk_free( dek );
            return CDK_Inv_Algo;
        }
        wipemem( dek, sizeof dek );
        cdk_dek_free( dek );
        chksum = 0;
        if( sk->version == 4 ) {
            ndata = sk->enclen;
            data = cdk_salloc( ndata, 1 );
            if( !data )
                return CDK_Out_Of_Core;
            cdk_cipher_decrypt( hd, data, sk->encdata, ndata );
            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;
                chksum = 1;
                dlen = cdk_md_get_algo_dlen( CDK_MD_SHA1 );
                if( ndata < dlen ) {
                    cdk_free( data );
                    return CDK_Inv_Packet;
                }
                else {
                    cdk_md_hd_t md = cdk_md_open( CDK_MD_SHA1, 1 );
                    if( !md )
                        return CDK_Gcry_Error;
                    cdk_md_write( md, data, ndata - dlen );
                    cdk_md_final( md );
                    if( !memcmp( cdk_md_read( md, CDK_MD_SHA1 ),
                                 data + ndata - dlen, dlen ) )
                        chksum = 0;	/* digest does match */
                    cdk_md_close( md );
		}
	    }
            else {
                for( i = 0; i < ndata - 2; i++)
                    chksum += data[i];
                sk->csum = data[ndata - 2] << 8 | data[ndata - 1];
	    }
            if( sk->csum == chksum ) {
                for( i = 0; i < nskey; i++ ) {
                    nbits = data[pos] << 8 | data[pos + 1];
                    ndata = (nbits + 7) / 8;
                    a = sk->mpi[i] = cdk_salloc( sizeof *a + ndata + 2, 1 );
                    if( !a ) {
                        cdk_free( data );
                        return CDK_Out_Of_Core;
                    }
                    a->bits = nbits;
                    a->bytes = ndata;
                    for( j = 0; j < ndata + 2; j++ )
                        a->data[j] = data[pos++];
		}
	    }
            wipemem( data, sk->enclen );
            cdk_free( data );
	}
        else {
            chksum = 0;
            for( i = 0; i < nskey; i++ ) {
                a = sk->mpi[i];
                cdk_cipher_sync( hd );
                cdk_cipher_decrypt( hd, a->data+2, a->data+2, a->bytes );
                chksum += checksum_mpi( a );
	    }
	}
        cdk_cipher_close( hd );
    }
    else {
        chksum = 0;
        for( i = 0; i < nskey; i++ )
            chksum += checksum_mpi( sk->mpi[i] );
    }
    if( chksum != sk->csum )
        return CDK_Chksum_Error;
    sk->is_protected = 0;
    return 0;
}


cdk_error_t
cdk_sk_protect( cdk_pkt_seckey_t sk, const char * pass )
{
    cdk_cipher_hd_t hd;
    cdk_md_hd_t md;
    cdk_mpi_t a;
    cdk_dek_t dek;
    cdk_s2k_t s2k;
    byte * p;
    size_t enclen = 0, nskey, i;
    int rc;

    rc = cdk_s2k_new( &s2k, 3, CDK_MD_SHA1, NULL );
    if( rc )
        return rc;
    rc = cdk_dek_from_passphrase( &dek, CDK_CIPHER_3DES, s2k, 2, pass );
    if( rc )
        return rc;
    
    nskey = cdk_pk_get_nskey( sk->pubkey_algo  );
    for( i = 0; i < nskey; i++ ) {
        enclen += 2;
        enclen += sk->mpi[i]->bytes;
    }
    p = sk->encdata = cdk_calloc( 1, enclen + 20 + 1  );
    if( !p )
        return CDK_Out_Of_Core;
    enclen = 0;
    for( i = 0; i < nskey; i++ ) {
        a = sk->mpi[i];
        p[enclen++] = a->bits >> 8;
        p[enclen++] = a->bits;
        memcpy( p + enclen, a->data, a->bytes  );
        enclen += a->bytes;
    }
    enclen += 20;
    sk->enclen = enclen;
    sk->protect.s2k = s2k;
    sk->protect.algo = CDK_CIPHER_3DES;
    sk->protect.ivlen = cdk_cipher_get_algo_blklen( sk->protect.algo  );
    gcry_randomize( sk->protect.iv, sk->protect.ivlen, GCRY_STRONG_RANDOM  );
    hd = cdk_cipher_open( sk->protect.algo, 1,
                          dek->key, dek->keylen,
                          sk->protect.iv, sk->protect.ivlen );
    if( !hd ) {
        cdk_free( p );
        return CDK_Gcry_Error;
    }

    md = cdk_md_open( CDK_MD_SHA1, GCRY_CIPHER_SECURE  );
    if( !md ) {
        cdk_cipher_close( hd  );
        cdk_free( p  );
        return CDK_Gcry_Error;
    }
    sk->protect.sha1chk = 1;
    sk->is_protected = 1;
    sk->csum = 0;
    cdk_md_write( md, p, enclen - 20 );
    cdk_md_final( md );
    memcpy( p + enclen - 20, cdk_md_read( md, 0 ), 20 );
    cdk_md_close( md );
    rc = cdk_cipher_encrypt( hd, p, p, enclen );
    cdk_cipher_close( hd );
    cdk_dek_free( dek );
    return rc;
}


/**
 * cdk_pk_from_secret_key:
 * @sk: the secret key
 * @ret_pk: the new public key
 *
 * Create a new public key from a secret key.
 **/
cdk_error_t
cdk_pk_from_secret_key( cdk_pkt_seckey_t sk, cdk_pkt_pubkey_t* ret_pk )
{
    if( !sk )
        return CDK_Inv_Value;
    return _cdk_copy_pubkey( ret_pk, sk->pk );
}


int
cdk_pk_revoke_import( cdk_keydb_hd_t db, const char * revcert )
{
    /* due to the fact the keydb code can't insert or modify packets
       it is not possible to handle this step yet */
    return 0;
}


int
cdk_pk_revoke_create( cdk_pkt_seckey_t sk, int code, const char * inf,
                      char ** ret_revcert )
{
    cdk_md_hd_t md;
    cdk_subpkt_t node;
    cdk_pkt_signature_t sig;
    char * p = NULL, * dat;
    int n;
    
    if( !sk || !ret_revcert )
        return CDK_Inv_Value;
    if( code < 0 || code > 3 )
        return CDK_Inv_Value;

    sig = cdk_calloc( 1, sizeof * sig );
    if( !sig )
        return CDK_Out_Of_Core;
    _cdk_sig_create( sk->pk, sig );
    n = 1;
    if( inf ) {
        n += strlen( p );
        p = cdk_utf8_encode( inf );
    }
    dat = cdk_calloc( 1, n+1 );
    if( !dat ) {
        _cdk_free_signature( sig );
        return CDK_Out_Of_Core;
    }
    dat[0] = code;
    if( inf )
        memcpy( dat+1, p, strlen( p ) );
    cdk_free( p );

    node = cdk_subpkt_new( n );
    if( node ) {
        cdk_subpkt_init( node, CDK_SIGSUBPKT_REVOC_REASON, dat, n );
        cdk_subpkt_add( sig->hashed, node );
    }
    cdk_free( dat );

    md = cdk_md_open( CDK_MD_SHA1, 0 );
    if( !md ) {
        _cdk_free_signature( sig );
        return CDK_Gcry_Error;
    }
    _cdk_hash_pubkey( sk->pk, md, 0 );
    _cdk_free_signature( sig );
    return 0;
}


int
_cdk_sk_get_csum( cdk_pkt_seckey_t sk )
{
    u16 csum = 0, i;

    if( !sk )
        return 0;
    for( i = 0; i < cdk_pk_get_nskey( sk->pubkey_algo ); i++ )
        csum += checksum_mpi( sk->mpi[i] );
    return csum;
}


int
cdk_pk_get_fingerprint( cdk_pkt_pubkey_t pk, byte * fpr )
{
    cdk_md_hd_t hd;
    int md_algo;
    int dlen = 0;

    if( !pk || !fpr )
        return CDK_Inv_Value;

    if( pk->version < 4 && is_RSA( pk->pubkey_algo ) )
        md_algo = CDK_MD_MD5; /* special */
    else
        md_algo = pk->version < 4 ? CDK_MD_RMD160 : CDK_MD_SHA1;
    dlen = cdk_md_get_algo_dlen( md_algo );
    hd = cdk_md_open( md_algo, 0 );
    if( !hd )
        return CDK_Gcry_Error;
    _cdk_hash_pubkey( pk, hd, 1 );
    cdk_md_final( hd );
    memcpy( fpr, cdk_md_read( hd, md_algo ), dlen );
    cdk_md_close( hd );
    if( dlen == 16 )
        memset( fpr + 16, 0, 4 );
    return 0;
}


u32
cdk_pk_fingerprint_get_keyid( const byte * fpr, size_t fprlen, u32 * keyid )
{
    u32 lowbits = 0;

    /* in this case we say the key is a V3 RSA key and we can't
       use the fingerprint to get the keyid. */
    if( fpr && fprlen == 16 )
        return 0;
    else if( keyid && fpr ) {
        keyid[0] = _cdk_buftou32( fpr + 12 );
        keyid[1] = _cdk_buftou32( fpr + 16 );
        lowbits = keyid[1];
    }
    else if( fpr )
        lowbits = _cdk_buftou32( fpr + 16 );
    return lowbits;
}


u32
cdk_pk_get_keyid( cdk_pkt_pubkey_t pk, u32 * keyid )
{
    u32 lowbits = 0;
    byte buf[24];

    if( pk &&( !pk->keyid[0] || !pk->keyid[1] ) ) {
        if( pk->version < 4 && is_RSA( pk->pubkey_algo ) ) {
            size_t n = pk->mpi[0]->bytes;
            const byte * p = pk->mpi[0]->data + 2;
            pk->keyid[0] = p[n-8] << 24 | p[n-7] << 16 | p[n-6] << 8 | p[n-5];
            pk->keyid[1] = p[n-4] << 24 | p[n-3] << 16 | p[n-2] << 8 | p[n-1];
	}
        else if( pk->version == 4 ) {
            cdk_pk_get_fingerprint( pk, buf );
            pk->keyid[0] = _cdk_buftou32( buf + 12 );
            pk->keyid[1] = _cdk_buftou32( buf + 16 );
	}
    }
    lowbits = pk ? pk->keyid[1] : 0;
    if( keyid && pk ) {
        keyid[0] = pk->keyid[0];
        keyid[1] = pk->keyid[1];
    }
    return lowbits;
}


u32
cdk_sk_get_keyid( cdk_pkt_seckey_t sk, u32 * keyid )
{
    u32 lowbits = 0;
    
    if( sk && sk->pk ) {
        lowbits = cdk_pk_get_keyid( sk->pk, keyid );
        sk->keyid[0] = sk->pk->keyid[0];
        sk->keyid[1] = sk->pk->keyid[1];
    }
    return lowbits;
}


u32
cdk_sig_get_keyid( cdk_pkt_signature_t sig, u32 * keyid )
{
    u32 lowbits = sig ? sig->keyid[1] : 0;
  
    if( keyid && sig ) {
        keyid[0] = sig->keyid[0];
        keyid[1] = sig->keyid[1];
    }
    return lowbits;
}


u32
_cdk_pkt_get_keyid( cdk_packet_t pkt, u32 * keyid )
{
    u32 lowbits;

    if( !pkt )
        return 0;
    
    switch( pkt->pkttype ) {
    case CDK_PKT_PUBLIC_KEY:
    case CDK_PKT_PUBLIC_SUBKEY:
        lowbits = cdk_pk_get_keyid( pkt->pkt.public_key, keyid );
        break;
      
    case CDK_PKT_SECRET_KEY:
    case CDK_PKT_SECRET_SUBKEY:
        lowbits = cdk_sk_get_keyid( pkt->pkt.secret_key, keyid );
        break;

    case CDK_PKT_SIGNATURE:
        lowbits = cdk_sig_get_keyid( pkt->pkt.signature, keyid );
        break;
      
    default:
        lowbits = 0;
    }
    return lowbits;
}


int
_cdk_pkt_get_fingerprint( cdk_packet_t pkt, byte * fpr )
{
    if( !pkt || !fpr )
        return CDK_Inv_Value;
    
    switch( pkt->pkttype ) {
    case CDK_PKT_PUBLIC_KEY:
    case CDK_PKT_PUBLIC_SUBKEY:
        return cdk_pk_get_fingerprint( pkt->pkt.public_key, fpr );

    case CDK_PKT_SECRET_KEY:
    case CDK_PKT_SECRET_SUBKEY:
        return cdk_pk_get_fingerprint( pkt->pkt.secret_key->pk, fpr );

    default:
        return CDK_Inv_Packet;
    }
    return 0;
}

Attachment: signature.asc
Description: Digital signature

Reply via email to