Integrating http://code.google.com/p/gnupg-ecc/source/detail?r=15 .
[gnupg.git] / g10 / ecdh.c
1 /* ecdh.c - ECDH public key operations used in public key glue code
2  *      Copyright (C) 2000, 2003 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <assert.h>
26
27 #include "gpg.h"
28 #include "util.h"
29 #include "pkglue.h"
30 #include "main.h"
31 #include "options.h"
32
33 gcry_mpi_t
34 pk_ecdh_default_params_to_mpi( int qbits )  {
35   gpg_error_t err;
36   gcry_mpi_t result;
37   /* Defaults are the strongest possible choices. Performance is not an issue here, only interoperability. */
38   byte kek_params[4] = { 
39         3       /*size of following field*/, 
40         1       /*fixed version for KDF+AESWRAP*/, 
41         DIGEST_ALGO_SHA512      /* KEK MD */, 
42         CIPHER_ALGO_AES256      /*KEK AESWRAP alg*/
43   };
44   int i;
45
46   static const struct {
47     int qbits;
48     int openpgp_hash_id;
49     int openpgp_cipher_id;
50   } kek_params_table[] = {
51     { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES    },
52     { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 },
53     { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 }     // 528 is 521 rounded to the 8 bit boundary
54   };
55
56   for( i=0; i<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++ )  {
57     if( kek_params_table[i].qbits >= qbits )  {
58       kek_params[2] = kek_params_table[i].openpgp_hash_id;
59       kek_params[3] = kek_params_table[i].openpgp_cipher_id;
60       break;
61     }
62   }
63   if( DBG_CIPHER )
64       log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params) );
65
66   err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, kek_params, sizeof(kek_params), NULL);
67   if (err)
68     log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
69
70   return result;
71 }
72
73 /* returns allocated (binary) KEK parameters; the size is returned in sizeout. 
74  * The caller must free returned value with xfree. 
75  * Returns NULL on error 
76  */
77 byte *
78 pk_ecdh_default_params( int qbits, size_t *sizeout )  {
79   gpg_error_t err;
80   gcry_mpi_t result;
81   /* Defaults are the strongest possible choices. Performance is not an issue here, only interoperability. */
82   byte kek_params[4] = { 
83         3       /*size of following field*/, 
84         1       /*fixed version for KDF+AESWRAP*/, 
85         DIGEST_ALGO_SHA512      /* KEK MD */, 
86         CIPHER_ALGO_AES256      /*KEK AESWRAP alg*/
87   };
88   int i;
89
90   static const struct {
91     int qbits;
92     int openpgp_hash_id;
93     int openpgp_cipher_id;
94   } kek_params_table[] = {
95     { 256, DIGEST_ALGO_SHA256, CIPHER_ALGO_AES    },
96     { 384, DIGEST_ALGO_SHA384, CIPHER_ALGO_AES256 },
97     { 528, DIGEST_ALGO_SHA512, CIPHER_ALGO_AES256 }     // 528 is 521 rounded to the 8 bit boundary
98   };
99
100   byte *p;
101
102   *sizeout = 0;
103
104   for( i=0; i<sizeof(kek_params_table)/sizeof(kek_params_table[0]); i++ )  {
105     if( kek_params_table[i].qbits >= qbits )  {
106       kek_params[2] = kek_params_table[i].openpgp_hash_id;
107       kek_params[3] = kek_params_table[i].openpgp_cipher_id;
108       break;
109     }
110   }
111   if( DBG_CIPHER )
112       log_printhex ("ecdh kek params are", kek_params, sizeof(kek_params) );
113
114   p = xtrymalloc( sizeof(kek_params) );
115   if( p == NULL )
116     return NULL;
117   memcpy( p, kek_params, sizeof(kek_params) );
118   *sizeout = sizeof(kek_params);
119   return p;
120 }
121
122 /* Encrypts/decrypts 'data' with a key derived from shared_mpi ECC point using FIPS SP 800-56A compliant method, which is
123  * key derivation + key wrapping. The direction is determined by the first parameter (is_encrypt=1 --> this is encryption).
124  * The result is returned in out as a size+value MPI.
125  * TODO: memory leaks (x_secret).
126  */
127 static int
128 pk_ecdh_encrypt_with_shared_point ( int is_encrypt, gcry_mpi_t shared_mpi, 
129         const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t data, gcry_mpi_t * pkey, gcry_mpi_t *out)
130 {
131   byte *secret_x;
132   int secret_x_size;
133   byte kdf_params[256];
134   int kdf_params_size=0;
135   int nbits;
136   int kdf_hash_algo;
137   int kdf_encr_algo;
138   int rc;
139
140   *out = NULL;
141
142   nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey );
143
144   {
145     size_t nbytes;
146     /* extract x component of the shared point: this is the actual shared secret */
147     nbytes = (mpi_get_nbits (pkey[1] /* public point */)+7)/8;
148     secret_x = xmalloc_secure( nbytes );
149     rc = gcry_mpi_print (GCRYMPI_FMT_USG, secret_x, nbytes, &nbytes, shared_mpi);
150     if( rc )  {
151       xfree( secret_x );
152       log_error ("ec ephemeral export of shared point failed: %s\n", gpg_strerror (rc) );
153       return rc;
154     }
155     secret_x_size = (nbits+7)/8; 
156     assert( nbytes > secret_x_size );
157     memmove( secret_x, secret_x+1, secret_x_size );
158     memset( secret_x+secret_x_size, 0, nbytes-secret_x_size );
159
160     if( DBG_CIPHER )
161         log_printhex ("ecdh shared secret X is:", secret_x, secret_x_size );
162   }
163
164   /*** We have now the shared secret bytes in secret_x ***/
165
166   /* At this point we are done with PK encryption and the rest of the function uses symmetric 
167    *  key encryption techniques to protect the input 'data'. The following two sections will
168    *  simply replace current secret_x with a value derived from it. This will become a KEK. 
169    */
170   {
171     IOBUF obuf = iobuf_temp(); 
172     rc = iobuf_write_size_body_mpi ( obuf, pkey[2]  );  /* KEK params */
173
174     kdf_params_size = iobuf_temp_to_buffer( obuf, kdf_params, sizeof(kdf_params) );
175
176     if( DBG_CIPHER )
177         log_printhex ("ecdh KDF public key params are:", kdf_params, kdf_params_size );
178
179     if( kdf_params_size != 4 || kdf_params[0] != 3 || kdf_params[1] != 1  )     /* expect 4 bytes  03 01 hash_alg symm_alg */
180       return GPG_ERR_BAD_PUBKEY;
181
182     kdf_hash_algo = kdf_params[2];
183     kdf_encr_algo = kdf_params[3];
184
185     if( DBG_CIPHER )
186       log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n", gcry_md_algo_name (kdf_hash_algo), openpgp_cipher_algo_name (kdf_encr_algo) );
187
188     if( kdf_hash_algo != GCRY_MD_SHA256 && kdf_hash_algo != GCRY_MD_SHA384 && kdf_hash_algo != GCRY_MD_SHA512 )
189       return GPG_ERR_BAD_PUBKEY;
190     if( kdf_encr_algo != GCRY_CIPHER_AES128 && kdf_encr_algo != GCRY_CIPHER_AES192 && kdf_encr_algo != GCRY_CIPHER_AES256 )
191       return GPG_ERR_BAD_PUBKEY;
192   }
193
194   /* build kdf_params */
195   {
196     IOBUF obuf;
197
198     obuf = iobuf_temp();
199     /* variable-length field 1, curve name OID */
200     rc = iobuf_write_size_body_mpi ( obuf, pkey[0] );
201     /* fixed-length field 2 */
202     iobuf_put (obuf, PUBKEY_ALGO_ECDH);
203     /* variable-length field 3, KDF params */
204     rc = (rc ? rc : iobuf_write_size_body_mpi ( obuf, pkey[2] ));
205     /* fixed-length field 4 */
206     iobuf_write (obuf, "Anonymous Sender    ", 20);
207     /* fixed-length field 5, recipient fp */
208     iobuf_write (obuf, pk_fp, 20);      
209
210     kdf_params_size = iobuf_temp_to_buffer( obuf, kdf_params, sizeof(kdf_params) );
211     iobuf_close( obuf );
212     if( rc )  {
213       return rc;
214     }
215     if( DBG_CIPHER )
216         log_printhex ("ecdh KDF message params are:", kdf_params, kdf_params_size );
217   }
218
219   /* Derive a KEK (key wrapping key) using kdf_params and secret_x. */
220   {
221     gcry_md_hd_t h;
222     int old_size;
223
224     rc = gcry_md_open (&h, kdf_hash_algo, 0);
225     if(rc)
226         log_bug ("gcry_md_open failed for algo %d: %s",
227                         kdf_hash_algo, gpg_strerror (gcry_error(rc)));
228     gcry_md_write(h, "\x00\x00\x00\x01", 4);    /* counter = 1 */
229     gcry_md_write(h, secret_x, secret_x_size);  /* x of the point X */
230     gcry_md_write(h, kdf_params, kdf_params_size);      /* KDF parameters */
231
232     gcry_md_final (h);
233
234     assert( gcry_md_get_algo_dlen (kdf_hash_algo) >= 32 );
235
236     memcpy (secret_x, gcry_md_read (h, kdf_hash_algo), gcry_md_get_algo_dlen (kdf_hash_algo));
237     gcry_md_close (h);
238
239     old_size = secret_x_size;
240     assert( old_size >= gcry_cipher_get_algo_keylen( kdf_encr_algo ) );
241     secret_x_size = gcry_cipher_get_algo_keylen( kdf_encr_algo );
242     assert( secret_x_size <= gcry_md_get_algo_dlen (kdf_hash_algo) );
243
244     memset( secret_x+secret_x_size, old_size-secret_x_size, 0 );        /* we could have allocated more, so clean the tail before returning */
245     if( DBG_CIPHER )
246       log_printhex ("ecdh KEK is:", secret_x, secret_x_size );
247    }
248
249   /* And, finally, aeswrap with key secret_x */
250   {
251     gcry_cipher_hd_t hd;
252     size_t nbytes;
253
254     byte *data_buf;
255     int data_buf_size;
256
257     gcry_mpi_t result;
258
259     rc = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
260     if (rc)
261     {
262       log_error( "ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (rc));
263       return rc;
264     }
265
266     rc = gcry_cipher_setkey (hd, secret_x, secret_x_size);
267     xfree( secret_x );
268     if (rc)
269     {
270       gcry_cipher_close (hd);
271       log_error("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (rc));
272       return rc;
273     }
274
275     data_buf_size = (gcry_mpi_get_nbits(data)+7)/8;
276     assert( (data_buf_size & 7) == (is_encrypt ? 0 : 1) );
277
278     data_buf = xmalloc_secure( 1 + 2*data_buf_size + 8 );
279     if( !data_buf )  {
280       gcry_cipher_close (hd);
281       return GPG_ERR_ENOMEM;
282     }
283
284     if( is_encrypt )  {
285       byte *in = data_buf+1+data_buf_size+8;
286
287       /* write data MPI into the end of data_buf. data_buf is  size aeswrap data */
288       rc = gcry_mpi_print (GCRYMPI_FMT_USG, in, data_buf_size, &nbytes, data/*in*/);
289       if( rc )   {
290         log_error("ecdh failed to export DEK: %s\n", gpg_strerror (rc));
291         gcry_cipher_close (hd);
292         xfree( data_buf );
293         return rc;
294       }
295
296       if( DBG_CIPHER )
297          log_printhex ("ecdh encrypting  :", in, data_buf_size );
298
299       rc = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8, in, data_buf_size);
300       memset( in, 0, data_buf_size);
301       gcry_cipher_close (hd);
302       if(rc)
303       {
304         log_error("ecdh failed in gcry_cipher_encrypt: %s\n", gpg_strerror (rc));
305         xfree( data_buf );
306         return rc;
307       }
308       data_buf[0] = data_buf_size+8;
309
310       if( DBG_CIPHER )
311          log_printhex ("ecdh encrypted to:", data_buf+1, data_buf[0] );
312
313       rc = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, data_buf, 1+data_buf[0], NULL); /* (byte)size + aeswrap of DEK */
314       xfree( data_buf );
315       if(rc)
316       {
317         log_error("ecdh failed to create an MPI: %s\n", gpg_strerror (rc));
318         return rc;
319       }
320
321       *out = result;
322     }
323     else  {
324       byte *in;
325
326       rc = gcry_mpi_print (GCRYMPI_FMT_USG, data_buf, data_buf_size, &nbytes, data/*in*/);
327       if( nbytes != data_buf_size || data_buf[0] != data_buf_size-1 )  {
328         log_error("ecdh inconsistent size\n");
329         xfree( data_buf );
330         return GPG_ERR_BAD_MPI;
331       }
332       in = data_buf+data_buf_size;
333       data_buf_size = data_buf[0];
334
335       if( DBG_CIPHER )
336          log_printhex ("ecdh decrypting :", data_buf+1, data_buf_size );
337       
338       rc = gcry_cipher_decrypt (hd, in, data_buf_size, data_buf+1, data_buf_size );
339       gcry_cipher_close (hd);
340       if(rc)
341       {
342         log_error("ecdh failed in gcry_cipher_decrypt: %s\n", gpg_strerror (rc));
343         xfree( data_buf );
344         return rc;
345       }
346
347       data_buf_size-=8;
348
349       if( DBG_CIPHER )
350          log_printhex ("ecdh decrypted to :", in, data_buf_size );
351
352       /* padding is removed later */
353       //if( in[data_buf_size-1] > 8 )  {
354       //  log_error("ecdh failed at decryption: invalid padding. %02x > 8\n", in[data_buf_size-1] );
355       //  return GPG_ERR_BAD_KEY;
356       //}
357  
358       rc = gcry_mpi_scan ( &result, GCRYMPI_FMT_USG, in, data_buf_size, NULL);
359       xfree( data_buf );
360       if(rc)
361       {
362         log_error("ecdh failed to create a plain text MPI: %s\n", gpg_strerror (rc));
363         return rc;
364       }
365
366       *out = result;
367     }
368   }
369
370   return rc;
371 }
372
373 /* Perform ECDH encryption, which involves ECDH key generation.
374  */
375 int
376 pk_ecdh_encrypt (gcry_mpi_t * resarr, const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t data, gcry_mpi_t * pkey)
377 {
378   gcry_sexp_t s_ciph, s_data, s_pkey;
379
380   PKT_public_key *pk_eph;
381   int nbits;
382   int rc;
383
384   nbits = pubkey_nbits( PUBKEY_ALGO_ECDH, pkey );
385  
386   /*** Generate an ephemeral key ***/
387
388   rc = pk_ecc_keypair_gen( &pk_eph, PUBKEY_ALGO_ECDH, KEYGEN_FLAG_TRANSIENT_KEY | KEYGEN_FLAG_NO_PROTECTION /*this is ephemeral*/, "", nbits );
389   if( rc )
390     return rc;
391   if( DBG_CIPHER )  {
392         unsigned char *buffer;
393         if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, pk_eph->pkey[1]))
394           BUG ();
395         log_debug("ephemeral key MPI #0: %s\n", buffer);
396         gcry_free( buffer );
397   }
398   free_public_key (pk_eph);
399
400   /*** Done with ephemeral key generation. 
401    * Now use ephemeral secret to get the shared secret. ***/
402
403   rc = gcry_sexp_build (&s_pkey, NULL,
404                     "(public-key(ecdh(c%m)(q%m)(p%m)))", pkey[0], pkey[1], pkey[2]);
405   if (rc)
406     BUG ();
407  
408   /* put the data into a simple list */
409   if (gcry_sexp_build (&s_data, NULL, "%m", pk_eph->pkey[3]))   /* ephemeral scalar goes as data */
410     BUG ();
411
412   /* pass it to libgcrypt */
413   rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
414   gcry_sexp_release (s_data);
415   gcry_sexp_release (s_pkey);
416   if (rc)
417     return rc;
418
419   /* finally, perform encryption */
420
421   {
422     gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a");            /* ... and get the shared point */
423     gcry_sexp_release (s_ciph);
424     resarr[0] = pk_eph->pkey[1];        /* ephemeral public key */
425
426     if( DBG_CIPHER )  {
427         unsigned char *buffer;
428         if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, resarr[0]))
429           BUG ();
430         log_debug("ephemeral key MPI: %s\n", buffer);
431         gcry_free( buffer );
432     }
433
434     rc = pk_ecdh_encrypt_with_shared_point ( 1 /*=encrypton*/, shared, pk_fp, data, pkey, resarr+1 );
435     mpi_release( shared );
436   }
437
438   return rc;
439 }
440
441 /* Perform ECDH decryption. 
442  */
443 int
444 pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t *data, gcry_mpi_t * skey)  {
445   gcry_sexp_t s_skey, s_data, s_ciph;
446   int rc;
447
448   if (!data[0] || !data[1])
449     return gpg_error (GPG_ERR_BAD_MPI);
450
451   rc = gcry_sexp_build (&s_skey, NULL,
452                             "(public-key(ecdh(c%m)(q%m)(p%m)))",
453                             skey[0]/*curve*/, data[0]/*ephemeral key*/, skey[2]/*KDF params*/);
454   if (rc)
455     BUG ();
456
457   /* put the data into a simple list */
458   if (gcry_sexp_build (&s_data, NULL, "%m", skey[3]))   /* static private key (scalar) goes as data */
459     BUG ();
460
461   rc = gcry_pk_encrypt (&s_ciph, s_data, s_skey);       /* encrypting ephemeral key with our private scalar yields the shared point */
462   gcry_sexp_release (s_skey);
463   gcry_sexp_release (s_data);
464   if (rc)
465     return rc;
466
467   {
468     gcry_mpi_t shared = mpi_from_sexp (s_ciph, "a");            /* get the shared point */
469     gcry_sexp_release (s_ciph);
470     rc = pk_ecdh_encrypt_with_shared_point ( 0 /*=decryption*/, shared, sk_fp, data[1]/*encr data as an MPI*/, skey, result );
471     mpi_release( shared );
472   }
473
474   return rc;
475 }
476
477