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