release 0.2.3
[gnupg.git] / g10 / seckey-cert.c
index 3cae571..855e3d1 100644 (file)
 #include "keydb.h"
 #include "cipher.h"
 
+#if  BLOWFISH_BLOCKSIZE != 8
+  #error unsupportted blocksize
+#endif
 
 static u16
-checksum( byte *p )
+checksum_u16( unsigned n )
 {
-    u16 n, a;
+    u16 a;
+
+    a  = (n >> 8) & 0xff;
+    a |= n & 0xff;
+    return a;
+}
+
+static u16
+checksum( byte *p, unsigned n )
+{
+    u16 a;
 
-    n = *p++ << 8;
-    n |= *p++;
     for(a=0; n; n-- )
        a += *p++;
     return a;
 }
 
 
-/****************
- * Check the secret key certificate
- */
-int
-check_secret_key( PKT_seckey_cert *cert )
+
+static int
+check_elg( PKT_secret_cert *cert )
 {
-    IDEA_context idea_ctx;  /* FIXME: allocate this in secure space ! */
-    byte iv[8];
-    byte *mpibuf;
-    u16 n;
-    MPI temp_mpi;
+    byte *buffer;
+    u16 csum=0;
     int res;
+    unsigned nbytes;
     u32 keyid[2];
+    ELG_secret_key skey;
+    char save_iv[8];
 
-#if IDEA_BLOCKSIZE != 8 || BLOWFISH_BLOCKSIZE != 8
-  #error unsupportted blocksize
-#endif
+    if( cert->d.elg.is_protected ) { /* remove the protection */
+       DEK *dek = NULL;
+       MPI test_x;
+       BLOWFISH_context *blowfish_ctx=NULL;
+
+       switch( cert->d.elg.protect_algo ) {
+         case CIPHER_ALGO_NONE: BUG(); break;
+         case CIPHER_ALGO_BLOWFISH:
+           keyid_from_skc( cert, keyid );
+           dek = get_passphrase_hash( keyid, NULL );
+           blowfish_ctx = m_alloc_secure( sizeof *blowfish_ctx );
+           blowfish_setkey( blowfish_ctx, dek->key, dek->keylen );
+           m_free(dek); /* pw is in secure memory, so m_free() burns it */
+           blowfish_setiv( blowfish_ctx, NULL );
+           memcpy(save_iv, cert->d.elg.protect.blowfish.iv, 8 );
+           blowfish_decode_cfb( blowfish_ctx,
+                                cert->d.elg.protect.blowfish.iv,
+                                cert->d.elg.protect.blowfish.iv, 8 );
+           mpi_set_secure(cert->d.elg.x );
+           /*fixme: maybe it is better to set the buger secure with a
+            * new get_buffer_secure() function */
+           buffer = mpi_get_buffer( cert->d.elg.x, &nbytes, NULL );
+           csum = checksum_u16( nbytes*8 );
+           blowfish_decode_cfb( blowfish_ctx, buffer, buffer, nbytes );
+           csum += checksum( buffer, nbytes );
+           test_x = mpi_alloc_secure( mpi_get_nlimbs(cert->d.elg.x) );
+           mpi_set_buffer( test_x, buffer, nbytes, 0 );
+           m_free( buffer );
+           m_free( blowfish_ctx );
+           /* now let's see wether we have used the right passphrase */
+           if( csum != cert->d.elg.csum ) {
+               mpi_free(test_x);
+               memcpy( cert->d.elg.protect.blowfish.iv, save_iv, 8 );
+               return G10ERR_BAD_PASS;
+           }
+
+           skey.p = cert->d.elg.p;
+           skey.g = cert->d.elg.g;
+           skey.y = cert->d.elg.y;
+           skey.x = test_x;
+           res = elg_check_secret_key( &skey );
+           memset( &skey, 0, sizeof skey );
+           if( !res ) {
+               mpi_free(test_x);
+               memcpy( cert->d.elg.protect.blowfish.iv, save_iv, 8 );
+               return G10ERR_BAD_PASS;
+           }
+           mpi_set(cert->d.elg.x, test_x);
+           mpi_free(test_x);
+           cert->d.elg.is_protected = 0;
+           break;
+
+         default:
+           return G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
+       }
+    }
+    else { /* not protected */
+       buffer = mpi_get_buffer( cert->d.elg.x, &nbytes, NULL );
+       csum = checksum_u16( nbytes*8 );
+       csum += checksum( buffer, nbytes );
+       m_free( buffer );
+       if( csum != cert->d.elg.csum )
+           return G10ERR_CHECKSUM;
+    }
+
+    return 0;
+}
+
+static int
+protect_elg( PKT_secret_cert *cert, DEK *dek )
+{
+    byte *buffer;
+    unsigned nbytes;
+
+    if( !cert->d.elg.is_protected ) { /* add the protection */
+       BLOWFISH_context *blowfish_ctx=NULL;
+
+       switch( cert->d.elg.protect_algo ) {
+         case CIPHER_ALGO_NONE: BUG(); break;
+         case CIPHER_ALGO_BLOWFISH:
+           blowfish_ctx = m_alloc_secure( sizeof *blowfish_ctx );
+           blowfish_setkey( blowfish_ctx, dek->key, dek->keylen );
+           blowfish_setiv( blowfish_ctx, NULL );
+           blowfish_encode_cfb( blowfish_ctx,
+                                cert->d.elg.protect.blowfish.iv,
+                                cert->d.elg.protect.blowfish.iv, 8 );
+           buffer = mpi_get_buffer( cert->d.elg.x, &nbytes, NULL );
+           blowfish_encode_cfb( blowfish_ctx, buffer, buffer, nbytes );
+           mpi_set_buffer( cert->d.elg.x, buffer, nbytes, 0 );
+           m_free( buffer );
+           m_free( blowfish_ctx );
+           cert->d.elg.is_protected = 1;
+           break;
+
+         default:
+           return G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
+       }
+    }
+    return 0;
+}
 
