1
/* protect.c - Un/Protect a secret key
2
* Copyright (C) 1998, 1999, 2000, 2001, 2002,
3
* 2003 Free Software Foundation, Inc.
5
* This file is part of GnuPG.
7
* GnuPG is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* GnuPG is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
34
#include "sexp-parse.h"
36
#define PROT_CIPHER GCRY_CIPHER_AES
37
#define PROT_CIPHER_STRING "aes"
38
#define PROT_CIPHER_KEYLEN (128/8)
41
/* A table containing the information needed to create a protected
46
int prot_from, prot_to;
48
{ "rsa", "nedpqu", 2, 5 },
49
{ "dsa", "pqgyx", 4, 4 },
50
{ "elg", "pgyx", 3, 3 },
56
hash_passphrase (const char *passphrase, int hashalgo,
58
const unsigned char *s2ksalt, unsigned long s2kcount,
59
unsigned char *key, size_t keylen);
63
/* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to
64
a 20 byte buffer. This function is suitable for any algorithms. */
66
calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
68
const unsigned char *hash_begin, *hash_end;
69
const unsigned char *s;
74
return gpg_error (GPG_ERR_INV_SEXP);
78
return gpg_error (GPG_ERR_INV_SEXP);
79
if (!smatch (&s, n, "private-key"))
80
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
82
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
87
return gpg_error (GPG_ERR_INV_SEXP);
88
s += n; /* skip over the algorithm name */
95
return gpg_error (GPG_ERR_INV_SEXP);
99
return gpg_error (GPG_ERR_INV_SEXP);
102
return gpg_error (GPG_ERR_INV_SEXP);
106
return gpg_error (GPG_ERR_INV_SEXP);
110
gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash,
111
hash_begin, hash_end - hash_begin);
118
/* Encrypt the parameter block starting at PROTBEGIN with length
119
PROTLEN using the utf8 encoded key PASSPHRASE and return the entire
120
encrypted block in RESULT or ereturn with an error code. SHA1HASH
121
is the 20 byte SHA-1 hash required for the integrity code.
123
The parameter block is expected to be an incomplete S-Expression of
124
the form (example in advanced format):
126
(d #046129F..[some bytes not shown]..81#)
127
(p #00e861b..[some bytes not shown]..f1#)
128
(q #00f7a7c..[some bytes not shown]..61#)
129
(u #304559a..[some bytes not shown]..9b#)
131
the returned block is the S-Expression:
133
(protected mode (parms) encrypted_octet_string)
137
do_encryption (const char *protbegin, size_t protlen,
138
const char *passphrase, const unsigned char *sha1hash,
139
unsigned char **result, size_t *resultlen)
142
const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc";
143
int blklen, enclen, outlen;
148
int saltpos, ivpos, encpos;
150
rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
156
/* We need to work on a copy of the data because this makes it
157
easier to add the trailer and the padding and more important we
158
have to prefix the text with 2 parenthesis, so we have to
159
allocate enough space for:
161
((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
163
We always append a full block of random bytes as padding but
164
encrypt only what is needed for a full blocksize */
165
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
166
outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
167
enclen = outlen/blklen * blklen;
168
outbuf = gcry_malloc_secure (outlen);
173
/* Allocate random bytes to be used as IV, padding and s2k salt. */
174
iv = xtrymalloc (blklen*2+8);
176
rc = gpg_error (GPG_ERR_ENOMEM);
177
gcry_create_nonce (iv, blklen*2+8);
178
rc = gcry_cipher_setiv (hd, iv, blklen);
183
size_t keylen = PROT_CIPHER_KEYLEN;
185
key = gcry_malloc_secure (keylen);
190
rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
191
3, iv+2*blklen, 96, key, keylen);
193
rc = gcry_cipher_setkey (hd, key, keylen);
202
memcpy (p, protbegin, protlen);
204
memcpy (p, ")(4:hash4:sha120:", 17);
206
memcpy (p, sha1hash, 20);
210
memcpy (p, iv+blklen, blklen);
212
assert ( p - outbuf == outlen);
213
rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
215
gcry_cipher_close (hd);
223
/* Now allocate the buffer we want to return. This is
225
(protected openpgp-s2k3-sha1-aes-cbc
226
((sha1 salt no_of_iterations) 16byte_iv)
227
encrypted_octet_string)
229
in canoncical format of course. We use asprintf and %n modifier
230
and spaces as palceholders. */
232
"(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
233
(int)strlen (modestr), modestr,
235
blklen, &ivpos, blklen, "",
236
enclen, &encpos, enclen, "");
238
{ /* asprintf does not use our malloc system */
240
p = xtrymalloc (strlen (psave)+1);
247
gpg_error_t tmperr = out_of_core ();
252
*resultlen = strlen (p);
254
memcpy (p+saltpos, iv+2*blklen, 8);
255
memcpy (p+ivpos, iv, blklen);
256
memcpy (p+encpos, outbuf, enclen);
264
/* Protect the key encoded in canonical format in plainkey. We assume
265
a valid S-Exp here. */
267
agent_protect (const unsigned char *plainkey, const char *passphrase,
268
unsigned char **result, size_t *resultlen)
271
const unsigned char *s;
272
const unsigned char *hash_begin, *hash_end;
273
const unsigned char *prot_begin, *prot_end, *real_end;
276
unsigned char hashvalue[20];
277
unsigned char *protected;
284
return gpg_error (GPG_ERR_INV_SEXP);
289
return gpg_error (GPG_ERR_INV_SEXP);
290
if (!smatch (&s, n, "private-key"))
291
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
293
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
299
return gpg_error (GPG_ERR_INV_SEXP);
301
for (infidx=0; protect_info[infidx].algo
302
&& !smatch (&s, n, protect_info[infidx].algo); infidx++)
304
if (!protect_info[infidx].algo)
305
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
307
prot_begin = prot_end = NULL;
308
for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
310
if (i == protect_info[infidx].prot_from)
313
return gpg_error (GPG_ERR_INV_SEXP);
318
return gpg_error (GPG_ERR_INV_SEXP);
319
if (n != 1 || c != *s)
320
return gpg_error (GPG_ERR_INV_SEXP);
324
return gpg_error (GPG_ERR_INV_SEXP);
325
s +=n; /* skip value */
327
return gpg_error (GPG_ERR_INV_SEXP);
329
if (i == protect_info[infidx].prot_to)
333
if (*s != ')' || !prot_begin || !prot_end )
334
return gpg_error (GPG_ERR_INV_SEXP);
338
/* skip to the end of the S-exp */
340
rc = sskip (&s, &depth);
346
gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue,
347
hash_begin, hash_end - hash_begin + 1);
349
rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
350
passphrase, hashvalue,
351
&protected, &protectedlen);
355
/* Now create the protected version of the key. Note that the 10
356
extra bytes are for for the inserted "protected-" string (the
357
beginning of the plaintext reads: "((11:private-key(" ). */
359
+ (prot_begin-plainkey)
361
+ (real_end-prot_end));
362
*result = p = xtrymalloc (*resultlen);
365
gpg_error_t tmperr = out_of_core ();
369
memcpy (p, "(21:protected-", 14);
371
memcpy (p, plainkey+4, prot_begin - plainkey - 4);
372
p += prot_begin - plainkey - 4;
373
memcpy (p, protected, protectedlen);
375
memcpy (p, prot_end+1, real_end - prot_end);
376
p += real_end - prot_end;
377
assert ( p - *result == *resultlen);
383
/* Do the actual decryption and check the return list for consistency. */
385
do_decryption (const unsigned char *protected, size_t protectedlen,
386
const char *passphrase,
387
const unsigned char *s2ksalt, unsigned long s2kcount,
388
const unsigned char *iv, size_t ivlen,
389
unsigned char **result)
394
unsigned char *outbuf;
397
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
398
if (protectedlen < 4 || (protectedlen%blklen))
399
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
401
rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
406
outbuf = gcry_malloc_secure (protectedlen);
410
rc = gcry_cipher_setiv (hd, iv, ivlen);
414
size_t keylen = PROT_CIPHER_KEYLEN;
416
key = gcry_malloc_secure (keylen);
421
rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
422
3, s2ksalt, s2kcount, key, keylen);
424
rc = gcry_cipher_setkey (hd, key, keylen);
429
rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
430
protected, protectedlen);
431
gcry_cipher_close (hd);
437
/* Do a quick check first. */
438
if (*outbuf != '(' && outbuf[1] != '(')
441
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
443
/* Check that we have a consistent S-Exp. */
444
reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL);
445
if (!reallen || (reallen + blklen < protectedlen) )
448
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
455
/* Merge the parameter list contained in CLEARTEXT with the original
456
protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
457
Return the new list in RESULT and the MIC value in the 20 byte
460
merge_lists (const unsigned char *protectedkey,
462
const unsigned char *cleartext,
463
unsigned char *sha1hash,
464
unsigned char **result, size_t *resultlen)
466
size_t n, newlistlen;
467
unsigned char *newlist, *p;
468
const unsigned char *s;
469
const unsigned char *startpos, *endpos;
473
return gpg_error (GPG_ERR_BUG);
475
/* Estimate the required size of the resulting list. We have a large
476
safety margin of >20 bytes (MIC hash from CLEARTEXT and the
477
removed "protected-" */
478
newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL);
480
return gpg_error (GPG_ERR_BUG);
481
n = gcry_sexp_canon_len (cleartext, 0, NULL, NULL);
483
return gpg_error (GPG_ERR_BUG);
485
newlist = gcry_malloc_secure (newlistlen);
487
return out_of_core ();
489
/* Copy the initial segment */
490
strcpy (newlist, "(11:private-key");
492
memcpy (p, protectedkey+15+10, replacepos-15-10);
493
p += replacepos-15-10;
495
/* copy the cleartext */
497
if (*s != '(' && s[1] != '(')
498
return gpg_error (GPG_ERR_BUG); /*we already checked this */
520
/* short intermezzo: Get the MIC */
525
if (!smatch (&s, n, "hash"))
528
if (!smatch (&s, n, "sha1"))
533
memcpy (sha1hash, s, 20);
539
/* append the parameter list */
540
memcpy (p, startpos, endpos - startpos);
541
p += endpos - startpos;
543
/* skip overt the protected list element in the original list */
544
s = protectedkey + replacepos;
552
i = 2; /* we are inside this level */
556
assert (s[-1] == ')');
557
endpos = s; /* one behind the end of the list */
559
/* append the rest */
560
memcpy (p, startpos, endpos - startpos);
561
p += endpos - startpos;
565
*resultlen = newlistlen;
569
wipememory (newlist, newlistlen);
574
wipememory (newlist, newlistlen);
576
return gpg_error (GPG_ERR_INV_SEXP);
581
/* Unprotect the key encoded in canonical format. We assume a valid
584
agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
585
unsigned char **result, size_t *resultlen)
588
const unsigned char *s;
591
unsigned char sha1hash[20], sha1hash2[20];
592
const unsigned char *s2ksalt;
593
unsigned long s2kcount;
594
const unsigned char *iv;
595
const unsigned char *prot_begin;
596
unsigned char *cleartext;
597
unsigned char *final;
602
return gpg_error (GPG_ERR_INV_SEXP);
606
return gpg_error (GPG_ERR_INV_SEXP);
607
if (!smatch (&s, n, "protected-private-key"))
608
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
610
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
614
return gpg_error (GPG_ERR_INV_SEXP);
616
for (infidx=0; protect_info[infidx].algo
617
&& !smatch (&s, n, protect_info[infidx].algo); infidx++)
619
if (!protect_info[infidx].algo)
620
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
622
/* Now find the list with the protected information. Here is an
623
example for such a list:
624
(protected openpgp-s2k3-sha1-aes-cbc
625
((sha1 <salt> <count>) <Initialization_Vector>)
631
return gpg_error (GPG_ERR_INV_SEXP);
636
return gpg_error (GPG_ERR_INV_SEXP);
637
if (smatch (&s, n, "protected"))
648
return gpg_error (GPG_ERR_INV_SEXP);
649
if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"))
650
return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
651
if (*s != '(' || s[1] != '(')
652
return gpg_error (GPG_ERR_INV_SEXP);
656
return gpg_error (GPG_ERR_INV_SEXP);
657
if (!smatch (&s, n, "sha1"))
658
return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
661
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
666
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
667
/* We expect a list close as next, so we can simply use strtoul()
668
here. We might want to check that we only have digits - but this
669
is nothing we should worry about */
671
return gpg_error (GPG_ERR_INV_SEXP);
672
s2kcount = strtoul (s, NULL, 10);
674
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
676
s++; /* skip list end */
679
if (n != 16) /* Wrong blocksize for IV (we support only aes-128). */
680
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
684
return gpg_error (GPG_ERR_INV_SEXP);
688
return gpg_error (GPG_ERR_INV_SEXP);
690
rc = do_decryption (s, n,
691
passphrase, s2ksalt, s2kcount,
697
rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
698
sha1hash, &final, &finallen);
699
/* Albeit cleartext has been allocated in secure memory and thus
700
xfree will wipe it out, we do an extra wipe just in case
701
somethings goes badly wrong. */
702
wipememory (cleartext, n);
707
rc = calculate_mic (final, sha1hash2);
708
if (!rc && memcmp (sha1hash, sha1hash2, 20))
709
rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
712
wipememory (final, finallen);
718
*resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
722
/* Check the type of the private key, this is one of the constants:
723
PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the
724
value 0), PRIVATE_KEY_CLEAR for an unprotected private key.
725
PRIVATE_KEY_PROTECTED for an protected private key or
726
PRIVATE_KEY_SHADOWED for a sub key where the secret parts are stored
729
agent_private_key_type (const unsigned char *privatekey)
731
const unsigned char *s;
736
return PRIVATE_KEY_UNKNOWN;
740
return PRIVATE_KEY_UNKNOWN;
741
if (smatch (&s, n, "protected-private-key"))
742
return PRIVATE_KEY_PROTECTED;
743
if (smatch (&s, n, "shadowed-private-key"))
744
return PRIVATE_KEY_SHADOWED;
745
if (smatch (&s, n, "private-key"))
746
return PRIVATE_KEY_CLEAR;
747
return PRIVATE_KEY_UNKNOWN;
752
/* Transform a passphrase into a suitable key of length KEYLEN and
753
store this key in the caller provided buffer KEY. The caller must
754
provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on
755
that mode an S2KSALT of 8 random bytes and an S2KCOUNT (a suitable
758
Returns an error code on failure. */
760
hash_passphrase (const char *passphrase, int hashalgo,
762
const unsigned char *s2ksalt,
763
unsigned long s2kcount,
764
unsigned char *key, size_t keylen)
770
int pwlen = strlen (passphrase);
772
if ( (s2kmode != 0 && s2kmode != 1 && s2kmode != 3)
773
|| !hashalgo || !keylen || !key || !passphrase)
774
return gpg_error (GPG_ERR_INV_VALUE);
775
if ((s2kmode == 1 ||s2kmode == 3) && !s2ksalt)
776
return gpg_error (GPG_ERR_INV_VALUE);
778
rc = gcry_md_open (&md, hashalgo, GCRY_MD_FLAG_SECURE);
782
for (pass=0; used < keylen; pass++)
787
for (i=0; i < pass; i++) /* preset the hash context */
788
gcry_md_putc (md, 0);
791
if (s2kmode == 1 || s2kmode == 3)
793
int len2 = pwlen + 8;
794
unsigned long count = len2;
798
count = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6);
805
gcry_md_write (md, s2ksalt, 8);
806
gcry_md_write (md, passphrase, pwlen);
810
gcry_md_write (md, s2ksalt, count);
813
gcry_md_write (md, s2ksalt, 8);
815
gcry_md_write (md, passphrase, count);
819
gcry_md_write (md, passphrase, pwlen);
822
i = gcry_md_get_algo_dlen (hashalgo);
823
if (i > keylen - used)
825
memcpy (key+used, gcry_md_read (md, hashalgo), i);
834
/* Create a shadow key from a public key. We use the shadow protocol
835
"ti-v1" and insert the S-expressionn SHADOW_INFO. The resulting
836
S-expression is returned in an allocated buffer RESULT will point
837
to. The input parameters are expected to be valid canonilized
840
agent_shadow_key (const unsigned char *pubkey,
841
const unsigned char *shadow_info,
842
unsigned char **result)
844
const unsigned char *s;
845
const unsigned char *point;
849
size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL);
850
size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL);
852
if (!pubkey_len || !shadow_info_len)
853
return gpg_error (GPG_ERR_INV_VALUE);
856
return gpg_error (GPG_ERR_INV_SEXP);
861
return gpg_error (GPG_ERR_INV_SEXP);
862
if (!smatch (&s, n, "public-key"))
863
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
865
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
870
return gpg_error (GPG_ERR_INV_SEXP);
871
s += n; /* skip over the algorithm name */
876
return gpg_error (GPG_ERR_INV_SEXP);
881
return gpg_error (GPG_ERR_INV_SEXP);
885
return gpg_error (GPG_ERR_INV_SEXP);
886
s +=n; /* skip value */
888
return gpg_error (GPG_ERR_INV_SEXP);
892
point = s; /* insert right before the point */
897
/* calculate required length by taking in account: the "shadowed-"
898
prefix, the "shadowed", "t1-v1" as well as some parenthesis */
899
n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1;
900
*result = p = xtrymalloc (n);
902
return out_of_core ();
903
p = stpcpy (p, "(20:shadowed-private-key");
904
/* (10:public-key ...)*/
905
memcpy (p, pubkey+14, point - (pubkey+14));
906
p += point - (pubkey+14);
907
p = stpcpy (p, "(8:shadowed5:t1-v1");
908
memcpy (p, shadow_info, shadow_info_len);
909
p += shadow_info_len;
911
memcpy (p, point, pubkey_len - (point - pubkey));
912
p += pubkey_len - (point - pubkey);
917
/* Parse a canonical encoded shadowed key and return a pointer to the
918
inner list with the shadow_info */
920
agent_get_shadow_info (const unsigned char *shadowkey,
921
unsigned char const **shadow_info)
923
const unsigned char *s;
929
return gpg_error (GPG_ERR_INV_SEXP);
934
return gpg_error (GPG_ERR_INV_SEXP);
935
if (!smatch (&s, n, "shadowed-private-key"))
936
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
938
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
943
return gpg_error (GPG_ERR_INV_SEXP);
944
s += n; /* skip over the algorithm name */
949
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
951
return gpg_error (GPG_ERR_INV_SEXP);
956
return gpg_error (GPG_ERR_INV_SEXP);
957
if (smatch (&s, n, "shadowed"))
962
return gpg_error (GPG_ERR_INV_SEXP);
963
s +=n; /* skip value */
965
return gpg_error (GPG_ERR_INV_SEXP);
969
/* Found the shadowed list, S points to the protocol */
972
return gpg_error (GPG_ERR_INV_SEXP);
973
if (smatch (&s, n, "t1-v1"))
976
return gpg_error (GPG_ERR_INV_SEXP);
980
return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);