- int rc;
- MPI plain_dek = NULL;
- byte *frame = NULL;
- unsigned n, nframe;
- u16 csum, csum2;
-
- rc = pubkey_decrypt(sk->pubkey_algo, &plain_dek, k->data, sk->skey );
- if( rc )
- goto leave;
- frame = mpi_get_buffer( plain_dek, &nframe, NULL );
- mpi_free( plain_dek ); plain_dek = NULL;
-
- /* Now get the DEK (data encryption key) from the frame
- *
- * Old versions encode the DEK in in this format (msb is left):
- *
- * 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2
- *
- * Later versions encode the DEK like this:
- *
- * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
- *
- * (mpi_get_buffer already removed the leading zero).
- *
- * RND are non-zero randow bytes.
- * A is the cipher algorithm
- * DEK is the encryption key (session key) with length k
- * CSUM
- */
- if( DBG_CIPHER )
- log_hexdump("DEK frame:", frame, nframe );
- n=0;
- if( n + 7 > nframe )
- { rc = G10ERR_WRONG_SECKEY; goto leave; }
- if( frame[n] == 1 && frame[nframe-1] == 2 ) {
- log_info(_("old encoding of the DEK is not supported\n"));
- rc = G10ERR_CIPHER_ALGO;
- goto leave;
+ gpg_error_t err;
+ byte *frame = NULL;
+ unsigned int n;
+ size_t nframe;
+ u16 csum, csum2;
+ int padding;
+ gcry_sexp_t s_data;
+ char *desc;
+ char *keygrip;
+ byte fp[MAX_FINGERPRINT_LEN];
+ size_t fpn;
+
+ if (DBG_CLOCK)
+ log_clock ("decryption start");
+
+ /* Get the keygrip. */
+ err = hexkeygrip_from_pk (sk, &keygrip);
+ if (err)
+ goto leave;
+
+ /* Convert the data to an S-expression. */
+ if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
+ || sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E)
+ {
+ if (!enc->data[0] || !enc->data[1])
+ err = gpg_error (GPG_ERR_BAD_MPI);
+ else
+ err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))",
+ enc->data[0], enc->data[1]);
+ }
+ else if (sk->pubkey_algo == PUBKEY_ALGO_RSA
+ || sk->pubkey_algo == PUBKEY_ALGO_RSA_E)
+ {
+ if (!enc->data[0])
+ err = gpg_error (GPG_ERR_BAD_MPI);
+ else
+ err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))",
+ enc->data[0]);
+ }
+ else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
+ {
+ if (!enc->data[0] || !enc->data[1])
+ err = gpg_error (GPG_ERR_BAD_MPI);
+ else
+ err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))",
+ enc->data[1], enc->data[0]);
+ }
+ else
+ err = gpg_error (GPG_ERR_BUG);
+
+ if (err)
+ goto leave;
+
+ if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
+ {
+ fingerprint_from_pk (sk, fp, &fpn);
+ log_assert (fpn == 20);
+ }
+
+ /* Decrypt. */
+ desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1);
+ err = agent_pkdecrypt (NULL, keygrip,
+ desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
+ s_data, &frame, &nframe, &padding);
+ xfree (desc);
+ gcry_sexp_release (s_data);
+ if (err)
+ goto leave;
+
+ /* Now get the DEK (data encryption key) from the frame
+ *
+ * Old versions encode the DEK in this format (msb is left):
+ *
+ * 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2
+ *
+ * Later versions encode the DEK like this:
+ *
+ * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
+ *
+ * (mpi_get_buffer already removed the leading zero).
+ *
+ * RND are non-zero randow bytes.
+ * A is the cipher algorithm
+ * DEK is the encryption key (session key) with length k
+ * CSUM
+ */
+ if (DBG_CRYPTO)
+ log_printhex (frame, nframe, "DEK frame:");
+ n = 0;
+
+ if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
+ {
+ gcry_mpi_t shared_mpi;
+ gcry_mpi_t decoded;
+
+ /* At the beginning the frame are the bytes of shared point MPI. */
+ err = gcry_mpi_scan (&shared_mpi, GCRYMPI_FMT_USG, frame, nframe, NULL);
+ if (err)
+ {
+ err = gpg_error (GPG_ERR_WRONG_SECKEY);
+ goto leave;
+ }
+
+ err = pk_ecdh_decrypt (&decoded, fp, enc->data[1]/*encr data as an MPI*/,
+ shared_mpi, sk->pkey);
+ mpi_release (shared_mpi);
+ if(err)
+ goto leave;
+
+ xfree (frame);
+ err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded);
+ mpi_release (decoded);
+ if (err)
+ goto leave;
+
+ /* Now the frame are the bytes decrypted but padded session key. */
+
+ /* Allow double padding for the benefit of DEK size concealment.
+ Higher than this is wasteful. */
+ if (!nframe || frame[nframe-1] > 8*2 || nframe <= 8
+ || frame[nframe-1] > nframe)
+ {
+ err = gpg_error (GPG_ERR_WRONG_SECKEY);
+ goto leave;
+ }
+ nframe -= frame[nframe-1]; /* Remove padding. */
+ log_assert (!n); /* (used just below) */