1 /* sexp-secret.c - SEXP handling of the secret key
2  * Copyright (C) 2020 g10 Code GmbH.
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 <https://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 #include "agent.h"
22 #include "../common/sexp-parse.h"
23 
24 /*
25  * When it's for ECC, fixup private key part in the cannonical SEXP
26  * representation in BUF.  If not ECC, do nothing.
27  */
28 gpg_error_t
fixup_when_ecc_private_key(unsigned char * buf,size_t * buflen_p)29 fixup_when_ecc_private_key (unsigned char *buf, size_t *buflen_p)
30 {
31   const unsigned char *s;
32   char curve_name[256];
33   size_t n;
34   size_t buflen = *buflen_p;
35 
36   s = buf;
37   if (*s != '(')
38     return gpg_error (GPG_ERR_INV_SEXP);
39   s++;
40   n = snext (&s);
41   if (!n)
42     return gpg_error (GPG_ERR_INV_SEXP);
43   if (smatch (&s, n, "shadowed-private-key"))
44     return 0;  /* Nothing to do.  */
45   if (!smatch (&s, n, "private-key"))
46     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
47   if (*s != '(')
48     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
49   s++;
50   n = snext (&s);
51   if (!smatch (&s, n, "ecc"))
52     return 0;
53 
54   /* It's ECC */
55   while (*s == '(')
56     {
57       s++;
58       n = snext (&s);
59       if (!n)
60         return gpg_error (GPG_ERR_INV_SEXP);
61       if (n == 5 && !memcmp (s, "curve", 5))
62         {
63           s += n;
64           n = snext (&s);
65           if (!n || n >= sizeof curve_name)
66             return gpg_error (GPG_ERR_INV_SEXP);
67 
68           memcpy (curve_name, s, n);
69           curve_name[n] = 0;
70           s += n;
71         }
72       else if (n == 1 && *s == 'd')
73         {
74           unsigned char *s0;
75           size_t n0;
76 
77           s += n;
78           s0 = (unsigned char *)s;
79           n = snext (&s);
80           n0 = s - s0;
81 
82           if (!n)
83             return gpg_error (GPG_ERR_INV_SEXP);
84           else if (!*s /* Leading 0x00 added at the front for classic curve */
85                    && strcmp (curve_name, "Ed25519")
86                    && strcmp (curve_name, "Ed448")
87                    && strcmp (curve_name, "X448"))
88             {
89               size_t numsize;
90 
91               n--;
92               buflen--;
93               numsize = snprintf (s0, s-s0+1, "%u:", (unsigned int)n);
94               memmove (s0+numsize, s+1, buflen - (s - buf));
95               memset (s0+numsize+buflen - (s - buf), 0, (n0 - numsize) + 1);
96               buflen -= (n0 - numsize);
97               s = s0+numsize+n;
98               *buflen_p = buflen;
99             }
100           else
101             s += n;
102         }
103       else
104         {
105           s += n;
106           n = snext (&s);
107           if (!n)
108             return gpg_error (GPG_ERR_INV_SEXP);
109           s += n;
110         }
111       if ( *s != ')' )
112         return gpg_error (GPG_ERR_INV_SEXP);
113       s++;
114     }
115   if (*s != ')')
116     return gpg_error (GPG_ERR_INV_SEXP);
117   s++;
118 
119   return 0;
120 }
121 
122 /*
123  * Scan BUF to get SEXP, put into RESULT.  Error offset will be in the
124  * pointer at R_ERROFF.  For ECC, the private part 'd' will be fixed
125  * up; That part may have 0x00 prefix of signed MPI encoding, which is
126  * incompatible to opaque MPI handling.
127  */
128 gpg_error_t
sexp_sscan_private_key(gcry_sexp_t * result,size_t * r_erroff,unsigned char * buf)129 sexp_sscan_private_key (gcry_sexp_t *result, size_t *r_erroff,
130                         unsigned char *buf)
131 {
132   gpg_error_t err;
133   size_t buflen, buflen0;
134 
135   buflen = buflen0 = gcry_sexp_canon_len (buf, 0, NULL, NULL);
136   err = fixup_when_ecc_private_key (buf, &buflen);
137   if (!err)
138     err = gcry_sexp_sscan (result, r_erroff, (char*)buf, buflen0);
139   wipememory (buf, buflen0);
140 
141   return err;
142 }
143