-    if( cert->pubkey_algo != PUBKEY_ALGO_RSA )
-       return G10ERR_PUBKEY_ALGO; /* unsupport algorithm */
+
+#ifdef HAVE_RSA_CIPHER
+static int
+check_rsa( PKT_secret_cert *cert )
+{
+    byte *buffer;
+    u16 csum=0;
+    int res;
+    unsigned nbytes;
+    u32 keyid[2];
+    RSA_secret_key skey;
 
     if( cert->d.rsa.is_protected ) { /* remove the protection */
        DEK *dek = NULL;
        BLOWFISH_context *blowfish_ctx=NULL;
 
        switch( cert->d.rsa.protect_algo ) {
-         case CIPHER_ALGO_NONE:
-           log_bug("unprotect seckey_cert is flagged protected\n");
-           break;
-         case CIPHER_ALGO_IDEA:
+           /* FIXME: use test variables to check for the correct key */
+         case CIPHER_ALGO_NONE: BUG(); break;
          case CIPHER_ALGO_BLOWFISH:
-           mpi_get_keyid( cert->d.rsa.rsa_n , keyid );
+           keyid_from_skc( cert, keyid );
            dek = get_passphrase_hash( keyid, NULL );
-
-         /*  idea_setkey( &idea_ctx, dpw );*/
+           blowfish_ctx = m_alloc_secure( sizeof *blowfish_ctx );
+           blowfish_setkey( blowfish_ctx, dek->key, dek->keylen );
            m_free(dek); /* pw is in secure memory, so m_free() burns it */
-           memset( iv, 0, BLOWFISH_BLOCKSIZE );
-           if( cert->d.rsa.protect_algo == CIPHER_ALGO_IDEA ) {
-               idea_setiv( &idea_ctx, iv );
-               /* fixme: is it save to leave the IV unencrypted in the
-                * certificate or should we move it to secure storage? */
-               idea_decode_cfb( &idea_ctx, cert->d.rsa.protect.idea.iv,
-                                           cert->d.rsa.protect.idea.iv, 8 );
-           }
-           else {
-               blowfish_ctx = m_alloc_secure( sizeof *blowfish_ctx );
-               blowfish_setiv( blowfish_ctx, iv );
-               blowfish_decode_cfb( blowfish_ctx,
-                                    cert->d.rsa.protect.blowfish.iv,
-                                    cert->d.rsa.protect.blowfish.iv, 8 );
-           }
-           cert->d.rsa.calc_csum = 0;
-         #define X(a) do {                                             \
-                   mpibuf = (byte*)cert->d.rsa.rsa_##a;                \
-                   n = ((mpibuf[0] << 8) | mpibuf[1])-2;               \
-                   if( blowfish_ctx )                                  \
-                       blowfish_decode_cfb( blowfish_ctx,              \
-                                            mpibuf+4, mpibuf+4, n );   \
-                   else                                                 \
-                       idea_decode_cfb( &idea_ctx, mpibuf+4, mpibuf+4, n );\
-                   cert->d.rsa.calc_csum += checksum( mpibuf );        \
-                   cert->d.rsa.rsa_##a = mpi_decode_buffer( mpibuf );  \
-                   m_free( mpibuf );                                   \
-               } while(0)
-           X(d);
-           X(p);
-           X(q);
-           X(u);
-         #undef X
-           m_free( blowfish_ctx );
-           cert->d.rsa.is_protected = 0;
-         #if 0
-           #define X(a) do { printf("\tRSA " #a ": ");                   \
-                             mpi_print(stdout, cert->d.rsa.rsa_##a, 1 ); \
-                             putchar('\n');                              \
-                           } while(0)
-           X(n);
-           X(e);
+           blowfish_setiv( blowfish_ctx, NULL );
+           blowfish_decode_cfb( blowfish_ctx,
+                                cert->d.rsa.protect.blowfish.iv,
+                                cert->d.rsa.protect.blowfish.iv, 8 );
+           csum = 0;
+           #define X(a) do { \
+               mpi_set_secure(cert->d.rsa.rsa_##a); \
+               buffer = mpi_get_buffer( cert->d.rsa.rsa_##a, &nbytes, NULL );\
+               csum += checksum_u16( nbytes*8 );                            \
+               blowfish_decode_cfb( blowfish_ctx, buffer, buffer, nbytes ); \
+               csum += checksum( buffer, nbytes );                          \
+               mpi_set_buffer(cert->d.rsa.rsa_##a, buffer, nbytes, 0 );     \
+               m_free( buffer );                                            \
+              } while(0)
            X(d);
            X(p);
            X(q);
            X(u);
            #undef X
-         #endif
+           cert->d.rsa.is_protected = 0;
+           m_free( blowfish_ctx );
            /* now let's see wether we have used the right passphrase */
-           if( cert->d.rsa.calc_csum != cert->d.rsa.csum )
+           if( csum != cert->d.rsa.csum )
                return G10ERR_BAD_PASS;
-           temp_mpi = mpi_alloc(40);
-           mpi_mul(temp_mpi, cert->d.rsa.rsa_p, cert->d.rsa.rsa_q );
-           res = mpi_cmp( temp_mpi, cert->d.rsa.rsa_n );
-           mpi_free(temp_mpi);
-           if( res )
+
+           skey.d = cert->d.rsa.rsa_d;
+           skey.p = cert->d.rsa.rsa_p;
+           skey.q = cert->d.rsa.rsa_q;
+           skey.u = cert->d.rsa.rsa_u;
+           res = rsa_check_secret_key( &skey );
+           memset( &skey, 0, sizeof skey );
+           if( !res )
                return G10ERR_BAD_PASS;
            break;
 
          default:
-           return G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
+           return G10ERR_CIPHER_ALGO; /* unsupported protection algorithm */
        }
     }
-    /* must check the checksum here, because we didn't do it when
-     * parsing an unprotected certificate */
-    if( cert->d.rsa.calc_csum != cert->d.rsa.csum ) {
-       log_error("checksum in secret key certificate is wrong\n");
-       log_debug("stored csum=%04hx calculated csum=%04hx\n",
-                  cert->d.rsa.csum, cert->d.rsa.calc_csum );
-       return G10ERR_CHECKSUM;
+    else { /* not protected */
+       csum =0;
+       buffer = mpi_get_buffer( cert->d.rsa.rsa_d, &nbytes, NULL );
+       csum += checksum_u16( nbytes*8 );
+       csum += checksum( buffer, nbytes );
+       m_free( buffer );
+       buffer = mpi_get_buffer( cert->d.rsa.rsa_p, &nbytes, NULL );
+       csum += checksum_u16( nbytes*8 );
+       csum += checksum( buffer, nbytes );
+       m_free( buffer );
+       buffer = mpi_get_buffer( cert->d.rsa.rsa_q, &nbytes, NULL );
+       csum += checksum_u16( nbytes*8 );
+       csum += checksum( buffer, nbytes );
+       m_free( buffer );
+       buffer = mpi_get_buffer( cert->d.rsa.rsa_u, &nbytes, NULL );
+       csum += checksum_u16( nbytes*8 );
+       csum += checksum( buffer, nbytes );
+       m_free( buffer );
+       if( csum != cert->d.rsa.csum )
+           return G10ERR_CHECKSUM;
     }
+
     return 0;
 }
+#endif /*HAVE_RSA_CIPHER*/
+
+
 
 
+/****************
+ * Check the secret key certificate
+ * Ask up to 3 time for a correct passphrase
+ */
+int
+check_secret_key( PKT_secret_cert *cert )
+{
+    int rc = G10ERR_BAD_PASS;
+    int i;
+
+    for(i=0; i < 3 && rc == G10ERR_BAD_PASS; i++ ) {
+       if( i )
+           log_error("Invalid passphrase; please try again ...\n");
+       if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL )
+           rc = check_elg( cert );
+      #ifdef HAVE_RSA_CIPHER
+       else if( cert->pubkey_algo == PUBKEY_ALGO_RSA )
+           rc = check_rsa( cert );
+      #endif
+       else
+           rc = G10ERR_PUBKEY_ALGO;
+       if( get_passphrase_fd() != -1 )
+           break;
+    }
+    return rc;
+}
+
+/****************
+ * check wether the secret key is protected.
+ * Returns: 0 not protected, -1 on error or the protection algorithm
+ */
+int
+is_secret_key_protected( PKT_secret_cert *cert )
+{
+    if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL )
+       return cert->d.elg.is_protected? cert->d.elg.protect_algo : 0;
+  #ifdef HAVE_RSA_CIPHER
+    else if( cert->pubkey_algo == PUBKEY_ALGO_RSA )
+       return cert->d.rsa.is_protected? cert->d.rsa.protect_algo : 0;
+  #endif
+    else
+       return -1; /* unsupported */
+}
+
+
+/****************
+ * Protect the secret key certificate with the passphrase from DEK
+ */
+int
+protect_secret_key( PKT_secret_cert *cert, DEK *dek )
+{
+    if( !dek )
+       return 0;
+
+    if( cert->pubkey_algo == PUBKEY_ALGO_ELGAMAL )
+       return protect_elg( cert, dek );
+  #if 0 /* noy yet implemented */
+    else if( cert->pubkey_algo == PUBKEY_ALGO_RSA )
+       return protect_rsa( cert, dek );
+  #endif
+    else
+       return G10ERR_PUBKEY_ALGO;
+}
+