Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F22948338
keygen.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
172 KB
Subscribers
None
keygen.c
View Options
/* keygen.c - Generate a key pair
* Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc.
* Copyright (C) 2014, 2015, 2016, 2017, 2018 Werner Koch
* Copyright (C) 2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<ctype.h>
#include
<errno.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<unistd.h>
#include
"gpg.h"
#include
"../common/util.h"
#include
"main.h"
#include
"packet.h"
#include
"../common/ttyio.h"
#include
"options.h"
#include
"keydb.h"
#include
"trustdb.h"
#include
"../common/status.h"
#include
"../common/i18n.h"
#include
"keyserver-internal.h"
#include
"call-agent.h"
#include
"pkglue.h"
#include
"../common/shareddefs.h"
#include
"../common/host2net.h"
#include
"../common/mbox-util.h"
/* The default algorithms. If you change them, you should ensure the
value is inside the bounds enforced by ask_keysize and gen_xxx.
See also get_keysize_range which encodes the allowed ranges. The
default answer in ask_algo also needs to be adjusted. */
#define DEFAULT_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr"
#define FUTURE_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr"
/* When generating keys using the streamlined key generation dialog,
use this as a default expiration interval. */
const
char
*
default_expiration_interval
=
"2y"
;
/* Flag bits used during key generation. */
#define KEYGEN_FLAG_NO_PROTECTION 1
#define KEYGEN_FLAG_TRANSIENT_KEY 2
#define KEYGEN_FLAG_CREATE_V5_KEY 4
/* Maximum number of supported algorithm preferences. */
#define MAX_PREFS 30
enum
para_name
{
pKEYTYPE
,
pKEYLENGTH
,
pKEYCURVE
,
pKEYUSAGE
,
pSUBKEYTYPE
,
pSUBKEYLENGTH
,
pSUBKEYCURVE
,
pSUBKEYUSAGE
,
pAUTHKEYTYPE
,
pNAMEREAL
,
pNAMEEMAIL
,
pNAMECOMMENT
,
pPREFERENCES
,
pREVOKER
,
pUSERID
,
pCREATIONDATE
,
pKEYCREATIONDATE
,
/* Same in seconds since epoch. */
pEXPIREDATE
,
pKEYEXPIRE
,
/* in n seconds */
pSUBKEYCREATIONDATE
,
pSUBKEYEXPIRE
,
/* in n seconds */
pAUTHKEYCREATIONDATE
,
/* Not yet used. */
pPASSPHRASE
,
pSERIALNO
,
pCARDBACKUPKEY
,
pHANDLE
,
pKEYSERVER
,
pKEYGRIP
,
pSUBKEYGRIP
,
pVERSION
,
/* Desired version of the key packet. */
pSUBVERSION
,
/* Ditto for the subpacket. */
pCARDKEY
/* The keygrips have been taken from active card (bool). */
};
struct
para_data_s
{
struct
para_data_s
*
next
;
int
lnr
;
enum
para_name
key
;
union
{
u32
expire
;
u32
creation
;
int
abool
;
unsigned
int
usage
;
struct
revocation_key
revkey
;
char
value
[
1
];
}
u
;
};
struct
output_control_s
{
int
lnr
;
int
dryrun
;
unsigned
int
keygen_flags
;
int
use_files
;
struct
{
char
*
fname
;
char
*
newfname
;
IOBUF
stream
;
armor_filter_context_t
*
afx
;
}
pub
;
};
struct
opaque_data_usage_and_pk
{
unsigned
int
usage
;
PKT_public_key
*
pk
;
};
/* FIXME: These globals vars are ugly. And using MAX_PREFS even for
* aeads is useless, given that we don't expects more than a very few
* algorithms. */
static
int
prefs_initialized
=
0
;
static
byte
sym_prefs
[
MAX_PREFS
];
static
int
nsym_prefs
;
static
byte
hash_prefs
[
MAX_PREFS
];
static
int
nhash_prefs
;
static
byte
zip_prefs
[
MAX_PREFS
];
static
int
nzip_prefs
;
static
byte
aead_prefs
[
MAX_PREFS
];
static
int
naead_prefs
;
static
int
mdc_available
;
static
int
ks_modify
;
static
int
aead_available
;
static
gpg_error_t
parse_algo_usage_expire
(
ctrl_t
ctrl
,
int
for_subkey
,
const
char
*
algostr
,
const
char
*
usagestr
,
const
char
*
expirestr
,
int
*
r_algo
,
unsigned
int
*
r_usage
,
u32
*
r_expire
,
unsigned
int
*
r_nbits
,
const
char
**
r_curve
,
int
*
r_version
,
char
**
r_keygrip
,
u32
*
r_keytime
);
static
void
do_generate_keypair
(
ctrl_t
ctrl
,
struct
para_data_s
*
para
,
struct
output_control_s
*
outctrl
,
int
card
);
static
int
write_keyblock
(
iobuf_t
out
,
kbnode_t
node
);
static
gpg_error_t
gen_card_key
(
int
keyno
,
int
algo
,
int
is_primary
,
kbnode_t
pub_root
,
u32
*
timestamp
,
u32
expireval
,
int
keygen_flags
);
static
unsigned
int
get_keysize_range
(
int
algo
,
unsigned
int
*
min
,
unsigned
int
*
max
);
/* Return the algo string for a default new key. */
const
char
*
get_default_pubkey_algo
(
void
)
{
if
(
opt
.
def_new_key_algo
)
{
if
(
*
opt
.
def_new_key_algo
&&
!
strchr
(
opt
.
def_new_key_algo
,
':'
))
return
opt
.
def_new_key_algo
;
/* To avoid checking that option every time we delay that until
* here. The only thing we really need to make sure is that
* there is no colon in the string so that the --gpgconf-list
* command won't mess up its output. */
log_info
(
_
(
"invalid value for option '%s'
\n
"
),
"--default-new-key-algo"
);
}
return
DEFAULT_STD_KEY_PARAM
;
}
static
void
print_status_key_created
(
int
letter
,
PKT_public_key
*
pk
,
const
char
*
handle
)
{
byte
array
[
MAX_FINGERPRINT_LEN
],
*
s
;
char
*
buf
,
*
p
;
size_t
i
,
n
;
if
(
!
handle
)
handle
=
""
;
buf
=
xmalloc
(
MAX_FINGERPRINT_LEN
*
2
+
31
+
strlen
(
handle
)
+
1
);
p
=
buf
;
if
(
letter
||
pk
)
{
*
p
++
=
letter
;
if
(
pk
)
{
*
p
++
=
' '
;
fingerprint_from_pk
(
pk
,
array
,
&
n
);
s
=
array
;
/* Fixme: Use bin2hex */
for
(
i
=
0
;
i
<
n
;
i
++
,
s
++
,
p
+=
2
)
snprintf
(
p
,
3
,
"%02X"
,
*
s
);
}
}
if
(
*
handle
)
{
*
p
++
=
' '
;
for
(
i
=
0
;
handle
[
i
]
&&
i
<
100
;
i
++
)
*
p
++
=
isspace
((
unsigned
int
)
handle
[
i
])
?
'_'
:
handle
[
i
];
}
*
p
=
0
;
write_status_text
((
letter
||
pk
)
?
STATUS_KEY_CREATED
:
STATUS_KEY_NOT_CREATED
,
buf
);
xfree
(
buf
);
}
static
void
print_status_key_not_created
(
const
char
*
handle
)
{
print_status_key_created
(
0
,
NULL
,
handle
);
}
static
gpg_error_t
write_uid
(
kbnode_t
root
,
const
char
*
s
)
{
PACKET
*
pkt
=
xmalloc_clear
(
sizeof
*
pkt
);
size_t
n
=
strlen
(
s
);
if
(
n
>
MAX_UID_PACKET_LENGTH
-
10
)
return
gpg_error
(
GPG_ERR_INV_USER_ID
);
pkt
->
pkttype
=
PKT_USER_ID
;
pkt
->
pkt
.
user_id
=
xmalloc_clear
(
sizeof
*
pkt
->
pkt
.
user_id
+
n
);
pkt
->
pkt
.
user_id
->
len
=
n
;
pkt
->
pkt
.
user_id
->
ref
=
1
;
strcpy
(
pkt
->
pkt
.
user_id
->
name
,
s
);
add_kbnode
(
root
,
new_kbnode
(
pkt
));
return
0
;
}
static
void
do_add_key_flags
(
PKT_signature
*
sig
,
unsigned
int
use
)
{
byte
buf
[
1
];
buf
[
0
]
=
0
;
/* The spec says that all primary keys MUST be able to certify. */
if
(
sig
->
sig_class
!=
0x18
)
buf
[
0
]
|=
0x01
;
if
(
use
&
PUBKEY_USAGE_SIG
)
buf
[
0
]
|=
0x02
;
if
(
use
&
PUBKEY_USAGE_ENC
)
buf
[
0
]
|=
0x04
|
0x08
;
if
(
use
&
PUBKEY_USAGE_AUTH
)
buf
[
0
]
|=
0x20
;
build_sig_subpkt
(
sig
,
SIGSUBPKT_KEY_FLAGS
,
buf
,
1
);
}
int
keygen_add_key_expire
(
PKT_signature
*
sig
,
void
*
opaque
)
{
PKT_public_key
*
pk
=
opaque
;
byte
buf
[
8
];
u32
u
;
if
(
pk
->
expiredate
)
{
if
(
pk
->
expiredate
>
pk
->
timestamp
)
u
=
pk
->
expiredate
-
pk
->
timestamp
;
else
u
=
1
;
buf
[
0
]
=
(
u
>>
24
)
&
0xff
;
buf
[
1
]
=
(
u
>>
16
)
&
0xff
;
buf
[
2
]
=
(
u
>>
8
)
&
0xff
;
buf
[
3
]
=
u
&
0xff
;
build_sig_subpkt
(
sig
,
SIGSUBPKT_KEY_EXPIRE
,
buf
,
4
);
}
else
{
/* Make sure we don't leave a key expiration subpacket lying
around */
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_KEY_EXPIRE
);
}
return
0
;
}
/* Add the key usage (i.e. key flags) in SIG from the public keys
* pubkey_usage field. OPAQUE has the public key. */
int
keygen_add_key_flags
(
PKT_signature
*
sig
,
void
*
opaque
)
{
PKT_public_key
*
pk
=
opaque
;
do_add_key_flags
(
sig
,
pk
->
pubkey_usage
);
return
0
;
}
static
int
keygen_add_key_flags_and_expire
(
PKT_signature
*
sig
,
void
*
opaque
)
{
struct
opaque_data_usage_and_pk
*
oduap
=
opaque
;
do_add_key_flags
(
sig
,
oduap
->
usage
);
return
keygen_add_key_expire
(
sig
,
oduap
->
pk
);
}
static
int
set_one_pref
(
int
val
,
int
type
,
const
char
*
item
,
byte
*
buf
,
int
*
nbuf
)
{
int
i
;
for
(
i
=
0
;
i
<
*
nbuf
;
i
++
)
if
(
buf
[
i
]
==
val
)
{
log_info
(
_
(
"preference '%s' duplicated
\n
"
),
item
);
return
-1
;
}
if
(
*
nbuf
>=
MAX_PREFS
)
{
if
(
type
==
1
)
log_info
(
_
(
"too many cipher preferences
\n
"
));
else
if
(
type
==
2
)
log_info
(
_
(
"too many digest preferences
\n
"
));
else
if
(
type
==
3
)
log_info
(
_
(
"too many compression preferences
\n
"
));
else
if
(
type
==
4
)
log_info
(
_
(
"too many AEAD preferences
\n
"
));
else
BUG
();
return
-1
;
}
buf
[(
*
nbuf
)
++
]
=
val
;
return
0
;
}
/*
* Parse the supplied string and use it to set the standard
* preferences. The string may be in a form like the one printed by
* "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual
* cipher/hash/compress names. Use NULL to set the default
* preferences. Returns: 0 = okay
*/
int
keygen_set_std_prefs
(
const
char
*
string
,
int
personal
)
{
byte
sym
[
MAX_PREFS
],
hash
[
MAX_PREFS
],
zip
[
MAX_PREFS
],
aead
[
MAX_PREFS
];
int
nsym
=
0
,
nhash
=
0
,
nzip
=
0
,
naead
=
0
,
val
,
rc
=
0
;
int
mdc
=
1
,
modify
=
0
;
/* mdc defaults on, modify defaults off. */
char
dummy_string
[
25
*
4
+
1
];
/* Enough for 25 items. */
if
(
!
string
||
!
ascii_strcasecmp
(
string
,
"default"
))
{
if
(
opt
.
def_preference_list
)
string
=
opt
.
def_preference_list
;
else
{
int
any_compress
=
0
;
dummy_string
[
0
]
=
'\0'
;
/* The rationale why we use the order AES256,192,128 is
for compatibility reasons with PGP. If gpg would
define AES128 first, we would get the somewhat
confusing situation:
gpg -r pgpkey -r gpgkey ---gives--> AES256
gpg -r gpgkey -r pgpkey ---gives--> AES
Note that by using --personal-cipher-preferences it is
possible to prefer AES128.
*/
/* Make sure we do not add more than 15 items here, as we
could overflow the size of dummy_string. We currently
have at most 12. */
if
(
!
openpgp_cipher_test_algo
(
CIPHER_ALGO_AES256
)
)
strcat
(
dummy_string
,
"S9 "
);
if
(
!
openpgp_cipher_test_algo
(
CIPHER_ALGO_AES192
)
)
strcat
(
dummy_string
,
"S8 "
);
if
(
!
openpgp_cipher_test_algo
(
CIPHER_ALGO_AES
)
)
strcat
(
dummy_string
,
"S7 "
);
strcat
(
dummy_string
,
"S2 "
);
/* 3DES */
if
(
opt
.
flags
.
rfc4880bis
&&
!
openpgp_aead_test_algo
(
AEAD_ALGO_OCB
))
strcat
(
dummy_string
,
"A2 "
);
if
(
opt
.
flags
.
rfc4880bis
&&
!
openpgp_aead_test_algo
(
AEAD_ALGO_EAX
))
strcat
(
dummy_string
,
"A1 "
);
if
(
personal
)
{
/* The default internal hash algo order is:
* SHA-256, SHA-384, SHA-512, SHA-224, SHA-1.
*/
if
(
!
openpgp_md_test_algo
(
DIGEST_ALGO_SHA256
))
strcat
(
dummy_string
,
"H8 "
);
if
(
!
openpgp_md_test_algo
(
DIGEST_ALGO_SHA384
))
strcat
(
dummy_string
,
"H9 "
);
if
(
!
openpgp_md_test_algo
(
DIGEST_ALGO_SHA512
))
strcat
(
dummy_string
,
"H10 "
);
}
else
{
/* The default advertised hash algo order is:
* SHA-512, SHA-384, SHA-256, SHA-224, SHA-1.
*/
if
(
!
openpgp_md_test_algo
(
DIGEST_ALGO_SHA512
))
strcat
(
dummy_string
,
"H10 "
);
if
(
!
openpgp_md_test_algo
(
DIGEST_ALGO_SHA384
))
strcat
(
dummy_string
,
"H9 "
);
if
(
!
openpgp_md_test_algo
(
DIGEST_ALGO_SHA256
))
strcat
(
dummy_string
,
"H8 "
);
}
if
(
!
openpgp_md_test_algo
(
DIGEST_ALGO_SHA224
))
strcat
(
dummy_string
,
"H11 "
);
strcat
(
dummy_string
,
"H2 "
);
/* SHA-1 */
if
(
!
check_compress_algo
(
COMPRESS_ALGO_ZLIB
))
{
strcat
(
dummy_string
,
"Z2 "
);
any_compress
=
1
;
}
if
(
!
check_compress_algo
(
COMPRESS_ALGO_BZIP2
))
{
strcat
(
dummy_string
,
"Z3 "
);
any_compress
=
1
;
}
if
(
!
check_compress_algo
(
COMPRESS_ALGO_ZIP
))
{
strcat
(
dummy_string
,
"Z1 "
);
any_compress
=
1
;
}
/* In case we have no compress algo at all, declare that
we prefer no compression. */
if
(
!
any_compress
)
strcat
(
dummy_string
,
"Z0 "
);
/* Remove the trailing space. */
if
(
*
dummy_string
&&
dummy_string
[
strlen
(
dummy_string
)
-1
]
==
' '
)
dummy_string
[
strlen
(
dummy_string
)
-1
]
=
0
;
string
=
dummy_string
;
}
}
else
if
(
!
ascii_strcasecmp
(
string
,
"none"
))
string
=
""
;
if
(
strlen
(
string
))
{
char
*
prefstringbuf
;
char
*
tok
,
*
prefstring
;
/* We need a writable string. */
prefstring
=
prefstringbuf
=
xstrdup
(
string
);
while
((
tok
=
strsep
(
&
prefstring
,
" ,"
)))
{
if
((
val
=
string_to_cipher_algo
(
tok
)))
{
if
(
set_one_pref
(
val
,
1
,
tok
,
sym
,
&
nsym
))
rc
=
-1
;
}
else
if
((
val
=
string_to_digest_algo
(
tok
)))
{
if
(
set_one_pref
(
val
,
2
,
tok
,
hash
,
&
nhash
))
rc
=
-1
;
}
else
if
((
val
=
string_to_compress_algo
(
tok
))
>
-1
)
{
if
(
set_one_pref
(
val
,
3
,
tok
,
zip
,
&
nzip
))
rc
=
-1
;
}
else
if
((
val
=
string_to_aead_algo
(
tok
)))
{
if
(
set_one_pref
(
val
,
4
,
tok
,
aead
,
&
naead
))
rc
=
-1
;
}
else
if
(
ascii_strcasecmp
(
tok
,
"mdc"
)
==
0
)
mdc
=
1
;
else
if
(
ascii_strcasecmp
(
tok
,
"no-mdc"
)
==
0
)
mdc
=
0
;
else
if
(
ascii_strcasecmp
(
tok
,
"ks-modify"
)
==
0
)
modify
=
1
;
else
if
(
ascii_strcasecmp
(
tok
,
"no-ks-modify"
)
==
0
)
modify
=
0
;
else
{
log_info
(
_
(
"invalid item '%s' in preference string
\n
"
),
tok
);
rc
=
-1
;
}
}
xfree
(
prefstringbuf
);
}
if
(
!
rc
)
{
if
(
personal
)
{
if
(
personal
==
PREFTYPE_SYM
)
{
xfree
(
opt
.
personal_cipher_prefs
);
if
(
nsym
==
0
)
opt
.
personal_cipher_prefs
=
NULL
;
else
{
int
i
;
opt
.
personal_cipher_prefs
=
xmalloc
(
sizeof
(
prefitem_t
*
)
*
(
nsym
+
1
));
for
(
i
=
0
;
i
<
nsym
;
i
++
)
{
opt
.
personal_cipher_prefs
[
i
].
type
=
PREFTYPE_SYM
;
opt
.
personal_cipher_prefs
[
i
].
value
=
sym
[
i
];
}
opt
.
personal_cipher_prefs
[
i
].
type
=
PREFTYPE_NONE
;
opt
.
personal_cipher_prefs
[
i
].
value
=
0
;
}
}
else
if
(
personal
==
PREFTYPE_AEAD
)
{
xfree
(
opt
.
personal_aead_prefs
);
if
(
!
naead
)
opt
.
personal_aead_prefs
=
NULL
;
else
{
int
i
;
opt
.
personal_aead_prefs
=
xmalloc
(
sizeof
(
prefitem_t
*
)
*
(
naead
+
1
));
for
(
i
=
0
;
i
<
naead
;
i
++
)
{
opt
.
personal_aead_prefs
[
i
].
type
=
PREFTYPE_AEAD
;
opt
.
personal_aead_prefs
[
i
].
value
=
sym
[
i
];
}
opt
.
personal_aead_prefs
[
i
].
type
=
PREFTYPE_NONE
;
opt
.
personal_aead_prefs
[
i
].
value
=
0
;
}
}
else
if
(
personal
==
PREFTYPE_HASH
)
{
xfree
(
opt
.
personal_digest_prefs
);
if
(
nhash
==
0
)
opt
.
personal_digest_prefs
=
NULL
;
else
{
int
i
;
opt
.
personal_digest_prefs
=
xmalloc
(
sizeof
(
prefitem_t
*
)
*
(
nhash
+
1
));
for
(
i
=
0
;
i
<
nhash
;
i
++
)
{
opt
.
personal_digest_prefs
[
i
].
type
=
PREFTYPE_HASH
;
opt
.
personal_digest_prefs
[
i
].
value
=
hash
[
i
];
}
opt
.
personal_digest_prefs
[
i
].
type
=
PREFTYPE_NONE
;
opt
.
personal_digest_prefs
[
i
].
value
=
0
;
}
}
else
if
(
personal
==
PREFTYPE_ZIP
)
{
xfree
(
opt
.
personal_compress_prefs
);
if
(
nzip
==
0
)
opt
.
personal_compress_prefs
=
NULL
;
else
{
int
i
;
opt
.
personal_compress_prefs
=
xmalloc
(
sizeof
(
prefitem_t
*
)
*
(
nzip
+
1
));
for
(
i
=
0
;
i
<
nzip
;
i
++
)
{
opt
.
personal_compress_prefs
[
i
].
type
=
PREFTYPE_ZIP
;
opt
.
personal_compress_prefs
[
i
].
value
=
zip
[
i
];
}
opt
.
personal_compress_prefs
[
i
].
type
=
PREFTYPE_NONE
;
opt
.
personal_compress_prefs
[
i
].
value
=
0
;
}
}
}
else
{
memcpy
(
sym_prefs
,
sym
,
(
nsym_prefs
=
nsym
));
memcpy
(
hash_prefs
,
hash
,
(
nhash_prefs
=
nhash
));
memcpy
(
zip_prefs
,
zip
,
(
nzip_prefs
=
nzip
));
memcpy
(
aead_prefs
,
aead
,
(
naead_prefs
=
naead
));
mdc_available
=
mdc
;
aead_available
=
!!
naead
;
ks_modify
=
modify
;
prefs_initialized
=
1
;
}
}
return
rc
;
}
/* Return a fake user ID containing the preferences. Caller must
free. */
PKT_user_id
*
keygen_get_std_prefs
(
void
)
{
int
i
,
j
=
0
;
PKT_user_id
*
uid
=
xmalloc_clear
(
sizeof
(
PKT_user_id
));
if
(
!
prefs_initialized
)
keygen_set_std_prefs
(
NULL
,
0
);
uid
->
ref
=
1
;
uid
->
prefs
=
xmalloc
((
sizeof
(
prefitem_t
*
)
*
(
nsym_prefs
+
naead_prefs
+
nhash_prefs
+
nzip_prefs
+
1
)));
for
(
i
=
0
;
i
<
nsym_prefs
;
i
++
,
j
++
)
{
uid
->
prefs
[
j
].
type
=
PREFTYPE_SYM
;
uid
->
prefs
[
j
].
value
=
sym_prefs
[
i
];
}
for
(
i
=
0
;
i
<
naead_prefs
;
i
++
,
j
++
)
{
uid
->
prefs
[
j
].
type
=
PREFTYPE_AEAD
;
uid
->
prefs
[
j
].
value
=
aead_prefs
[
i
];
}
for
(
i
=
0
;
i
<
nhash_prefs
;
i
++
,
j
++
)
{
uid
->
prefs
[
j
].
type
=
PREFTYPE_HASH
;
uid
->
prefs
[
j
].
value
=
hash_prefs
[
i
];
}
for
(
i
=
0
;
i
<
nzip_prefs
;
i
++
,
j
++
)
{
uid
->
prefs
[
j
].
type
=
PREFTYPE_ZIP
;
uid
->
prefs
[
j
].
value
=
zip_prefs
[
i
];
}
uid
->
prefs
[
j
].
type
=
PREFTYPE_NONE
;
uid
->
prefs
[
j
].
value
=
0
;
uid
->
flags
.
mdc
=
mdc_available
;
uid
->
flags
.
aead
=
aead_available
;
uid
->
flags
.
ks_modify
=
ks_modify
;
return
uid
;
}
static
void
add_feature_mdc
(
PKT_signature
*
sig
,
int
enabled
)
{
const
byte
*
s
;
size_t
n
;
int
i
;
char
*
buf
;
s
=
parse_sig_subpkt
(
sig
,
1
,
SIGSUBPKT_FEATURES
,
&
n
);
/* Already set or cleared */
if
(
s
&&
n
&&
((
enabled
&&
(
s
[
0
]
&
0x01
))
||
(
!
enabled
&&
!
(
s
[
0
]
&
0x01
))))
return
;
if
(
!
s
||
!
n
)
{
/* create a new one */
n
=
1
;
buf
=
xmalloc_clear
(
n
);
}
else
{
buf
=
xmalloc
(
n
);
memcpy
(
buf
,
s
,
n
);
}
if
(
enabled
)
buf
[
0
]
|=
0x01
;
/* MDC feature */
else
buf
[
0
]
&=
~
0x01
;
/* Are there any bits set? */
for
(
i
=
0
;
i
<
n
;
i
++
)
if
(
buf
[
i
]
!=
0
)
break
;
if
(
i
==
n
)
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_FEATURES
);
else
build_sig_subpkt
(
sig
,
SIGSUBPKT_FEATURES
,
buf
,
n
);
xfree
(
buf
);
}
static
void
add_feature_aead
(
PKT_signature
*
sig
,
int
enabled
)
{
const
byte
*
s
;
size_t
n
;
int
i
;
char
*
buf
;
s
=
parse_sig_subpkt
(
sig
,
1
,
SIGSUBPKT_FEATURES
,
&
n
);
if
(
s
&&
n
&&
((
enabled
&&
(
s
[
0
]
&
0x02
))
||
(
!
enabled
&&
!
(
s
[
0
]
&
0x02
))))
return
;
/* Already set or cleared */
if
(
!
s
||
!
n
)
{
/* Create a new one */
n
=
1
;
buf
=
xmalloc_clear
(
n
);
}
else
{
buf
=
xmalloc
(
n
);
memcpy
(
buf
,
s
,
n
);
}
if
(
enabled
)
buf
[
0
]
|=
0x02
;
/* AEAD supported */
else
buf
[
0
]
&=
~
0x02
;
/* Are there any bits set? */
for
(
i
=
0
;
i
<
n
;
i
++
)
if
(
buf
[
i
])
break
;
if
(
i
==
n
)
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_FEATURES
);
else
build_sig_subpkt
(
sig
,
SIGSUBPKT_FEATURES
,
buf
,
n
);
xfree
(
buf
);
}
static
void
add_feature_v5
(
PKT_signature
*
sig
,
int
enabled
)
{
const
byte
*
s
;
size_t
n
;
int
i
;
char
*
buf
;
s
=
parse_sig_subpkt
(
sig
,
1
,
SIGSUBPKT_FEATURES
,
&
n
);
if
(
s
&&
n
&&
((
enabled
&&
(
s
[
0
]
&
0x04
))
||
(
!
enabled
&&
!
(
s
[
0
]
&
0x04
))))
return
;
/* Already set or cleared */
if
(
!
s
||
!
n
)
{
/* Create a new one */
n
=
1
;
buf
=
xmalloc_clear
(
n
);
}
else
{
buf
=
xmalloc
(
n
);
memcpy
(
buf
,
s
,
n
);
}
if
(
enabled
)
buf
[
0
]
|=
0x04
;
/* v5 key supported */
else
buf
[
0
]
&=
~
0x04
;
/* Are there any bits set? */
for
(
i
=
0
;
i
<
n
;
i
++
)
if
(
buf
[
i
])
break
;
if
(
i
==
n
)
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_FEATURES
);
else
build_sig_subpkt
(
sig
,
SIGSUBPKT_FEATURES
,
buf
,
n
);
xfree
(
buf
);
}
static
void
add_keyserver_modify
(
PKT_signature
*
sig
,
int
enabled
)
{
const
byte
*
s
;
size_t
n
;
int
i
;
char
*
buf
;
/* The keyserver modify flag is a negative flag (i.e. no-modify) */
enabled
=!
enabled
;
s
=
parse_sig_subpkt
(
sig
,
1
,
SIGSUBPKT_KS_FLAGS
,
&
n
);
/* Already set or cleared */
if
(
s
&&
n
&&
((
enabled
&&
(
s
[
0
]
&
0x80
))
||
(
!
enabled
&&
!
(
s
[
0
]
&
0x80
))))
return
;
if
(
!
s
||
!
n
)
{
/* create a new one */
n
=
1
;
buf
=
xmalloc_clear
(
n
);
}
else
{
buf
=
xmalloc
(
n
);
memcpy
(
buf
,
s
,
n
);
}
if
(
enabled
)
buf
[
0
]
|=
0x80
;
/* no-modify flag */
else
buf
[
0
]
&=
~
0x80
;
/* Are there any bits set? */
for
(
i
=
0
;
i
<
n
;
i
++
)
if
(
buf
[
i
]
!=
0
)
break
;
if
(
i
==
n
)
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_KS_FLAGS
);
else
build_sig_subpkt
(
sig
,
SIGSUBPKT_KS_FLAGS
,
buf
,
n
);
xfree
(
buf
);
}
int
keygen_upd_std_prefs
(
PKT_signature
*
sig
,
void
*
opaque
)
{
(
void
)
opaque
;
if
(
!
prefs_initialized
)
keygen_set_std_prefs
(
NULL
,
0
);
if
(
nsym_prefs
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_SYM
,
sym_prefs
,
nsym_prefs
);
else
{
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_PREF_SYM
);
delete_sig_subpkt
(
sig
->
unhashed
,
SIGSUBPKT_PREF_SYM
);
}
if
(
naead_prefs
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_AEAD
,
aead_prefs
,
naead_prefs
);
else
{
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_PREF_AEAD
);
delete_sig_subpkt
(
sig
->
unhashed
,
SIGSUBPKT_PREF_AEAD
);
}
if
(
nhash_prefs
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_HASH
,
hash_prefs
,
nhash_prefs
);
else
{
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_PREF_HASH
);
delete_sig_subpkt
(
sig
->
unhashed
,
SIGSUBPKT_PREF_HASH
);
}
if
(
nzip_prefs
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_COMPR
,
zip_prefs
,
nzip_prefs
);
else
{
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_PREF_COMPR
);
delete_sig_subpkt
(
sig
->
unhashed
,
SIGSUBPKT_PREF_COMPR
);
}
/* Make sure that the MDC feature flag is set if needed. */
add_feature_mdc
(
sig
,
mdc_available
);
add_feature_aead
(
sig
,
aead_available
);
add_feature_v5
(
sig
,
opt
.
flags
.
rfc4880bis
);
add_keyserver_modify
(
sig
,
ks_modify
);
keygen_add_keyserver_url
(
sig
,
NULL
);
return
0
;
}
/****************
* Add preference to the self signature packet.
* This is only called for packets with version > 3.
*/
int
keygen_add_std_prefs
(
PKT_signature
*
sig
,
void
*
opaque
)
{
PKT_public_key
*
pk
=
opaque
;
do_add_key_flags
(
sig
,
pk
->
pubkey_usage
);
keygen_add_key_expire
(
sig
,
opaque
);
keygen_upd_std_prefs
(
sig
,
opaque
);
keygen_add_keyserver_url
(
sig
,
NULL
);
return
0
;
}
int
keygen_add_keyserver_url
(
PKT_signature
*
sig
,
void
*
opaque
)
{
const
char
*
url
=
opaque
;
if
(
!
url
)
url
=
opt
.
def_keyserver_url
;
if
(
url
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_KS
,
url
,
strlen
(
url
));
else
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_PREF_KS
);
return
0
;
}
int
keygen_add_notations
(
PKT_signature
*
sig
,
void
*
opaque
)
{
struct
notation
*
notation
;
/* We always start clean */
delete_sig_subpkt
(
sig
->
hashed
,
SIGSUBPKT_NOTATION
);
delete_sig_subpkt
(
sig
->
unhashed
,
SIGSUBPKT_NOTATION
);
sig
->
flags
.
notation
=
0
;
for
(
notation
=
opaque
;
notation
;
notation
=
notation
->
next
)
if
(
!
notation
->
flags
.
ignore
)
{
unsigned
char
*
buf
;
unsigned
int
n1
,
n2
;
n1
=
strlen
(
notation
->
name
);
if
(
notation
->
altvalue
)
n2
=
strlen
(
notation
->
altvalue
);
else
if
(
notation
->
bdat
)
n2
=
notation
->
blen
;
else
n2
=
strlen
(
notation
->
value
);
buf
=
xmalloc
(
8
+
n1
+
n2
);
/* human readable or not */
buf
[
0
]
=
notation
->
bdat
?
0
:
0x80
;
buf
[
1
]
=
buf
[
2
]
=
buf
[
3
]
=
0
;
buf
[
4
]
=
n1
>>
8
;
buf
[
5
]
=
n1
;
buf
[
6
]
=
n2
>>
8
;
buf
[
7
]
=
n2
;
memcpy
(
buf
+
8
,
notation
->
name
,
n1
);
if
(
notation
->
altvalue
)
memcpy
(
buf
+
8
+
n1
,
notation
->
altvalue
,
n2
);
else
if
(
notation
->
bdat
)
memcpy
(
buf
+
8
+
n1
,
notation
->
bdat
,
n2
);
else
memcpy
(
buf
+
8
+
n1
,
notation
->
value
,
n2
);
build_sig_subpkt
(
sig
,
SIGSUBPKT_NOTATION
|
(
notation
->
flags
.
critical
?
SIGSUBPKT_FLAG_CRITICAL
:
0
),
buf
,
8
+
n1
+
n2
);
xfree
(
buf
);
}
return
0
;
}
int
keygen_add_revkey
(
PKT_signature
*
sig
,
void
*
opaque
)
{
struct
revocation_key
*
revkey
=
opaque
;
byte
buf
[
2
+
MAX_FINGERPRINT_LEN
];
log_assert
(
revkey
->
fprlen
<=
MAX_FINGERPRINT_LEN
);
buf
[
0
]
=
revkey
->
class
;
buf
[
1
]
=
revkey
->
algid
;
memcpy
(
buf
+
2
,
revkey
->
fpr
,
revkey
->
fprlen
);
memset
(
buf
+
2
+
revkey
->
fprlen
,
0
,
sizeof
(
revkey
->
fpr
)
-
revkey
->
fprlen
);
build_sig_subpkt
(
sig
,
SIGSUBPKT_REV_KEY
,
buf
,
2
+
revkey
->
fprlen
);
/* All sigs with revocation keys set are nonrevocable. */
sig
->
flags
.
revocable
=
0
;
buf
[
0
]
=
0
;
build_sig_subpkt
(
sig
,
SIGSUBPKT_REVOCABLE
,
buf
,
1
);
parse_revkeys
(
sig
);
return
0
;
}
/* Create a back-signature. If TIMESTAMP is not NULL, use it for the
signature creation time. */
gpg_error_t
make_backsig
(
ctrl_t
ctrl
,
PKT_signature
*
sig
,
PKT_public_key
*
pk
,
PKT_public_key
*
sub_pk
,
PKT_public_key
*
sub_psk
,
u32
timestamp
,
const
char
*
cache_nonce
)
{
gpg_error_t
err
;
PKT_signature
*
backsig
;
cache_public_key
(
sub_pk
);
err
=
make_keysig_packet
(
ctrl
,
&
backsig
,
pk
,
NULL
,
sub_pk
,
sub_psk
,
0x19
,
timestamp
,
0
,
NULL
,
NULL
,
cache_nonce
);
if
(
err
)
log_error
(
"make_keysig_packet failed for backsig: %s
\n
"
,
gpg_strerror
(
err
));
else
{
/* Get it into a binary packed form. */
IOBUF
backsig_out
=
iobuf_temp
();
PACKET
backsig_pkt
;
init_packet
(
&
backsig_pkt
);
backsig_pkt
.
pkttype
=
PKT_SIGNATURE
;
backsig_pkt
.
pkt
.
signature
=
backsig
;
err
=
build_packet
(
backsig_out
,
&
backsig_pkt
);
free_packet
(
&
backsig_pkt
,
NULL
);
if
(
err
)
log_error
(
"build_packet failed for backsig: %s
\n
"
,
gpg_strerror
(
err
));
else
{
size_t
pktlen
=
0
;
byte
*
buf
=
iobuf_get_temp_buffer
(
backsig_out
);
/* Remove the packet header. */
if
(
buf
[
0
]
&
0x40
)
{
if
(
buf
[
1
]
<
192
)
{
pktlen
=
buf
[
1
];
buf
+=
2
;
}
else
if
(
buf
[
1
]
<
224
)
{
pktlen
=
(
buf
[
1
]
-192
)
*
256
;
pktlen
+=
buf
[
2
]
+
192
;
buf
+=
3
;
}
else
if
(
buf
[
1
]
==
255
)
{
pktlen
=
buf32_to_size_t
(
buf
+
2
);
buf
+=
6
;
}
else
BUG
();
}
else
{
int
mark
=
1
;
switch
(
buf
[
0
]
&
3
)
{
case
3
:
BUG
();
break
;
case
2
:
pktlen
=
(
size_t
)
buf
[
mark
++
]
<<
24
;
pktlen
|=
buf
[
mark
++
]
<<
16
;
/* fall through */
case
1
:
pktlen
|=
buf
[
mark
++
]
<<
8
;
/* fall through */
case
0
:
pktlen
|=
buf
[
mark
++
];
}
buf
+=
mark
;
}
/* Now make the binary blob into a subpacket. */
build_sig_subpkt
(
sig
,
SIGSUBPKT_SIGNATURE
,
buf
,
pktlen
);
iobuf_close
(
backsig_out
);
}
}
return
err
;
}
/* Write a direct key signature to the first key in ROOT using the key
PSK. REVKEY is describes the direct key signature and TIMESTAMP is
the timestamp to set on the signature. */
static
gpg_error_t
write_direct_sig
(
ctrl_t
ctrl
,
kbnode_t
root
,
PKT_public_key
*
psk
,
struct
revocation_key
*
revkey
,
u32
timestamp
,
const
char
*
cache_nonce
)
{
gpg_error_t
err
;
PACKET
*
pkt
;
PKT_signature
*
sig
;
KBNODE
node
;
PKT_public_key
*
pk
;
if
(
opt
.
verbose
)
log_info
(
_
(
"writing direct signature
\n
"
));
/* Get the pk packet from the pub_tree. */
node
=
find_kbnode
(
root
,
PKT_PUBLIC_KEY
);
if
(
!
node
)
BUG
();
pk
=
node
->
pkt
->
pkt
.
public_key
;
/* We have to cache the key, so that the verification of the
signature creation is able to retrieve the public key. */
cache_public_key
(
pk
);
/* Make the signature. */
err
=
make_keysig_packet
(
ctrl
,
&
sig
,
pk
,
NULL
,
NULL
,
psk
,
0x1F
,
timestamp
,
0
,
keygen_add_revkey
,
revkey
,
cache_nonce
);
if
(
err
)
{
log_error
(
"make_keysig_packet failed: %s
\n
"
,
gpg_strerror
(
err
)
);
return
err
;
}
pkt
=
xmalloc_clear
(
sizeof
*
pkt
);
pkt
->
pkttype
=
PKT_SIGNATURE
;
pkt
->
pkt
.
signature
=
sig
;
add_kbnode
(
root
,
new_kbnode
(
pkt
));
return
err
;
}
/* Write a self-signature to the first user id in ROOT using the key
PSK. USE and TIMESTAMP give the extra data we need for the
signature. */
static
gpg_error_t
write_selfsigs
(
ctrl_t
ctrl
,
kbnode_t
root
,
PKT_public_key
*
psk
,
unsigned
int
use
,
u32
timestamp
,
const
char
*
cache_nonce
)
{
gpg_error_t
err
;
PACKET
*
pkt
;
PKT_signature
*
sig
;
PKT_user_id
*
uid
;
KBNODE
node
;
PKT_public_key
*
pk
;
if
(
opt
.
verbose
)
log_info
(
_
(
"writing self signature
\n
"
));
/* Get the uid packet from the list. */
node
=
find_kbnode
(
root
,
PKT_USER_ID
);
if
(
!
node
)
BUG
();
/* No user id packet in tree. */
uid
=
node
->
pkt
->
pkt
.
user_id
;
/* Get the pk packet from the pub_tree. */
node
=
find_kbnode
(
root
,
PKT_PUBLIC_KEY
);
if
(
!
node
)
BUG
();
pk
=
node
->
pkt
->
pkt
.
public_key
;
/* The usage has not yet been set - do it now. */
pk
->
pubkey_usage
=
use
;
/* We have to cache the key, so that the verification of the
signature creation is able to retrieve the public key. */
cache_public_key
(
pk
);
/* Make the signature. */
err
=
make_keysig_packet
(
ctrl
,
&
sig
,
pk
,
uid
,
NULL
,
psk
,
0x13
,
timestamp
,
0
,
keygen_add_std_prefs
,
pk
,
cache_nonce
);
if
(
err
)
{
log_error
(
"make_keysig_packet failed: %s
\n
"
,
gpg_strerror
(
err
));
return
err
;
}
pkt
=
xmalloc_clear
(
sizeof
*
pkt
);
pkt
->
pkttype
=
PKT_SIGNATURE
;
pkt
->
pkt
.
signature
=
sig
;
add_kbnode
(
root
,
new_kbnode
(
pkt
));
return
err
;
}
/* Write the key binding signature. If TIMESTAMP is not NULL use the
signature creation time. PRI_PSK is the key use for signing.
SUB_PSK is a key used to create a back-signature; that one is only
used if USE has the PUBKEY_USAGE_SIG capability. */
static
int
write_keybinding
(
ctrl_t
ctrl
,
kbnode_t
root
,
PKT_public_key
*
pri_psk
,
PKT_public_key
*
sub_psk
,
unsigned
int
use
,
u32
timestamp
,
const
char
*
cache_nonce
)
{
gpg_error_t
err
;
PACKET
*
pkt
;
PKT_signature
*
sig
;
KBNODE
node
;
PKT_public_key
*
pri_pk
,
*
sub_pk
;
struct
opaque_data_usage_and_pk
oduap
;
if
(
opt
.
verbose
)
log_info
(
_
(
"writing key binding signature
\n
"
));
/* Get the primary pk packet from the tree. */
node
=
find_kbnode
(
root
,
PKT_PUBLIC_KEY
);
if
(
!
node
)
BUG
();
pri_pk
=
node
->
pkt
->
pkt
.
public_key
;
/* We have to cache the key, so that the verification of the
* signature creation is able to retrieve the public key. */
cache_public_key
(
pri_pk
);
/* Find the last subkey. */
sub_pk
=
NULL
;
for
(
node
=
root
;
node
;
node
=
node
->
next
)
{
if
(
node
->
pkt
->
pkttype
==
PKT_PUBLIC_SUBKEY
)
sub_pk
=
node
->
pkt
->
pkt
.
public_key
;
}
if
(
!
sub_pk
)
BUG
();
/* Make the signature. */
oduap
.
usage
=
use
;
oduap
.
pk
=
sub_pk
;
err
=
make_keysig_packet
(
ctrl
,
&
sig
,
pri_pk
,
NULL
,
sub_pk
,
pri_psk
,
0x18
,
timestamp
,
0
,
keygen_add_key_flags_and_expire
,
&
oduap
,
cache_nonce
);
if
(
err
)
{
log_error
(
"make_keysig_packeto failed: %s
\n
"
,
gpg_strerror
(
err
));
return
err
;
}
/* Make a backsig. */
if
(
use
&
PUBKEY_USAGE_SIG
)
{
err
=
make_backsig
(
ctrl
,
sig
,
pri_pk
,
sub_pk
,
sub_psk
,
timestamp
,
cache_nonce
);
if
(
err
)
return
err
;
}
pkt
=
xmalloc_clear
(
sizeof
*
pkt
);
pkt
->
pkttype
=
PKT_SIGNATURE
;
pkt
->
pkt
.
signature
=
sig
;
add_kbnode
(
root
,
new_kbnode
(
pkt
)
);
return
err
;
}
static
gpg_error_t
ecckey_from_sexp
(
gcry_mpi_t
*
array
,
gcry_sexp_t
sexp
,
int
algo
)
{
gpg_error_t
err
;
gcry_sexp_t
list
,
l2
;
char
*
curve
=
NULL
;
int
i
;
const
char
*
oidstr
;
unsigned
int
nbits
;
array
[
0
]
=
NULL
;
array
[
1
]
=
NULL
;
array
[
2
]
=
NULL
;
list
=
gcry_sexp_find_token
(
sexp
,
"public-key"
,
0
);
if
(
!
list
)
return
gpg_error
(
GPG_ERR_INV_OBJ
);
l2
=
gcry_sexp_cadr
(
list
);
gcry_sexp_release
(
list
);
list
=
l2
;
if
(
!
list
)
return
gpg_error
(
GPG_ERR_NO_OBJ
);
l2
=
gcry_sexp_find_token
(
list
,
"curve"
,
0
);
if
(
!
l2
)
{
err
=
gpg_error
(
GPG_ERR_NO_OBJ
);
goto
leave
;
}
curve
=
gcry_sexp_nth_string
(
l2
,
1
);
if
(
!
curve
)
{
err
=
gpg_error
(
GPG_ERR_NO_OBJ
);
goto
leave
;
}
gcry_sexp_release
(
l2
);
oidstr
=
openpgp_curve_to_oid
(
curve
,
&
nbits
,
NULL
);
if
(
!
oidstr
)
{
/* That can't happen because we used one of the curves
gpg_curve_to_oid knows about. */
err
=
gpg_error
(
GPG_ERR_INV_OBJ
);
goto
leave
;
}
err
=
openpgp_oid_from_str
(
oidstr
,
&
array
[
0
]);
if
(
err
)
goto
leave
;
err
=
sexp_extract_param_sos
(
list
,
"q"
,
&
array
[
1
]);
if
(
err
)
goto
leave
;
gcry_sexp_release
(
list
);
if
(
algo
==
PUBKEY_ALGO_ECDH
)
{
array
[
2
]
=
pk_ecdh_default_params
(
nbits
);
if
(
!
array
[
2
])
{
err
=
gpg_error_from_syserror
();
goto
leave
;
}
}
leave
:
xfree
(
curve
);
if
(
err
)
{
for
(
i
=
0
;
i
<
3
;
i
++
)
{
gcry_mpi_release
(
array
[
i
]);
array
[
i
]
=
NULL
;
}
}
return
err
;
}
/* Extract key parameters from SEXP and store them in ARRAY. ELEMS is
a string where each character denotes a parameter name. TOPNAME is
the name of the top element above the elements. */
static
int
key_from_sexp
(
gcry_mpi_t
*
array
,
gcry_sexp_t
sexp
,
const
char
*
topname
,
const
char
*
elems
)
{
gcry_sexp_t
list
,
l2
;
const
char
*
s
;
int
i
,
idx
;
int
rc
=
0
;
list
=
gcry_sexp_find_token
(
sexp
,
topname
,
0
);
if
(
!
list
)
return
gpg_error
(
GPG_ERR_INV_OBJ
);
l2
=
gcry_sexp_cadr
(
list
);
gcry_sexp_release
(
list
);
list
=
l2
;
if
(
!
list
)
return
gpg_error
(
GPG_ERR_NO_OBJ
);
for
(
idx
=
0
,
s
=
elems
;
*
s
;
s
++
,
idx
++
)
{
l2
=
gcry_sexp_find_token
(
list
,
s
,
1
);
if
(
!
l2
)
{
rc
=
gpg_error
(
GPG_ERR_NO_OBJ
);
/* required parameter not found */
goto
leave
;
}
array
[
idx
]
=
gcry_sexp_nth_mpi
(
l2
,
1
,
GCRYMPI_FMT_USG
);
gcry_sexp_release
(
l2
);
if
(
!
array
[
idx
])
{
rc
=
gpg_error
(
GPG_ERR_INV_OBJ
);
/* required parameter invalid */
goto
leave
;
}
}
gcry_sexp_release
(
list
);
leave
:
if
(
rc
)
{
for
(
i
=
0
;
i
<
idx
;
i
++
)
{
gcry_mpi_release
(
array
[
i
]);
array
[
i
]
=
NULL
;
}
gcry_sexp_release
(
list
);
}
return
rc
;
}
/* Create a keyblock using the given KEYGRIP. ALGO is the OpenPGP
* algorithm of that keygrip. If CARDKEY is true the key is expected
* to already live on the active card. */
static
int
do_create_from_keygrip
(
ctrl_t
ctrl
,
int
algo
,
const
char
*
hexkeygrip
,
int
cardkey
,
kbnode_t
pub_root
,
u32
timestamp
,
u32
expireval
,
int
is_subkey
,
int
keygen_flags
)
{
int
err
;
PACKET
*
pkt
;
PKT_public_key
*
pk
;
gcry_sexp_t
s_key
;
const
char
*
algoelem
;
if
(
hexkeygrip
[
0
]
==
'&'
)
hexkeygrip
++
;
switch
(
algo
)
{
case
PUBKEY_ALGO_RSA
:
algoelem
=
"ne"
;
break
;
case
PUBKEY_ALGO_DSA
:
algoelem
=
"pqgy"
;
break
;
case
PUBKEY_ALGO_ELGAMAL_E
:
algoelem
=
"pgy"
;
break
;
case
PUBKEY_ALGO_ECDH
:
case
PUBKEY_ALGO_ECDSA
:
algoelem
=
""
;
break
;
case
PUBKEY_ALGO_EDDSA
:
algoelem
=
""
;
break
;
default
:
return
gpg_error
(
GPG_ERR_INTERNAL
);
}
/* Ask the agent for the public key matching HEXKEYGRIP. */
if
(
cardkey
)
{
err
=
agent_scd_readkey
(
ctrl
,
hexkeygrip
,
&
s_key
,
NULL
);
if
(
err
)
return
err
;
}
else
{
unsigned
char
*
public
;
err
=
agent_readkey
(
ctrl
,
0
,
hexkeygrip
,
&
public
);
if
(
err
)
return
err
;
err
=
gcry_sexp_sscan
(
&
s_key
,
NULL
,
public
,
gcry_sexp_canon_len
(
public
,
0
,
NULL
,
NULL
));
xfree
(
public
);
if
(
err
)
return
err
;
}
/* Build a public key packet. */
pk
=
xtrycalloc
(
1
,
sizeof
*
pk
);
if
(
!
pk
)
{
err
=
gpg_error_from_syserror
();
gcry_sexp_release
(
s_key
);
return
err
;
}
pk
->
timestamp
=
timestamp
;
pk
->
version
=
(
keygen_flags
&
KEYGEN_FLAG_CREATE_V5_KEY
)
?
5
:
4
;
if
(
expireval
)
pk
->
expiredate
=
pk
->
timestamp
+
expireval
;
pk
->
pubkey_algo
=
algo
;
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
err
=
ecckey_from_sexp
(
pk
->
pkey
,
s_key
,
algo
);
else
err
=
key_from_sexp
(
pk
->
pkey
,
s_key
,
"public-key"
,
algoelem
);
if
(
err
)
{
log_error
(
"key_from_sexp failed: %s
\n
"
,
gpg_strerror
(
err
)
);
gcry_sexp_release
(
s_key
);
free_public_key
(
pk
);
return
err
;
}
gcry_sexp_release
(
s_key
);
pkt
=
xtrycalloc
(
1
,
sizeof
*
pkt
);
if
(
!
pkt
)
{
err
=
gpg_error_from_syserror
();
free_public_key
(
pk
);
return
err
;
}
pkt
->
pkttype
=
is_subkey
?
PKT_PUBLIC_SUBKEY
:
PKT_PUBLIC_KEY
;
pkt
->
pkt
.
public_key
=
pk
;
add_kbnode
(
pub_root
,
new_kbnode
(
pkt
));
return
0
;
}
/* Common code for the key generation function gen_xxx. */
static
int
common_gen
(
const
char
*
keyparms
,
int
algo
,
const
char
*
algoelem
,
kbnode_t
pub_root
,
u32
timestamp
,
u32
expireval
,
int
is_subkey
,
int
keygen_flags
,
const
char
*
passphrase
,
char
**
cache_nonce_addr
,
char
**
passwd_nonce_addr
)
{
int
err
;
PACKET
*
pkt
;
PKT_public_key
*
pk
;
gcry_sexp_t
s_key
;
err
=
agent_genkey
(
NULL
,
cache_nonce_addr
,
passwd_nonce_addr
,
keyparms
,
!!
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
),
passphrase
,
timestamp
,
&
s_key
);
if
(
err
)
{
log_error
(
"agent_genkey failed: %s
\n
"
,
gpg_strerror
(
err
)
);
return
err
;
}
pk
=
xtrycalloc
(
1
,
sizeof
*
pk
);
if
(
!
pk
)
{
err
=
gpg_error_from_syserror
();
gcry_sexp_release
(
s_key
);
return
err
;
}
pk
->
timestamp
=
timestamp
;
pk
->
version
=
(
keygen_flags
&
KEYGEN_FLAG_CREATE_V5_KEY
)
?
5
:
4
;
if
(
expireval
)
pk
->
expiredate
=
pk
->
timestamp
+
expireval
;
pk
->
pubkey_algo
=
algo
;
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
err
=
ecckey_from_sexp
(
pk
->
pkey
,
s_key
,
algo
);
else
err
=
key_from_sexp
(
pk
->
pkey
,
s_key
,
"public-key"
,
algoelem
);
if
(
err
)
{
log_error
(
"key_from_sexp failed: %s
\n
"
,
gpg_strerror
(
err
)
);
gcry_sexp_release
(
s_key
);
free_public_key
(
pk
);
return
err
;
}
gcry_sexp_release
(
s_key
);
pkt
=
xtrycalloc
(
1
,
sizeof
*
pkt
);
if
(
!
pkt
)
{
err
=
gpg_error_from_syserror
();
free_public_key
(
pk
);
return
err
;
}
pkt
->
pkttype
=
is_subkey
?
PKT_PUBLIC_SUBKEY
:
PKT_PUBLIC_KEY
;
pkt
->
pkt
.
public_key
=
pk
;
add_kbnode
(
pub_root
,
new_kbnode
(
pkt
));
return
0
;
}
/*
* Generate an Elgamal key.
*/
static
int
gen_elg
(
int
algo
,
unsigned
int
nbits
,
KBNODE
pub_root
,
u32
timestamp
,
u32
expireval
,
int
is_subkey
,
int
keygen_flags
,
const
char
*
passphrase
,
char
**
cache_nonce_addr
,
char
**
passwd_nonce_addr
)
{
int
err
;
char
*
keyparms
;
char
nbitsstr
[
35
];
log_assert
(
is_ELGAMAL
(
algo
));
if
(
nbits
<
1024
)
{
nbits
=
2048
;
log_info
(
_
(
"keysize invalid; using %u bits
\n
"
),
nbits
);
}
else
if
(
nbits
>
4096
)
{
nbits
=
4096
;
log_info
(
_
(
"keysize invalid; using %u bits
\n
"
),
nbits
);
}
if
((
nbits
%
32
))
{
nbits
=
((
nbits
+
31
)
/
32
)
*
32
;
log_info
(
_
(
"keysize rounded up to %u bits
\n
"
),
nbits
);
}
/* Note that we use transient-key only if no-protection has also
been enabled. */
snprintf
(
nbitsstr
,
sizeof
nbitsstr
,
"%u"
,
nbits
);
keyparms
=
xtryasprintf
(
"(genkey(%s(nbits %zu:%s)%s))"
,
algo
==
GCRY_PK_ELG_E
?
"openpgp-elg"
:
algo
==
GCRY_PK_ELG
?
"elg"
:
"x-oops"
,
strlen
(
nbitsstr
),
nbitsstr
,
((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
"(transient-key)"
:
""
);
if
(
!
keyparms
)
err
=
gpg_error_from_syserror
();
else
{
err
=
common_gen
(
keyparms
,
algo
,
"pgy"
,
pub_root
,
timestamp
,
expireval
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
xfree
(
keyparms
);
}
return
err
;
}
/*
* Generate an DSA key
*/
static
gpg_error_t
gen_dsa
(
unsigned
int
nbits
,
KBNODE
pub_root
,
u32
timestamp
,
u32
expireval
,
int
is_subkey
,
int
keygen_flags
,
const
char
*
passphrase
,
char
**
cache_nonce_addr
,
char
**
passwd_nonce_addr
)
{
int
err
;
unsigned
int
qbits
;
char
*
keyparms
;
char
nbitsstr
[
35
];
char
qbitsstr
[
35
];
if
(
nbits
<
768
)
{
nbits
=
2048
;
log_info
(
_
(
"keysize invalid; using %u bits
\n
"
),
nbits
);
}
else
if
(
nbits
>
3072
)
{
nbits
=
3072
;
log_info
(
_
(
"keysize invalid; using %u bits
\n
"
),
nbits
);
}
if
(
(
nbits
%
64
)
)
{
nbits
=
((
nbits
+
63
)
/
64
)
*
64
;
log_info
(
_
(
"keysize rounded up to %u bits
\n
"
),
nbits
);
}
/* To comply with FIPS rules we round up to the next value unless in
expert mode. */
if
(
!
opt
.
expert
&&
nbits
>
1024
&&
(
nbits
%
1024
))
{
nbits
=
((
nbits
+
1023
)
/
1024
)
*
1024
;
log_info
(
_
(
"keysize rounded up to %u bits
\n
"
),
nbits
);
}
/*
Figure out a q size based on the key size. FIPS 180-3 says:
L = 1024, N = 160
L = 2048, N = 224
L = 2048, N = 256
L = 3072, N = 256
2048/256 is an odd pair since there is also a 2048/224 and
3072/256. Matching sizes is not a very exact science.
We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024
but less than 2048, and 160 for 1024 (DSA1).
*/
if
(
nbits
>
2047
)
qbits
=
256
;
else
if
(
nbits
>
1024
)
qbits
=
224
;
else
qbits
=
160
;
if
(
qbits
!=
160
)
log_info
(
_
(
"WARNING: some OpenPGP programs can't"
" handle a DSA key with this digest size
\n
"
));
snprintf
(
nbitsstr
,
sizeof
nbitsstr
,
"%u"
,
nbits
);
snprintf
(
qbitsstr
,
sizeof
qbitsstr
,
"%u"
,
qbits
);
keyparms
=
xtryasprintf
(
"(genkey(dsa(nbits %zu:%s)(qbits %zu:%s)%s))"
,
strlen
(
nbitsstr
),
nbitsstr
,
strlen
(
qbitsstr
),
qbitsstr
,
((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
"(transient-key)"
:
""
);
if
(
!
keyparms
)
err
=
gpg_error_from_syserror
();
else
{
err
=
common_gen
(
keyparms
,
PUBKEY_ALGO_DSA
,
"pqgy"
,
pub_root
,
timestamp
,
expireval
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
xfree
(
keyparms
);
}
return
err
;
}
/*
* Generate an ECC key
*/
static
gpg_error_t
gen_ecc
(
int
algo
,
const
char
*
curve
,
kbnode_t
pub_root
,
u32
timestamp
,
u32
expireval
,
int
is_subkey
,
int
keygen_flags
,
const
char
*
passphrase
,
char
**
cache_nonce_addr
,
char
**
passwd_nonce_addr
)
{
gpg_error_t
err
;
char
*
keyparms
;
log_assert
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
);
if
(
!
curve
||
!*
curve
)
return
gpg_error
(
GPG_ERR_UNKNOWN_CURVE
);
/* Map the displayed short forms of some curves to their canonical
* names. */
if
(
!
ascii_strcasecmp
(
curve
,
"cv25519"
))
curve
=
"Curve25519"
;
else
if
(
!
ascii_strcasecmp
(
curve
,
"ed25519"
))
curve
=
"Ed25519"
;
else
if
(
!
ascii_strcasecmp
(
curve
,
"cv448"
))
curve
=
"X448"
;
else
if
(
!
ascii_strcasecmp
(
curve
,
"ed448"
))
curve
=
"Ed448"
;
/* Note that we use the "comp" flag with EdDSA to request the use of
a 0x40 compression prefix octet. */
if
(
algo
==
PUBKEY_ALGO_EDDSA
&&
!
strcmp
(
curve
,
"Ed25519"
))
keyparms
=
xtryasprintf
(
"(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))"
,
strlen
(
curve
),
curve
,
(((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
" transient-key"
:
""
));
else
if
(
algo
==
PUBKEY_ALGO_EDDSA
&&
!
strcmp
(
curve
,
"Ed448"
))
keyparms
=
xtryasprintf
(
"(genkey(ecc(curve %zu:%s)(flags comp%s)))"
,
strlen
(
curve
),
curve
,
(((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
" transient-key"
:
""
));
else
if
(
algo
==
PUBKEY_ALGO_ECDH
&&
!
strcmp
(
curve
,
"Curve25519"
))
keyparms
=
xtryasprintf
(
"(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))"
,
strlen
(
curve
),
curve
,
(((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
" transient-key"
:
""
));
else
if
(
algo
==
PUBKEY_ALGO_ECDH
&&
!
strcmp
(
curve
,
"X448"
))
keyparms
=
xtryasprintf
(
"(genkey(ecc(curve %zu:%s)(flags comp%s)))"
,
strlen
(
curve
),
curve
,
(((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
" transient-key"
:
""
));
else
keyparms
=
xtryasprintf
(
"(genkey(ecc(curve %zu:%s)(flags nocomp%s)))"
,
strlen
(
curve
),
curve
,
(((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
" transient-key"
:
""
));
if
(
!
keyparms
)
err
=
gpg_error_from_syserror
();
else
{
err
=
common_gen
(
keyparms
,
algo
,
""
,
pub_root
,
timestamp
,
expireval
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
xfree
(
keyparms
);
}
return
err
;
}
/*
* Generate an RSA key.
*/
static
int
gen_rsa
(
int
algo
,
unsigned
int
nbits
,
KBNODE
pub_root
,
u32
timestamp
,
u32
expireval
,
int
is_subkey
,
int
keygen_flags
,
const
char
*
passphrase
,
char
**
cache_nonce_addr
,
char
**
passwd_nonce_addr
)
{
int
err
;
char
*
keyparms
;
char
nbitsstr
[
35
];
const
unsigned
maxsize
=
(
opt
.
flags
.
large_rsa
?
8192
:
4096
);
log_assert
(
is_RSA
(
algo
));
if
(
!
nbits
)
nbits
=
get_keysize_range
(
algo
,
NULL
,
NULL
);
if
(
nbits
<
1024
)
{
nbits
=
3072
;
log_info
(
_
(
"keysize invalid; using %u bits
\n
"
),
nbits
);
}
else
if
(
nbits
>
maxsize
)
{
nbits
=
maxsize
;
log_info
(
_
(
"keysize invalid; using %u bits
\n
"
),
nbits
);
}
if
((
nbits
%
32
))
{
nbits
=
((
nbits
+
31
)
/
32
)
*
32
;
log_info
(
_
(
"keysize rounded up to %u bits
\n
"
),
nbits
);
}
snprintf
(
nbitsstr
,
sizeof
nbitsstr
,
"%u"
,
nbits
);
keyparms
=
xtryasprintf
(
"(genkey(rsa(nbits %zu:%s)%s))"
,
strlen
(
nbitsstr
),
nbitsstr
,
((
keygen_flags
&
KEYGEN_FLAG_TRANSIENT_KEY
)
&&
(
keygen_flags
&
KEYGEN_FLAG_NO_PROTECTION
))
?
"(transient-key)"
:
""
);
if
(
!
keyparms
)
err
=
gpg_error_from_syserror
();
else
{
err
=
common_gen
(
keyparms
,
algo
,
"ne"
,
pub_root
,
timestamp
,
expireval
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
xfree
(
keyparms
);
}
return
err
;
}
/****************
* check valid days:
* return 0 on error or the multiplier
*/
static
int
check_valid_days
(
const
char
*
s
)
{
if
(
!
digitp
(
s
)
)
return
0
;
for
(
s
++
;
*
s
;
s
++
)
if
(
!
digitp
(
s
)
)
break
;
if
(
!*
s
)
return
1
;
if
(
s
[
1
]
)
return
0
;
/* e.g. "2323wc" */
if
(
*
s
==
'd'
||
*
s
==
'D'
)
return
1
;
if
(
*
s
==
'w'
||
*
s
==
'W'
)
return
7
;
if
(
*
s
==
'm'
||
*
s
==
'M'
)
return
30
;
if
(
*
s
==
'y'
||
*
s
==
'Y'
)
return
365
;
return
0
;
}
static
void
print_key_flags
(
int
flags
)
{
if
(
flags
&
PUBKEY_USAGE_SIG
)
tty_printf
(
"%s "
,
_
(
"Sign"
));
if
(
flags
&
PUBKEY_USAGE_CERT
)
tty_printf
(
"%s "
,
_
(
"Certify"
));
if
(
flags
&
PUBKEY_USAGE_ENC
)
tty_printf
(
"%s "
,
_
(
"Encrypt"
));
if
(
flags
&
PUBKEY_USAGE_AUTH
)
tty_printf
(
"%s "
,
_
(
"Authenticate"
));
}
/* Ask for the key flags and return them. CURRENT gives the current
* usage which should normally be given as 0. MASK gives the allowed
* flags. */
unsigned
int
ask_key_flags_with_mask
(
int
algo
,
int
subkey
,
unsigned
int
current
,
unsigned
int
mask
)
{
/* TRANSLATORS: Please use only plain ASCII characters for the
* translation. If this is not possible use single digits. The
* string needs to 8 bytes long. Here is a description of the
* functions:
*
* s = Toggle signing capability
* e = Toggle encryption capability
* a = Toggle authentication capability
* q = Finish
*/
const
char
*
togglers
=
_
(
"SsEeAaQq"
);
char
*
answer
=
NULL
;
const
char
*
s
;
unsigned
int
possible
;
if
(
strlen
(
togglers
)
!=
8
)
{
tty_printf
(
"NOTE: Bad translation at %s:%d. "
"Please report.
\n
"
,
__FILE__
,
__LINE__
);
togglers
=
"11223300"
;
}
/* Mask the possible usage flags. This is for example used for a
* card based key. For ECDH we need to allows additional usages if
* they are provided. */
possible
=
(
openpgp_pk_algo_usage
(
algo
)
&
mask
);
if
(
algo
==
PUBKEY_ALGO_ECDH
)
possible
|=
(
current
&
(
PUBKEY_USAGE_ENC
|
PUBKEY_USAGE_CERT
|
PUBKEY_USAGE_SIG
|
PUBKEY_USAGE_AUTH
));
/* However, only primary keys may certify. */
if
(
subkey
)
possible
&=
~
PUBKEY_USAGE_CERT
;
/* Preload the current set with the possible set, without
* authentication if CURRENT is 0. If CURRENT is non-zero we mask
* with all possible usages. */
if
(
current
)
current
&=
possible
;
else
current
=
(
possible
&~
PUBKEY_USAGE_AUTH
);
for
(;;)
{
tty_printf
(
"
\n
"
);
tty_printf
(
_
(
"Possible actions for this %s key: "
),
(
algo
==
PUBKEY_ALGO_ECDH
||
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
)
?
"ECC"
:
openpgp_pk_algo_name
(
algo
));
print_key_flags
(
possible
);
tty_printf
(
"
\n
"
);
tty_printf
(
_
(
"Current allowed actions: "
));
print_key_flags
(
current
);
tty_printf
(
"
\n\n
"
);
if
(
possible
&
PUBKEY_USAGE_SIG
)
tty_printf
(
_
(
" (%c) Toggle the sign capability
\n
"
),
togglers
[
0
]);
if
(
possible
&
PUBKEY_USAGE_ENC
)
tty_printf
(
_
(
" (%c) Toggle the encrypt capability
\n
"
),
togglers
[
2
]);
if
(
possible
&
PUBKEY_USAGE_AUTH
)
tty_printf
(
_
(
" (%c) Toggle the authenticate capability
\n
"
),
togglers
[
4
]);
tty_printf
(
_
(
" (%c) Finished
\n
"
),
togglers
[
6
]);
tty_printf
(
"
\n
"
);
xfree
(
answer
);
answer
=
cpr_get
(
"keygen.flags"
,
_
(
"Your selection? "
));
cpr_kill_prompt
();
if
(
*
answer
==
'='
)
{
/* Hack to allow direct entry of the capabilities. */
current
=
0
;
for
(
s
=
answer
+
1
;
*
s
;
s
++
)
{
if
((
*
s
==
's'
||
*
s
==
'S'
)
&&
(
possible
&
PUBKEY_USAGE_SIG
))
current
|=
PUBKEY_USAGE_SIG
;
else
if
((
*
s
==
'e'
||
*
s
==
'E'
)
&&
(
possible
&
PUBKEY_USAGE_ENC
))
current
|=
PUBKEY_USAGE_ENC
;
else
if
((
*
s
==
'a'
||
*
s
==
'A'
)
&&
(
possible
&
PUBKEY_USAGE_AUTH
))
current
|=
PUBKEY_USAGE_AUTH
;
else
if
(
!
subkey
&&
*
s
==
'c'
)
{
/* Accept 'c' for the primary key because USAGE_CERT
will be set anyway. This is for folks who
want to experiment with a cert-only primary key. */
current
|=
PUBKEY_USAGE_CERT
;
}
}
break
;
}
else
if
(
strlen
(
answer
)
>
1
)
tty_printf
(
_
(
"Invalid selection.
\n
"
));
else
if
(
*
answer
==
'\0'
||
*
answer
==
togglers
[
6
]
||
*
answer
==
togglers
[
7
])
break
;
else
if
((
*
answer
==
togglers
[
0
]
||
*
answer
==
togglers
[
1
])
&&
possible
&
PUBKEY_USAGE_SIG
)
{
if
(
current
&
PUBKEY_USAGE_SIG
)
current
&=~
PUBKEY_USAGE_SIG
;
else
current
|=
PUBKEY_USAGE_SIG
;
}
else
if
((
*
answer
==
togglers
[
2
]
||
*
answer
==
togglers
[
3
])
&&
possible
&
PUBKEY_USAGE_ENC
)
{
if
(
current
&
PUBKEY_USAGE_ENC
)
current
&=~
PUBKEY_USAGE_ENC
;
else
current
|=
PUBKEY_USAGE_ENC
;
}
else
if
((
*
answer
==
togglers
[
4
]
||
*
answer
==
togglers
[
5
])
&&
possible
&
PUBKEY_USAGE_AUTH
)
{
if
(
current
&
PUBKEY_USAGE_AUTH
)
current
&=~
PUBKEY_USAGE_AUTH
;
else
current
|=
PUBKEY_USAGE_AUTH
;
}
else
tty_printf
(
_
(
"Invalid selection.
\n
"
));
}
xfree
(
answer
);
return
current
;
}
unsigned
int
ask_key_flags
(
int
algo
,
int
subkey
,
unsigned
int
current
)
{
return
ask_key_flags_with_mask
(
algo
,
subkey
,
current
,
~
0
);
}
/* Check whether we have a key for the key with HEXGRIP. Returns 0 if
there is no such key or the OpenPGP algo number for the key. */
static
int
check_keygrip
(
ctrl_t
ctrl
,
const
char
*
hexgrip
)
{
gpg_error_t
err
;
unsigned
char
*
public
;
size_t
publiclen
;
int
algo
;
if
(
hexgrip
[
0
]
==
'&'
)
hexgrip
++
;
err
=
agent_readkey
(
ctrl
,
0
,
hexgrip
,
&
public
);
if
(
err
)
return
0
;
publiclen
=
gcry_sexp_canon_len
(
public
,
0
,
NULL
,
NULL
);
algo
=
get_pk_algo_from_canon_sexp
(
public
,
publiclen
);
xfree
(
public
);
return
map_gcry_pk_to_openpgp
(
algo
);
}
/* Ask for an algorithm. The function returns the algorithm id to
* create. If ADDMODE is false the function won't show an option to
* create the primary and subkey combined and won't set R_USAGE
* either. If a combined algorithm has been selected, the subkey
* algorithm is stored at R_SUBKEY_ALGO. If R_KEYGRIP is given, the
* user has the choice to enter the keygrip of an existing key. That
* keygrip is then stored at this address. The caller needs to free
* it. If R_CARDKEY is not NULL and the keygrip has been taken from
* an active card, true is stored there; if R_KEYTIME is not NULL the
* creation time of that key is then stored there. */
static
int
ask_algo
(
ctrl_t
ctrl
,
int
addmode
,
int
*
r_subkey_algo
,
unsigned
int
*
r_usage
,
char
**
r_keygrip
,
int
*
r_cardkey
,
u32
*
r_keytime
)
{
gpg_error_t
err
;
char
*
keygrip
=
NULL
;
u32
keytime
=
0
;
char
*
answer
=
NULL
;
int
cardkey
=
0
;
int
algo
;
int
dummy_algo
;
if
(
!
r_subkey_algo
)
r_subkey_algo
=
&
dummy_algo
;
tty_printf
(
_
(
"Please select what kind of key you want:
\n
"
));
#if GPG_USE_RSA
if
(
!
addmode
)
tty_printf
(
_
(
" (%d) RSA and RSA%s
\n
"
),
1
,
""
);
#endif
if
(
!
addmode
&&
opt
.
compliance
!=
CO_DE_VS
)
tty_printf
(
_
(
" (%d) DSA and Elgamal%s
\n
"
),
2
,
""
);
if
(
opt
.
compliance
!=
CO_DE_VS
)
tty_printf
(
_
(
" (%d) DSA (sign only)%s
\n
"
),
3
,
""
);
#if GPG_USE_RSA
tty_printf
(
_
(
" (%d) RSA (sign only)%s
\n
"
),
4
,
""
);
#endif
if
(
addmode
)
{
if
(
opt
.
compliance
!=
CO_DE_VS
)
tty_printf
(
_
(
" (%d) Elgamal (encrypt only)%s
\n
"
),
5
,
""
);
#if GPG_USE_RSA
tty_printf
(
_
(
" (%d) RSA (encrypt only)%s
\n
"
),
6
,
""
);
#endif
}
if
(
opt
.
expert
)
{
if
(
opt
.
compliance
!=
CO_DE_VS
)
tty_printf
(
_
(
" (%d) DSA (set your own capabilities)%s
\n
"
),
7
,
""
);
#if GPG_USE_RSA
tty_printf
(
_
(
" (%d) RSA (set your own capabilities)%s
\n
"
),
8
,
""
);
#endif
}
#if GPG_USE_ECDSA || GPG_USE_ECDH || GPG_USE_EDDSA
if
(
!
addmode
)
tty_printf
(
_
(
" (%d) ECC (sign and encrypt)%s
\n
"
),
9
,
_
(
" *default*"
)
);
tty_printf
(
_
(
" (%d) ECC (sign only)
\n
"
),
10
);
if
(
opt
.
expert
)
tty_printf
(
_
(
" (%d) ECC (set your own capabilities)%s
\n
"
),
11
,
""
);
if
(
addmode
)
tty_printf
(
_
(
" (%d) ECC (encrypt only)%s
\n
"
),
12
,
""
);
#endif
if
(
opt
.
expert
&&
r_keygrip
)
tty_printf
(
_
(
" (%d) Existing key%s
\n
"
),
13
,
""
);
if
(
r_keygrip
)
tty_printf
(
_
(
" (%d) Existing key from card%s
\n
"
),
14
,
""
);
for
(;;)
{
*
r_usage
=
0
;
*
r_subkey_algo
=
0
;
xfree
(
answer
);
answer
=
cpr_get
(
"keygen.algo"
,
_
(
"Your selection? "
));
cpr_kill_prompt
();
algo
=
*
answer
?
atoi
(
answer
)
:
9
;
/* Default algo is 9 */
if
(
opt
.
compliance
==
CO_DE_VS
&&
(
algo
==
2
||
algo
==
3
||
algo
==
5
||
algo
==
7
))
{
tty_printf
(
_
(
"Invalid selection.
\n
"
));
}
else
if
((
algo
==
1
||
!
strcmp
(
answer
,
"rsa+rsa"
))
&&
!
addmode
)
{
algo
=
PUBKEY_ALGO_RSA
;
*
r_subkey_algo
=
PUBKEY_ALGO_RSA
;
break
;
}
else
if
((
algo
==
2
||
!
strcmp
(
answer
,
"dsa+elg"
))
&&
!
addmode
)
{
algo
=
PUBKEY_ALGO_DSA
;
*
r_subkey_algo
=
PUBKEY_ALGO_ELGAMAL_E
;
break
;
}
else
if
(
algo
==
3
||
!
strcmp
(
answer
,
"dsa"
))
{
algo
=
PUBKEY_ALGO_DSA
;
*
r_usage
=
PUBKEY_USAGE_SIG
;
break
;
}
else
if
(
algo
==
4
||
!
strcmp
(
answer
,
"rsa/s"
))
{
algo
=
PUBKEY_ALGO_RSA
;
*
r_usage
=
PUBKEY_USAGE_SIG
;
break
;
}
else
if
((
algo
==
5
||
!
strcmp
(
answer
,
"elg"
))
&&
addmode
)
{
algo
=
PUBKEY_ALGO_ELGAMAL_E
;
*
r_usage
=
PUBKEY_USAGE_ENC
;
break
;
}
else
if
((
algo
==
6
||
!
strcmp
(
answer
,
"rsa/e"
))
&&
addmode
)
{
algo
=
PUBKEY_ALGO_RSA
;
*
r_usage
=
PUBKEY_USAGE_ENC
;
break
;
}
else
if
((
algo
==
7
||
!
strcmp
(
answer
,
"dsa/*"
))
&&
opt
.
expert
)
{
algo
=
PUBKEY_ALGO_DSA
;
*
r_usage
=
ask_key_flags
(
algo
,
addmode
,
0
);
break
;
}
else
if
((
algo
==
8
||
!
strcmp
(
answer
,
"rsa/*"
))
&&
opt
.
expert
)
{
algo
=
PUBKEY_ALGO_RSA
;
*
r_usage
=
ask_key_flags
(
algo
,
addmode
,
0
);
break
;
}
else
if
((
algo
==
9
||
!
strcmp
(
answer
,
"ecc+ecc"
))
&&
!
addmode
)
{
algo
=
PUBKEY_ALGO_ECDSA
;
*
r_subkey_algo
=
PUBKEY_ALGO_ECDH
;
break
;
}
else
if
((
algo
==
10
||
!
strcmp
(
answer
,
"ecc/s"
)))
{
algo
=
PUBKEY_ALGO_ECDSA
;
*
r_usage
=
PUBKEY_USAGE_SIG
;
break
;
}
else
if
((
algo
==
11
||
!
strcmp
(
answer
,
"ecc/*"
))
&&
opt
.
expert
)
{
algo
=
PUBKEY_ALGO_ECDSA
;
*
r_usage
=
ask_key_flags
(
algo
,
addmode
,
0
);
break
;
}
else
if
((
algo
==
12
||
!
strcmp
(
answer
,
"ecc/e"
))
&&
addmode
)
{
algo
=
PUBKEY_ALGO_ECDH
;
*
r_usage
=
PUBKEY_USAGE_ENC
;
break
;
}
else
if
((
algo
==
13
||
!
strcmp
(
answer
,
"keygrip"
))
&&
opt
.
expert
&&
r_keygrip
)
{
for
(;;)
{
xfree
(
answer
);
answer
=
tty_get
(
_
(
"Enter the keygrip: "
));
tty_kill_prompt
();
trim_spaces
(
answer
);
if
(
!*
answer
)
{
xfree
(
answer
);
answer
=
NULL
;
continue
;
}
if
(
strlen
(
answer
)
!=
40
&&
!
(
answer
[
0
]
==
'&'
&&
strlen
(
answer
+
1
)
==
40
))
tty_printf
(
_
(
"Not a valid keygrip (expecting 40 hex digits)
\n
"
));
else
if
(
!
(
algo
=
check_keygrip
(
ctrl
,
answer
))
)
tty_printf
(
_
(
"No key with this keygrip
\n
"
));
else
break
;
/* Okay. */
}
xfree
(
keygrip
);
keygrip
=
answer
;
answer
=
NULL
;
*
r_usage
=
ask_key_flags
(
algo
,
addmode
,
0
);
break
;
}
else
if
((
algo
==
14
||
!
strcmp
(
answer
,
"cardkey"
))
&&
r_keygrip
)
{
char
*
serialno
;
keypair_info_t
keypairlist
,
kpi
;
int
count
,
selection
;
err
=
agent_scd_serialno
(
&
serialno
,
NULL
);
if
(
err
)
{
tty_printf
(
_
(
"error reading the card: %s
\n
"
),
gpg_strerror
(
err
));
goto
ask_again
;
}
tty_printf
(
_
(
"Serial number of the card: %s
\n
"
),
serialno
);
xfree
(
serialno
);
err
=
agent_scd_keypairinfo
(
ctrl
,
NULL
,
&
keypairlist
);
if
(
err
)
{
tty_printf
(
_
(
"error reading the card: %s
\n
"
),
gpg_strerror
(
err
));
goto
ask_again
;
}
do
{
char
*
authkeyref
,
*
encrkeyref
,
*
signkeyref
;
agent_scd_getattr_one
(
"$AUTHKEYID"
,
&
authkeyref
);
agent_scd_getattr_one
(
"$ENCRKEYID"
,
&
encrkeyref
);
agent_scd_getattr_one
(
"$SIGNKEYID"
,
&
signkeyref
);
tty_printf
(
_
(
"Available keys:
\n
"
));
for
(
count
=
1
,
kpi
=
keypairlist
;
kpi
;
kpi
=
kpi
->
next
,
count
++
)
{
gcry_sexp_t
s_pkey
;
char
*
algostr
=
NULL
;
enum
gcry_pk_algos
algoid
=
0
;
const
char
*
keyref
=
kpi
->
idstr
;
int
any
=
0
;
if
(
keyref
&&
!
agent_scd_readkey
(
ctrl
,
keyref
,
&
s_pkey
,
NULL
))
{
algostr
=
pubkey_algo_string
(
s_pkey
,
&
algoid
);
gcry_sexp_release
(
s_pkey
);
}
/* We need to tweak the algo in case GCRY_PK_ECC is
* returned because pubkey_algo_string is not aware
* of the OpenPGP algo mapping. We need to
* distinguish between ECDH and ECDSA but we can do
* that only if we got usage flags.
* Note: Keep this in sync with parse_key_parameter_part.
*/
if
(
algoid
==
GCRY_PK_ECC
&&
algostr
)
{
if
(
!
strcmp
(
algostr
,
"ed25519"
))
kpi
->
algo
=
PUBKEY_ALGO_EDDSA
;
else
if
(
!
strcmp
(
algostr
,
"ed448"
))
kpi
->
algo
=
PUBKEY_ALGO_EDDSA
;
else
if
(
!
strcmp
(
algostr
,
"cv25519"
))
kpi
->
algo
=
PUBKEY_ALGO_ECDH
;
else
if
(
!
strcmp
(
algostr
,
"cv448"
))
kpi
->
algo
=
PUBKEY_ALGO_ECDH
;
else
if
((
kpi
->
usage
&
GCRY_PK_USAGE_ENCR
))
kpi
->
algo
=
PUBKEY_ALGO_ECDH
;
else
kpi
->
algo
=
PUBKEY_ALGO_ECDSA
;
}
else
kpi
->
algo
=
map_gcry_pk_to_openpgp
(
algoid
);
tty_printf
(
" (%d) %s %s %s"
,
count
,
kpi
->
keygrip
,
keyref
,
algostr
);
if
((
kpi
->
usage
&
GCRY_PK_USAGE_CERT
))
{
tty_printf
(
"%scert"
,
any
?
","
:
" ("
);
any
=
1
;
}
if
((
kpi
->
usage
&
GCRY_PK_USAGE_SIGN
))
{
tty_printf
(
"%ssign%s"
,
any
?
","
:
" ("
,
(
signkeyref
&&
keyref
&&
!
strcmp
(
signkeyref
,
keyref
))
?
"*"
:
""
);
any
=
1
;
}
if
((
kpi
->
usage
&
GCRY_PK_USAGE_AUTH
))
{
tty_printf
(
"%sauth%s"
,
any
?
","
:
" ("
,
(
authkeyref
&&
keyref
&&
!
strcmp
(
authkeyref
,
keyref
))
?
"*"
:
""
);
any
=
1
;
}
if
((
kpi
->
usage
&
GCRY_PK_USAGE_ENCR
))
{
tty_printf
(
"%sencr%s"
,
any
?
","
:
" ("
,
(
encrkeyref
&&
keyref
&&
!
strcmp
(
encrkeyref
,
keyref
))
?
"*"
:
""
);
any
=
1
;
}
tty_printf
(
"%s
\n
"
,
any
?
")"
:
""
);
xfree
(
algostr
);
}
xfree
(
answer
);
answer
=
cpr_get
(
"keygen.cardkey"
,
_
(
"Your selection? "
));
cpr_kill_prompt
();
trim_spaces
(
answer
);
selection
=
atoi
(
answer
);
xfree
(
authkeyref
);
xfree
(
encrkeyref
);
xfree
(
signkeyref
);
}
while
(
!
(
selection
>
0
&&
selection
<
count
));
for
(
count
=
1
,
kpi
=
keypairlist
;
kpi
;
kpi
=
kpi
->
next
,
count
++
)
if
(
count
==
selection
)
break
;
if
(
!
kpi
)
{
/* Just in case COUNT is zero (no keys). */
free_keypair_info
(
keypairlist
);
goto
ask_again
;
}
xfree
(
keygrip
);
keygrip
=
xstrdup
(
kpi
->
keygrip
);
cardkey
=
1
;
algo
=
kpi
->
algo
;
keytime
=
kpi
->
keytime
;
/* In expert mode allow to change the usage flags. */
if
(
opt
.
expert
)
*
r_usage
=
ask_key_flags_with_mask
(
algo
,
addmode
,
kpi
->
usage
,
kpi
->
usage
);
else
{
*
r_usage
=
kpi
->
usage
;
if
(
addmode
)
*
r_usage
&=
~
GCRY_PK_USAGE_CERT
;
}
free_keypair_info
(
keypairlist
);
break
;
}
else
tty_printf
(
_
(
"Invalid selection.
\n
"
));
ask_again
:
;
}
xfree
(
answer
);
if
(
r_keygrip
)
*
r_keygrip
=
keygrip
;
if
(
r_cardkey
)
*
r_cardkey
=
cardkey
;
if
(
r_keytime
)
*
r_keytime
=
keytime
;
return
algo
;
}
static
unsigned
int
get_keysize_range
(
int
algo
,
unsigned
int
*
min
,
unsigned
int
*
max
)
{
unsigned
int
def
;
unsigned
int
dummy1
,
dummy2
;
if
(
!
min
)
min
=
&
dummy1
;
if
(
!
max
)
max
=
&
dummy2
;
switch
(
algo
)
{
case
PUBKEY_ALGO_DSA
:
*
min
=
opt
.
expert
?
768
:
1024
;
*
max
=
3072
;
def
=
2048
;
break
;
case
PUBKEY_ALGO_ECDSA
:
case
PUBKEY_ALGO_ECDH
:
*
min
=
256
;
*
max
=
521
;
def
=
256
;
break
;
case
PUBKEY_ALGO_EDDSA
:
*
min
=
255
;
*
max
=
441
;
def
=
255
;
break
;
default
:
*
min
=
opt
.
compliance
==
CO_DE_VS
?
2048
:
1024
;
*
max
=
4096
;
def
=
3072
;
break
;
}
return
def
;
}
/* Return a fixed up keysize depending on ALGO. */
static
unsigned
int
fixup_keysize
(
unsigned
int
nbits
,
int
algo
,
int
silent
)
{
if
(
algo
==
PUBKEY_ALGO_DSA
&&
(
nbits
%
64
))
{
nbits
=
((
nbits
+
63
)
/
64
)
*
64
;
if
(
!
silent
)
tty_printf
(
_
(
"rounded up to %u bits
\n
"
),
nbits
);
}
else
if
(
algo
==
PUBKEY_ALGO_EDDSA
)
{
if
(
nbits
!=
255
&&
nbits
!=
441
)
{
if
(
nbits
<
256
)
nbits
=
255
;
else
nbits
=
441
;
if
(
!
silent
)
tty_printf
(
_
(
"rounded to %u bits
\n
"
),
nbits
);
}
}
else
if
(
algo
==
PUBKEY_ALGO_ECDH
||
algo
==
PUBKEY_ALGO_ECDSA
)
{
if
(
nbits
!=
256
&&
nbits
!=
384
&&
nbits
!=
521
)
{
if
(
nbits
<
256
)
nbits
=
256
;
else
if
(
nbits
<
384
)
nbits
=
384
;
else
nbits
=
521
;
if
(
!
silent
)
tty_printf
(
_
(
"rounded to %u bits
\n
"
),
nbits
);
}
}
else
if
((
nbits
%
32
))
{
nbits
=
((
nbits
+
31
)
/
32
)
*
32
;
if
(
!
silent
)
tty_printf
(
_
(
"rounded up to %u bits
\n
"
),
nbits
);
}
return
nbits
;
}
/* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE
is not 0, the function asks for the size of the encryption
subkey. */
static
unsigned
ask_keysize
(
int
algo
,
unsigned
int
primary_keysize
)
{
unsigned
int
nbits
;
unsigned
int
min
,
def
,
max
;
int
for_subkey
=
!!
primary_keysize
;
int
autocomp
=
0
;
def
=
get_keysize_range
(
algo
,
&
min
,
&
max
);
if
(
primary_keysize
&&
!
opt
.
expert
)
{
/* Deduce the subkey size from the primary key size. */
if
(
algo
==
PUBKEY_ALGO_DSA
&&
primary_keysize
>
3072
)
nbits
=
3072
;
/* For performance reasons we don't support more
than 3072 bit DSA. However we won't see this
case anyway because DSA can't be used as an
encryption subkey ;-). */
else
nbits
=
primary_keysize
;
autocomp
=
1
;
goto
leave
;
}
tty_printf
(
_
(
"%s keys may be between %u and %u bits long.
\n
"
),
openpgp_pk_algo_name
(
algo
),
min
,
max
);
for
(;;)
{
char
*
prompt
,
*
answer
;
if
(
for_subkey
)
prompt
=
xasprintf
(
_
(
"What keysize do you want "
"for the subkey? (%u) "
),
def
);
else
prompt
=
xasprintf
(
_
(
"What keysize do you want? (%u) "
),
def
);
answer
=
cpr_get
(
"keygen.size"
,
prompt
);
cpr_kill_prompt
();
nbits
=
*
answer
?
atoi
(
answer
)
:
def
;
xfree
(
prompt
);
xfree
(
answer
);
if
(
nbits
<
min
||
nbits
>
max
)
tty_printf
(
_
(
"%s keysizes must be in the range %u-%u
\n
"
),
openpgp_pk_algo_name
(
algo
),
min
,
max
);
else
break
;
}
tty_printf
(
_
(
"Requested keysize is %u bits
\n
"
),
nbits
);
leave
:
nbits
=
fixup_keysize
(
nbits
,
algo
,
autocomp
);
return
nbits
;
}
/* Ask for the curve. ALGO is the selected algorithm which this
function may adjust. Returns a const string of the name of the
curve. */
const
char
*
ask_curve
(
int
*
algo
,
int
*
subkey_algo
,
const
char
*
current
)
{
/* NB: We always use a complete algo list so that we have stable
numbers in the menu regardless on how Gpg was configured. */
struct
{
const
char
*
name
;
const
char
*
eddsa_curve
;
/* Corresponding EdDSA curve. */
const
char
*
pretty_name
;
unsigned
int
supported
:
1
;
/* Supported by gpg. */
unsigned
int
de_vs
:
1
;
/* Allowed in CO_DE_VS. */
unsigned
int
expert_only
:
1
;
/* Only with --expert */
unsigned
int
available
:
1
;
/* Available in Libycrypt (runtime checked) */
}
curves
[]
=
{
#if GPG_USE_ECDSA || GPG_USE_ECDH
# define MY_USE_ECDSADH 1
#else
# define MY_USE_ECDSADH 0
#endif
{
"Curve25519"
,
"Ed25519"
,
"Curve 25519"
,
!!
GPG_USE_EDDSA
,
0
,
0
,
0
},
{
"X448"
,
"Ed448"
,
"Curve 448"
,
!!
GPG_USE_EDDSA
,
0
,
1
,
0
},
{
"NIST P-256"
,
NULL
,
NULL
,
MY_USE_ECDSADH
,
0
,
1
,
0
},
{
"NIST P-384"
,
NULL
,
NULL
,
MY_USE_ECDSADH
,
0
,
0
,
0
},
{
"NIST P-521"
,
NULL
,
NULL
,
MY_USE_ECDSADH
,
0
,
1
,
0
},
{
"brainpoolP256r1"
,
NULL
,
"Brainpool P-256"
,
MY_USE_ECDSADH
,
1
,
0
,
0
},
{
"brainpoolP384r1"
,
NULL
,
"Brainpool P-384"
,
MY_USE_ECDSADH
,
1
,
1
,
0
},
{
"brainpoolP512r1"
,
NULL
,
"Brainpool P-512"
,
MY_USE_ECDSADH
,
1
,
1
,
0
},
{
"secp256k1"
,
NULL
,
NULL
,
MY_USE_ECDSADH
,
0
,
1
,
0
},
};
#undef MY_USE_ECDSADH
int
idx
;
char
*
answer
;
const
char
*
result
=
NULL
;
gcry_sexp_t
keyparms
;
tty_printf
(
_
(
"Please select which elliptic curve you want:
\n
"
));
keyparms
=
NULL
;
for
(
idx
=
0
;
idx
<
DIM
(
curves
);
idx
++
)
{
int
rc
;
curves
[
idx
].
available
=
0
;
if
(
!
curves
[
idx
].
supported
)
continue
;
if
(
opt
.
compliance
==
CO_DE_VS
)
{
if
(
!
curves
[
idx
].
de_vs
)
continue
;
/* Not allowed. */
}
else
if
(
!
opt
.
expert
&&
curves
[
idx
].
expert_only
)
continue
;
/* We need to switch from the ECDH name of the curve to the
EDDSA name of the curve if we want a signing key. */
gcry_sexp_release
(
keyparms
);
rc
=
gcry_sexp_build
(
&
keyparms
,
NULL
,
"(public-key(ecc(curve %s)))"
,
curves
[
idx
].
eddsa_curve
?
curves
[
idx
].
eddsa_curve
/**/
:
curves
[
idx
].
name
);
if
(
rc
)
continue
;
if
(
!
gcry_pk_get_curve
(
keyparms
,
0
,
NULL
))
continue
;
if
(
subkey_algo
&&
curves
[
idx
].
eddsa_curve
)
{
/* Both Curve 25519 (or 448) keys are to be created. Check that
Libgcrypt also supports the real Curve25519 (or 448). */
gcry_sexp_release
(
keyparms
);
rc
=
gcry_sexp_build
(
&
keyparms
,
NULL
,
"(public-key(ecc(curve %s)))"
,
curves
[
idx
].
name
);
if
(
rc
)
continue
;
if
(
!
gcry_pk_get_curve
(
keyparms
,
0
,
NULL
))
continue
;
}
curves
[
idx
].
available
=
1
;
tty_printf
(
" (%d) %s%s
\n
"
,
idx
+
1
,
curves
[
idx
].
pretty_name
?
curves
[
idx
].
pretty_name
:
curves
[
idx
].
name
,
idx
==
0
?
_
(
" *default*"
)
:
""
);
}
gcry_sexp_release
(
keyparms
);
for
(;;)
{
answer
=
cpr_get
(
"keygen.curve"
,
_
(
"Your selection? "
));
cpr_kill_prompt
();
idx
=
*
answer
?
atoi
(
answer
)
:
1
;
if
(
!*
answer
&&
current
)
{
xfree
(
answer
);
return
NULL
;
}
else
if
(
*
answer
&&
!
idx
)
{
/* See whether the user entered the name of the curve. */
for
(
idx
=
0
;
idx
<
DIM
(
curves
);
idx
++
)
{
if
(
!
opt
.
expert
&&
curves
[
idx
].
expert_only
)
continue
;
if
(
!
stricmp
(
curves
[
idx
].
name
,
answer
)
||
(
curves
[
idx
].
pretty_name
&&
!
stricmp
(
curves
[
idx
].
pretty_name
,
answer
)))
break
;
}
if
(
idx
==
DIM
(
curves
))
idx
=
-1
;
}
else
idx
--
;
xfree
(
answer
);
answer
=
NULL
;
if
(
idx
<
0
||
idx
>=
DIM
(
curves
)
||
!
curves
[
idx
].
available
)
tty_printf
(
_
(
"Invalid selection.
\n
"
));
else
{
/* If the user selected a signing algorithm and Curve25519
we need to set the algo to EdDSA and update the curve name.
If switching away from EdDSA, we need to set the algo back
to ECDSA. */
if
(
*
algo
==
PUBKEY_ALGO_ECDSA
||
*
algo
==
PUBKEY_ALGO_EDDSA
)
{
if
(
curves
[
idx
].
eddsa_curve
)
{
if
(
subkey_algo
&&
*
subkey_algo
==
PUBKEY_ALGO_ECDSA
)
*
subkey_algo
=
PUBKEY_ALGO_EDDSA
;
*
algo
=
PUBKEY_ALGO_EDDSA
;
result
=
curves
[
idx
].
eddsa_curve
;
}
else
{
if
(
subkey_algo
&&
*
subkey_algo
==
PUBKEY_ALGO_EDDSA
)
*
subkey_algo
=
PUBKEY_ALGO_ECDSA
;
*
algo
=
PUBKEY_ALGO_ECDSA
;
result
=
curves
[
idx
].
name
;
}
}
else
result
=
curves
[
idx
].
name
;
break
;
}
}
if
(
!
result
)
result
=
curves
[
0
].
name
;
return
result
;
}
/****************
* Parse an expire string and return its value in seconds.
* Returns (u32)-1 on error.
* This isn't perfect since scan_isodatestr returns unix time, and
* OpenPGP actually allows a 32-bit time *plus* a 32-bit offset.
* Because of this, we only permit setting expirations up to 2106, but
* OpenPGP could theoretically allow up to 2242. I think we'll all
* just cope for the next few years until we get a 64-bit time_t or
* similar.
*/
u32
parse_expire_string
(
const
char
*
string
)
{
int
mult
;
u32
seconds
;
u32
abs_date
=
0
;
u32
curtime
=
make_timestamp
();
time_t
tt
;
if
(
!
string
||
!*
string
||
!
strcmp
(
string
,
"none"
)
||
!
strcmp
(
string
,
"never"
)
||
!
strcmp
(
string
,
"-"
))
seconds
=
0
;
else
if
(
!
strncmp
(
string
,
"seconds="
,
8
))
seconds
=
atoi
(
string
+
8
);
else
if
((
abs_date
=
scan_isodatestr
(
string
))
&&
(
abs_date
+
86400
/
2
)
>
curtime
)
seconds
=
(
abs_date
+
86400
/
2
)
-
curtime
;
else
if
((
tt
=
isotime2epoch
(
string
))
!=
(
time_t
)(
-1
))
seconds
=
(
u32
)
tt
-
curtime
;
else
if
((
mult
=
check_valid_days
(
string
)))
seconds
=
atoi
(
string
)
*
86400L
*
mult
;
else
seconds
=
(
u32
)(
-1
);
return
seconds
;
}
/* Parse a Creation-Date string which is either "1986-04-26" or
"19860426T042640". Returns 0 on error. */
static
u32
parse_creation_string
(
const
char
*
string
)
{
u32
seconds
;
if
(
!*
string
)
seconds
=
0
;
else
if
(
!
strncmp
(
string
,
"seconds="
,
8
)
)
seconds
=
atoi
(
string
+
8
);
else
if
(
!
(
seconds
=
scan_isodatestr
(
string
)))
{
time_t
tmp
=
isotime2epoch
(
string
);
seconds
=
(
tmp
==
(
time_t
)(
-1
))
?
0
:
tmp
;
}
return
seconds
;
}
/* object == 0 for a key, and 1 for a sig */
u32
ask_expire_interval
(
int
object
,
const
char
*
def_expire
)
{
u32
interval
;
char
*
answer
;
switch
(
object
)
{
case
0
:
if
(
def_expire
)
BUG
();
tty_printf
(
_
(
"Please specify how long the key should be valid.
\n
"
" 0 = key does not expire
\n
"
" <n> = key expires in n days
\n
"
" <n>w = key expires in n weeks
\n
"
" <n>m = key expires in n months
\n
"
" <n>y = key expires in n years
\n
"
));
break
;
case
1
:
if
(
!
def_expire
)
BUG
();
tty_printf
(
_
(
"Please specify how long the signature should be valid.
\n
"
" 0 = signature does not expire
\n
"
" <n> = signature expires in n days
\n
"
" <n>w = signature expires in n weeks
\n
"
" <n>m = signature expires in n months
\n
"
" <n>y = signature expires in n years
\n
"
));
break
;
default
:
BUG
();
}
/* Note: The elgamal subkey for DSA has no expiration date because
* it must be signed with the DSA key and this one has the expiration
* date */
answer
=
NULL
;
for
(;;)
{
u32
curtime
;
xfree
(
answer
);
if
(
object
==
0
)
answer
=
cpr_get
(
"keygen.valid"
,
_
(
"Key is valid for? (0) "
));
else
{
char
*
prompt
;
prompt
=
xasprintf
(
_
(
"Signature is valid for? (%s) "
),
def_expire
);
answer
=
cpr_get
(
"siggen.valid"
,
prompt
);
xfree
(
prompt
);
if
(
*
answer
==
'\0'
)
answer
=
xstrdup
(
def_expire
);
}
cpr_kill_prompt
();
trim_spaces
(
answer
);
curtime
=
make_timestamp
();
interval
=
parse_expire_string
(
answer
);
if
(
interval
==
(
u32
)
-1
)
{
tty_printf
(
_
(
"invalid value
\n
"
));
continue
;
}
if
(
!
interval
)
{
tty_printf
((
object
==
0
)
?
_
(
"Key does not expire at all
\n
"
)
:
_
(
"Signature does not expire at all
\n
"
));
}
else
{
tty_printf
(
object
==
0
?
_
(
"Key expires at %s
\n
"
)
:
_
(
"Signature expires at %s
\n
"
),
asctimestamp
((
ulong
)(
curtime
+
interval
)
)
);
#if SIZEOF_TIME_T <= 4 && !defined (HAVE_UNSIGNED_TIME_T)
if
(
(
time_t
)((
ulong
)(
curtime
+
interval
))
<
0
)
tty_printf
(
_
(
"Your system can't display dates beyond 2038.
\n
"
"However, it will be correctly handled up to"
" 2106.
\n
"
));
else
#endif
/*SIZEOF_TIME_T*/
if
(
(
time_t
)((
unsigned
long
)(
curtime
+
interval
))
<
curtime
)
{
tty_printf
(
_
(
"invalid value
\n
"
));
continue
;
}
}
if
(
cpr_enabled
()
||
cpr_get_answer_is_yes
(
"keygen.valid.okay"
,
_
(
"Is this correct? (y/N) "
))
)
break
;
}
xfree
(
answer
);
return
interval
;
}
u32
ask_expiredate
()
{
u32
x
=
ask_expire_interval
(
0
,
NULL
);
return
x
?
make_timestamp
()
+
x
:
0
;
}
static
PKT_user_id
*
uid_from_string
(
const
char
*
string
)
{
size_t
n
;
PKT_user_id
*
uid
;
n
=
strlen
(
string
);
uid
=
xmalloc_clear
(
sizeof
*
uid
+
n
);
uid
->
len
=
n
;
strcpy
(
uid
->
name
,
string
);
uid
->
ref
=
1
;
return
uid
;
}
/* Return true if the user id UID already exists in the keyblock. */
static
int
uid_already_in_keyblock
(
kbnode_t
keyblock
,
const
char
*
uid
)
{
PKT_user_id
*
uidpkt
=
uid_from_string
(
uid
);
kbnode_t
node
;
int
result
=
0
;
for
(
node
=
keyblock
;
node
&&
!
result
;
node
=
node
->
next
)
if
(
!
is_deleted_kbnode
(
node
)
&&
node
->
pkt
->
pkttype
==
PKT_USER_ID
&&
!
cmp_user_ids
(
uidpkt
,
node
->
pkt
->
pkt
.
user_id
))
result
=
1
;
free_user_id
(
uidpkt
);
return
result
;
}
/* Ask for a user ID. With a MODE of 1 an extra help prompt is
printed for use during a new key creation. If KEYBLOCK is not NULL
the function prevents the creation of an already existing user
ID. IF FULL is not set some prompts are not shown. */
static
char
*
ask_user_id
(
int
mode
,
int
full
,
KBNODE
keyblock
)
{
char
*
answer
;
char
*
aname
,
*
acomment
,
*
amail
,
*
uid
;
if
(
!
mode
)
{
/* TRANSLATORS: This is the new string telling the user what
gpg is now going to do (i.e. ask for the parts of the user
ID). Note that if you do not translate this string, a
different string will be used, which might still have
a correct translation. */
const
char
*
s1
=
N_
(
"
\n
"
"GnuPG needs to construct a user ID to identify your key.
\n
"
"
\n
"
);
const
char
*
s2
=
_
(
s1
);
if
(
!
strcmp
(
s1
,
s2
))
{
/* There is no translation for the string thus we to use
the old info text. gettext has no way to tell whether
a translation is actually available, thus we need to
to compare again. */
/* TRANSLATORS: This string is in general not anymore used
but you should keep your existing translation. In case
the new string is not translated this old string will
be used. */
const
char
*
s3
=
N_
(
"
\n
"
"You need a user ID to identify your key; "
"the software constructs the user ID
\n
"
"from the Real Name, Comment and Email Address in this form:
\n
"
"
\"
Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>
\"\n\n
"
);
const
char
*
s4
=
_
(
s3
);
if
(
strcmp
(
s3
,
s4
))
s2
=
s3
;
/* A translation exists - use it. */
}
tty_printf
(
"%s"
,
s2
)
;
}
uid
=
aname
=
acomment
=
amail
=
NULL
;
for
(;;)
{
char
*
p
;
int
fail
=
0
;
if
(
!
aname
)
{
for
(;;)
{
xfree
(
aname
);
aname
=
cpr_get
(
"keygen.name"
,
_
(
"Real name: "
));
trim_spaces
(
aname
);
cpr_kill_prompt
();
if
(
opt
.
allow_freeform_uid
)
break
;
if
(
strpbrk
(
aname
,
"<>"
)
)
{
tty_printf
(
_
(
"Invalid character in name
\n
"
));
tty_printf
(
_
(
"The characters '%s' and '%s' may not "
"appear in name
\n
"
),
"<"
,
">"
);
}
else
if
(
digitp
(
aname
)
)
tty_printf
(
_
(
"Name may not start with a digit
\n
"
));
else
if
(
*
aname
&&
strlen
(
aname
)
<
5
)
{
tty_printf
(
_
(
"Name must be at least 5 characters long
\n
"
));
/* However, we allow an empty name. */
}
else
break
;
}
}
if
(
!
amail
)
{
for
(;;)
{
xfree
(
amail
);
amail
=
cpr_get
(
"keygen.email"
,
_
(
"Email address: "
));
trim_spaces
(
amail
);
cpr_kill_prompt
();
if
(
!*
amail
||
opt
.
allow_freeform_uid
)
break
;
/* no email address is okay */
else
if
(
!
is_valid_mailbox
(
amail
)
)
tty_printf
(
_
(
"Not a valid email address
\n
"
));
else
break
;
}
}
if
(
!
acomment
)
{
if
(
full
)
{
for
(;;)
{
xfree
(
acomment
);
acomment
=
cpr_get
(
"keygen.comment"
,
_
(
"Comment: "
));
trim_spaces
(
acomment
);
cpr_kill_prompt
();
if
(
!*
acomment
)
break
;
/* no comment is okay */
else
if
(
strpbrk
(
acomment
,
"()" ) )
tty_printf
(
_
(
"Invalid character in comment
\n
"
));
else
break
;
}
}
else
{
xfree
(
acomment
);
acomment
=
xstrdup
(
""
);
}
}
xfree
(
uid
);
uid
=
p
=
xmalloc
(
strlen
(
aname
)
+
strlen
(
amail
)
+
strlen
(
acomment
)
+
12
+
10
);
if
(
!*
aname
&&
*
amail
&&
!*
acomment
&&
!
random_is_faked
())
{
/* Empty name and comment but with mail address. Use
simplified form with only the non-angle-bracketed mail
address. */
p
=
stpcpy
(
p
,
amail
);
}
else
{
p
=
stpcpy
(
p
,
aname
);
if
(
*
acomment
)
p
=
stpcpy
(
stpcpy
(
stpcpy
(
p
,
" ("
),
acomment
),
")"
);
if
(
*
amail
)
p
=
stpcpy
(
stpcpy
(
stpcpy
(
p
,
" <"
),
amail
),
">"
);
}
/* Append a warning if the RNG is switched into fake mode. */
if
(
random_is_faked
()
)
strcpy
(
p
,
" (insecure!)"
);
/* print a note in case that UTF8 mapping has to be done */
for
(
p
=
uid
;
*
p
;
p
++
)
{
if
(
*
p
&
0x80
)
{
tty_printf
(
_
(
"You are using the '%s' character set.
\n
"
),
get_native_charset
()
);
break
;
}
}
tty_printf
(
_
(
"You selected this USER-ID:
\n
\"
%s
\"\n\n
"
),
uid
);
if
(
!*
amail
&&
!
opt
.
allow_freeform_uid
&&
(
strchr
(
aname
,
'@'
)
||
strchr
(
acomment
,
'@'
)))
{
fail
=
1
;
tty_printf
(
_
(
"Please don't put the email address "
"into the real name or the comment
\n
"
)
);
}
if
(
!
fail
&&
keyblock
)
{
if
(
uid_already_in_keyblock
(
keyblock
,
uid
))
{
tty_printf
(
_
(
"Such a user ID already exists on this key!
\n
"
));
fail
=
1
;
}
}
for
(;;)
{
/* TRANSLATORS: These are the allowed answers in
lower and uppercase. Below you will find the matching
string which should be translated accordingly and the
letter changed to match the one in the answer string.
n = Change name
c = Change comment
e = Change email
o = Okay (ready, continue)
q = Quit
*/
const
char
*
ansstr
=
_
(
"NnCcEeOoQq"
);
if
(
strlen
(
ansstr
)
!=
10
)
BUG
();
if
(
cpr_enabled
()
)
{
answer
=
xstrdup
(
ansstr
+
(
fail
?
8
:
6
));
answer
[
1
]
=
0
;
}
else
if
(
full
)
{
answer
=
cpr_get
(
"keygen.userid.cmd"
,
fail
?
_
(
"Change (N)ame, (C)omment, (E)mail or (Q)uit? "
)
:
_
(
"Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "
));
cpr_kill_prompt
();
}
else
{
answer
=
cpr_get
(
"keygen.userid.cmd"
,
fail
?
_
(
"Change (N)ame, (E)mail, or (Q)uit? "
)
:
_
(
"Change (N)ame, (E)mail, or (O)kay/(Q)uit? "
));
cpr_kill_prompt
();
}
if
(
strlen
(
answer
)
>
1
)
;
else
if
(
*
answer
==
ansstr
[
0
]
||
*
answer
==
ansstr
[
1
]
)
{
xfree
(
aname
);
aname
=
NULL
;
break
;
}
else
if
(
*
answer
==
ansstr
[
2
]
||
*
answer
==
ansstr
[
3
]
)
{
xfree
(
acomment
);
acomment
=
NULL
;
break
;
}
else
if
(
*
answer
==
ansstr
[
4
]
||
*
answer
==
ansstr
[
5
]
)
{
xfree
(
amail
);
amail
=
NULL
;
break
;
}
else
if
(
*
answer
==
ansstr
[
6
]
||
*
answer
==
ansstr
[
7
]
)
{
if
(
fail
)
{
tty_printf
(
_
(
"Please correct the error first
\n
"
));
}
else
{
xfree
(
aname
);
aname
=
NULL
;
xfree
(
acomment
);
acomment
=
NULL
;
xfree
(
amail
);
amail
=
NULL
;
break
;
}
}
else
if
(
*
answer
==
ansstr
[
8
]
||
*
answer
==
ansstr
[
9
]
)
{
xfree
(
aname
);
aname
=
NULL
;
xfree
(
acomment
);
acomment
=
NULL
;
xfree
(
amail
);
amail
=
NULL
;
xfree
(
uid
);
uid
=
NULL
;
break
;
}
xfree
(
answer
);
}
xfree
(
answer
);
if
(
!
amail
&&
!
acomment
)
break
;
xfree
(
uid
);
uid
=
NULL
;
}
if
(
uid
)
{
char
*
p
=
native_to_utf8
(
uid
);
xfree
(
uid
);
uid
=
p
;
}
return
uid
;
}
/* Basic key generation. Here we divert to the actual generation
routines based on the requested algorithm. */
static
int
do_create
(
int
algo
,
unsigned
int
nbits
,
const
char
*
curve
,
kbnode_t
pub_root
,
u32
timestamp
,
u32
expiredate
,
int
is_subkey
,
int
keygen_flags
,
const
char
*
passphrase
,
char
**
cache_nonce_addr
,
char
**
passwd_nonce_addr
)
{
gpg_error_t
err
;
/* Fixme: The entropy collecting message should be moved to a
libgcrypt progress handler. */
if
(
!
opt
.
batch
)
tty_printf
(
_
(
"We need to generate a lot of random bytes. It is a good idea to perform
\n
"
"some other action (type on the keyboard, move the mouse, utilize the
\n
"
"disks) during the prime generation; this gives the random number
\n
"
"generator a better chance to gain enough entropy.
\n
"
)
);
if
(
algo
==
PUBKEY_ALGO_ELGAMAL_E
)
err
=
gen_elg
(
algo
,
nbits
,
pub_root
,
timestamp
,
expiredate
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
else
if
(
algo
==
PUBKEY_ALGO_DSA
)
err
=
gen_dsa
(
nbits
,
pub_root
,
timestamp
,
expiredate
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
else
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
err
=
gen_ecc
(
algo
,
curve
,
pub_root
,
timestamp
,
expiredate
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
else
if
(
algo
==
PUBKEY_ALGO_RSA
)
err
=
gen_rsa
(
algo
,
nbits
,
pub_root
,
timestamp
,
expiredate
,
is_subkey
,
keygen_flags
,
passphrase
,
cache_nonce_addr
,
passwd_nonce_addr
);
else
BUG
();
return
err
;
}
/* Generate a new user id packet or return NULL if canceled. If
KEYBLOCK is not NULL the function prevents the creation of an
already existing user ID. If UIDSTR is not NULL the user is not
asked but UIDSTR is used to create the user id packet; if the user
id already exists NULL is returned. UIDSTR is expected to be utf-8
encoded and should have already been checked for a valid length
etc. */
PKT_user_id
*
generate_user_id
(
KBNODE
keyblock
,
const
char
*
uidstr
)
{
PKT_user_id
*
uid
;
char
*
p
;
if
(
uidstr
)
{
if
(
uid_already_in_keyblock
(
keyblock
,
uidstr
))
return
NULL
;
/* Already exists. */
uid
=
uid_from_string
(
uidstr
);
}
else
{
p
=
ask_user_id
(
1
,
1
,
keyblock
);
if
(
!
p
)
return
NULL
;
/* Canceled. */
uid
=
uid_from_string
(
p
);
xfree
(
p
);
}
return
uid
;
}
/* Helper for parse_key_parameter_part_parameter_string for one part of the
* specification string; i.e. ALGO/FLAGS. If STRING is NULL or empty
* success is returned. On error an error code is returned. Note
* that STRING may be modified by this function. NULL may be passed
* for any parameter. FOR_SUBKEY shall be true if this is used as a
* subkey. If CLEAR_CERT is set a default CERT usage will be cleared;
* this is useful if for example the default algorithm is used for a
* subkey. If R_KEYVERSION is not NULL it will receive the version of
* the key; this is currently 4 but can be changed with the flag "v5"
* to create a v5 key. If R_KEYTIME is not NULL and the key has been
* taken from active OpenPGP card, its creation time is stored
* there. */
static
gpg_error_t
parse_key_parameter_part
(
ctrl_t
ctrl
,
char
*
string
,
int
for_subkey
,
int
clear_cert
,
int
*
r_algo
,
unsigned
int
*
r_size
,
unsigned
int
*
r_keyuse
,
char
const
**
r_curve
,
int
*
r_keyversion
,
char
**
r_keygrip
,
u32
*
r_keytime
)
{
gpg_error_t
err
;
char
*
flags
;
int
algo
;
char
*
endp
;
const
char
*
curve
=
NULL
;
int
ecdh_or_ecdsa
=
0
;
unsigned
int
size
;
int
keyuse
;
int
keyversion
=
4
;
int
i
;
const
char
*
s
;
int
from_card
=
0
;
char
*
keygrip
=
NULL
;
u32
keytime
=
0
;
if
(
!
string
||
!*
string
)
return
0
;
/* Success. */
flags
=
strchr
(
string
,
'/'
);
if
(
flags
)
*
flags
++
=
0
;
algo
=
0
;
if
(
!
ascii_strcasecmp
(
string
,
"card"
))
from_card
=
1
;
else
if
(
strlen
(
string
)
>=
3
&&
(
digitp
(
string
+
3
)
||
!
string
[
3
]))
{
if
(
!
ascii_memcasecmp
(
string
,
"rsa"
,
3
))
algo
=
PUBKEY_ALGO_RSA
;
else
if
(
!
ascii_memcasecmp
(
string
,
"dsa"
,
3
))
algo
=
PUBKEY_ALGO_DSA
;
else
if
(
!
ascii_memcasecmp
(
string
,
"elg"
,
3
))
algo
=
PUBKEY_ALGO_ELGAMAL_E
;
}
if
(
from_card
)
;
/* We need the flags before we can figure out the key to use. */
else
if
(
algo
)
{
if
(
!
string
[
3
])
size
=
get_keysize_range
(
algo
,
NULL
,
NULL
);
else
{
size
=
strtoul
(
string
+
3
,
&
endp
,
10
);
if
(
size
<
512
||
size
>
16384
||
*
endp
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
}
else
if
((
curve
=
openpgp_is_curve_supported
(
string
,
&
algo
,
&
size
)))
{
if
(
!
algo
)
{
algo
=
PUBKEY_ALGO_ECDH
;
/* Default ECC algorithm. */
ecdh_or_ecdsa
=
1
;
/* We may need to switch the algo. */
}
}
else
return
gpg_error
(
GPG_ERR_UNKNOWN_CURVE
);
/* Parse the flags. */
keyuse
=
0
;
if
(
flags
)
{
char
**
tokens
=
NULL
;
tokens
=
strtokenize
(
flags
,
","
);
if
(
!
tokens
)
return
gpg_error_from_syserror
();
for
(
i
=
0
;
(
s
=
tokens
[
i
]);
i
++
)
{
if
(
!*
s
)
;
else
if
(
!
ascii_strcasecmp
(
s
,
"sign"
))
keyuse
|=
PUBKEY_USAGE_SIG
;
else
if
(
!
ascii_strcasecmp
(
s
,
"encrypt"
)
||
!
ascii_strcasecmp
(
s
,
"encr"
))
keyuse
|=
PUBKEY_USAGE_ENC
;
else
if
(
!
ascii_strcasecmp
(
s
,
"auth"
))
keyuse
|=
PUBKEY_USAGE_AUTH
;
else
if
(
!
ascii_strcasecmp
(
s
,
"cert"
))
keyuse
|=
PUBKEY_USAGE_CERT
;
else
if
(
!
ascii_strcasecmp
(
s
,
"ecdsa"
)
&&
!
from_card
)
{
if
(
algo
==
PUBKEY_ALGO_ECDH
||
algo
==
PUBKEY_ALGO_ECDSA
)
algo
=
PUBKEY_ALGO_ECDSA
;
else
{
xfree
(
tokens
);
return
gpg_error
(
GPG_ERR_INV_FLAG
);
}
ecdh_or_ecdsa
=
0
;
}
else
if
(
!
ascii_strcasecmp
(
s
,
"ecdh"
)
&&
!
from_card
)
{
if
(
algo
==
PUBKEY_ALGO_ECDH
||
algo
==
PUBKEY_ALGO_ECDSA
)
algo
=
PUBKEY_ALGO_ECDH
;
else
{
xfree
(
tokens
);
return
gpg_error
(
GPG_ERR_INV_FLAG
);
}
ecdh_or_ecdsa
=
0
;
}
else
if
(
!
ascii_strcasecmp
(
s
,
"eddsa"
)
&&
!
from_card
)
{
/* Not required but we allow it for consistency. */
if
(
algo
==
PUBKEY_ALGO_EDDSA
)
;
else
{
xfree
(
tokens
);
return
gpg_error
(
GPG_ERR_INV_FLAG
);
}
}
else
if
(
!
ascii_strcasecmp
(
s
,
"v5"
))
{
if
(
opt
.
flags
.
rfc4880bis
)
keyversion
=
5
;
}
else
if
(
!
ascii_strcasecmp
(
s
,
"v4"
))
keyversion
=
4
;
else
{
xfree
(
tokens
);
return
gpg_error
(
GPG_ERR_UNKNOWN_FLAG
);
}
}
xfree
(
tokens
);
}
/* If not yet decided switch between ecdh and ecdsa unless we want
* to read the algo from the current card. */
if
(
from_card
)
{
keypair_info_t
keypairlist
,
kpi
;
char
*
reqkeyref
;
if
(
!
keyuse
)
keyuse
=
(
for_subkey
?
PUBKEY_USAGE_ENC
/* */
:
(
PUBKEY_USAGE_CERT
|
PUBKEY_USAGE_SIG
));
/* Access the card to make sure we have one and to show the S/N. */
{
char
*
serialno
;
err
=
agent_scd_serialno
(
&
serialno
,
NULL
);
if
(
err
)
{
log_error
(
_
(
"error reading the card: %s
\n
"
),
gpg_strerror
(
err
));
return
err
;
}
if
(
!
opt
.
quiet
)
log_info
(
_
(
"Serial number of the card: %s
\n
"
),
serialno
);
xfree
(
serialno
);
}
err
=
agent_scd_keypairinfo
(
ctrl
,
NULL
,
&
keypairlist
);
if
(
err
)
{
log_error
(
_
(
"error reading the card: %s
\n
"
),
gpg_strerror
(
err
));
return
err
;
}
agent_scd_getattr_one
((
keyuse
&
(
PUBKEY_USAGE_SIG
|
PUBKEY_USAGE_CERT
))
?
"$SIGNKEYID"
:
"$ENCRKEYID"
,
&
reqkeyref
);
algo
=
0
;
/* Should already be the case. */
for
(
kpi
=
keypairlist
;
kpi
&&
!
algo
;
kpi
=
kpi
->
next
)
{
gcry_sexp_t
s_pkey
;
char
*
algostr
=
NULL
;
enum
gcry_pk_algos
algoid
=
0
;
const
char
*
keyref
=
kpi
->
idstr
;
if
(
!
reqkeyref
)
continue
;
/* Card does not provide the info (skip all). */
if
(
!
keyref
)
continue
;
/* Ooops. */
if
(
strcmp
(
reqkeyref
,
keyref
))
continue
;
/* This is not the requested keyref. */
if
((
keyuse
&
(
PUBKEY_USAGE_SIG
|
PUBKEY_USAGE_CERT
))
&&
(
kpi
->
usage
&
(
GCRY_PK_USAGE_SIGN
|
GCRY_PK_USAGE_CERT
)))
;
/* Okay */
else
if
((
keyuse
&
PUBKEY_USAGE_ENC
)
&&
(
kpi
->
usage
&
GCRY_PK_USAGE_ENCR
))
;
/* Okay */
else
continue
;
/* Not usable for us. */
if
(
agent_scd_readkey
(
ctrl
,
keyref
,
&
s_pkey
,
NULL
))
continue
;
/* Could not read the key. */
algostr
=
pubkey_algo_string
(
s_pkey
,
&
algoid
);
gcry_sexp_release
(
s_pkey
);
/* Map to OpenPGP algo number.
* We need to tweak the algo in case GCRY_PK_ECC is
* returned because pubkey_algo_string is not aware
* of the OpenPGP algo mapping. We need to
* distinguish between ECDH and ECDSA but we can do
* that only if we got usage flags.
* Note: Keep this in sync with ask_algo. */
if
(
algoid
==
GCRY_PK_ECC
&&
algostr
)
{
if
(
!
strcmp
(
algostr
,
"ed25519"
))
algo
=
PUBKEY_ALGO_EDDSA
;
else
if
(
!
strcmp
(
algostr
,
"ed448"
))
algo
=
PUBKEY_ALGO_EDDSA
;
else
if
(
!
strcmp
(
algostr
,
"cv25519"
))
algo
=
PUBKEY_ALGO_ECDH
;
else
if
(
!
strcmp
(
algostr
,
"cv448"
))
algo
=
PUBKEY_ALGO_ECDH
;
else
if
((
kpi
->
usage
&
GCRY_PK_USAGE_ENCR
))
algo
=
PUBKEY_ALGO_ECDH
;
else
algo
=
PUBKEY_ALGO_ECDSA
;
}
else
algo
=
map_gcry_pk_to_openpgp
(
algoid
);
xfree
(
algostr
);
xfree
(
keygrip
);
keygrip
=
xtrystrdup
(
kpi
->
keygrip
);
if
(
!
keygrip
)
{
err
=
gpg_error_from_syserror
();
xfree
(
reqkeyref
);
free_keypair_info
(
keypairlist
);
return
err
;
}
keytime
=
kpi
->
keytime
;
}
xfree
(
reqkeyref
);
free_keypair_info
(
keypairlist
);
if
(
!
algo
||
!
keygrip
)
{
err
=
gpg_error
(
GPG_ERR_PUBKEY_ALGO
);
log_error
(
"no usable key on the card: %s
\n
"
,
gpg_strerror
(
err
));
xfree
(
keygrip
);
return
err
;
}
}
else
if
(
ecdh_or_ecdsa
&&
keyuse
)
algo
=
(
keyuse
&
PUBKEY_USAGE_ENC
)
?
PUBKEY_ALGO_ECDH
:
PUBKEY_ALGO_ECDSA
;
else
if
(
ecdh_or_ecdsa
)
algo
=
for_subkey
?
PUBKEY_ALGO_ECDH
:
PUBKEY_ALGO_ECDSA
;
/* Set or fix key usage. */
if
(
!
keyuse
)
{
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_DSA
)
keyuse
=
PUBKEY_USAGE_SIG
;
else
if
(
algo
==
PUBKEY_ALGO_RSA
)
keyuse
=
for_subkey
?
PUBKEY_USAGE_ENC
:
PUBKEY_USAGE_SIG
;
else
keyuse
=
PUBKEY_USAGE_ENC
;
}
else
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_DSA
)
{
keyuse
&=
~
PUBKEY_USAGE_ENC
;
/* Forbid encryption. */
}
else
if
(
algo
==
PUBKEY_ALGO_ECDH
||
algo
==
PUBKEY_ALGO_ELGAMAL_E
)
{
keyuse
=
PUBKEY_USAGE_ENC
;
/* Allow only encryption. */
}
/* Make sure a primary key can certify. */
if
(
!
for_subkey
)
keyuse
|=
PUBKEY_USAGE_CERT
;
/* But if requested remove th cert usage. */
if
(
clear_cert
)
keyuse
&=
~
PUBKEY_USAGE_CERT
;
/* Check that usage is actually possible. */
if
(
/**/
((
keyuse
&
(
PUBKEY_USAGE_SIG
|
PUBKEY_USAGE_AUTH
|
PUBKEY_USAGE_CERT
))
&&
!
pubkey_get_nsig
(
algo
))
||
((
keyuse
&
PUBKEY_USAGE_ENC
)
&&
!
pubkey_get_nenc
(
algo
))
||
(
for_subkey
&&
(
keyuse
&
PUBKEY_USAGE_CERT
)))
{
xfree
(
keygrip
);
return
gpg_error
(
GPG_ERR_WRONG_KEY_USAGE
);
}
/* Return values. */
if
(
r_algo
)
*
r_algo
=
algo
;
if
(
r_size
)
{
unsigned
int
min
,
def
,
max
;
/* Make sure the keysize is in the allowed range. */
def
=
get_keysize_range
(
algo
,
&
min
,
&
max
);
if
(
!
size
)
size
=
def
;
else
if
(
size
<
min
)
size
=
min
;
else
if
(
size
>
max
)
size
=
max
;
*
r_size
=
fixup_keysize
(
size
,
algo
,
1
);
}
if
(
r_keyuse
)
*
r_keyuse
=
keyuse
;
if
(
r_curve
)
*
r_curve
=
curve
;
if
(
r_keyversion
)
*
r_keyversion
=
keyversion
;
if
(
r_keygrip
)
*
r_keygrip
=
keygrip
;
else
xfree
(
keygrip
);
if
(
r_keytime
)
*
r_keytime
=
keytime
;
return
0
;
}
/* Parse and return the standard key generation parameter.
* The string is expected to be in this format:
*
* ALGO[/FLAGS][+SUBALGO[/FLAGS]]
*
* Here ALGO is a string in the same format as printed by the
* keylisting. For example:
*
* rsa3072 := RSA with 3072 bit.
* dsa2048 := DSA with 2048 bit.
* elg2048 := Elgamal with 2048 bit.
* ed25519 := EDDSA using curve Ed25519.
* ed448 := EDDSA using curve Ed448.
* cv25519 := ECDH using curve Curve25519.
* cv448 := ECDH using curve X448.
* nistp256:= ECDSA or ECDH using curve NIST P-256
*
* All strings with an unknown prefix are considered an elliptic
* curve. Curves which have no implicit algorithm require that FLAGS
* is given to select whether ECDSA or ECDH is used; this can either
* be done using an algorithm keyword or usage keywords.
*
* FLAGS is a comma delimited string of keywords:
*
* cert := Allow usage Certify
* sign := Allow usage Sign
* encr := Allow usage Encrypt
* auth := Allow usage Authentication
* encrypt := Alias for "encr"
* ecdsa := Use algorithm ECDSA.
* eddsa := Use algorithm EdDSA.
* ecdh := Use algorithm ECDH.
* v5 := Create version 5 key (requires option --rfc4880bis)
*
* There are several defaults and fallbacks depending on the
* algorithm. PART can be used to select which part of STRING is
* used:
* -1 := Both parts
* 0 := Only the part of the primary key
* 1 := If there is one part parse that one, if there are
* two parts parse the part which best matches the
* SUGGESTED_USE or in case that can't be evaluated the second part.
* Always return using the args for the primary key (R_ALGO,....).
*
*/
gpg_error_t
parse_key_parameter_string
(
ctrl_t
ctrl
,
const
char
*
string
,
int
part
,
unsigned
int
suggested_use
,
int
*
r_algo
,
unsigned
int
*
r_size
,
unsigned
int
*
r_keyuse
,
char
const
**
r_curve
,
int
*
r_version
,
char
**
r_keygrip
,
u32
*
r_keytime
,
int
*
r_subalgo
,
unsigned
int
*
r_subsize
,
unsigned
int
*
r_subkeyuse
,
char
const
**
r_subcurve
,
int
*
r_subversion
,
char
**
r_subkeygrip
,
u32
*
r_subkeytime
)
{
gpg_error_t
err
=
0
;
char
*
primary
,
*
secondary
;
if
(
r_algo
)
*
r_algo
=
0
;
if
(
r_size
)
*
r_size
=
0
;
if
(
r_keyuse
)
*
r_keyuse
=
0
;
if
(
r_curve
)
*
r_curve
=
NULL
;
if
(
r_version
)
*
r_version
=
4
;
if
(
r_keygrip
)
*
r_keygrip
=
NULL
;
if
(
r_keytime
)
*
r_keytime
=
0
;
if
(
r_subalgo
)
*
r_subalgo
=
0
;
if
(
r_subsize
)
*
r_subsize
=
0
;
if
(
r_subkeyuse
)
*
r_subkeyuse
=
0
;
if
(
r_subcurve
)
*
r_subcurve
=
NULL
;
if
(
r_subversion
)
*
r_subversion
=
4
;
if
(
r_subkeygrip
)
*
r_subkeygrip
=
NULL
;
if
(
r_subkeytime
)
*
r_subkeytime
=
0
;
if
(
!
string
||
!*
string
||
!
ascii_strcasecmp
(
string
,
"default"
)
||
!
strcmp
(
string
,
"-"
))
string
=
get_default_pubkey_algo
();
else
if
(
!
ascii_strcasecmp
(
string
,
"future-default"
)
||
!
ascii_strcasecmp
(
string
,
"futuredefault"
))
string
=
FUTURE_STD_KEY_PARAM
;
else
if
(
!
ascii_strcasecmp
(
string
,
"card"
))
string
=
"card/cert,sign+card/encr"
;
primary
=
xstrdup
(
string
);
secondary
=
strchr
(
primary
,
'+'
);
if
(
secondary
)
*
secondary
++
=
0
;
if
(
part
==
-1
||
part
==
0
)
{
err
=
parse_key_parameter_part
(
ctrl
,
primary
,
0
,
0
,
r_algo
,
r_size
,
r_keyuse
,
r_curve
,
r_version
,
r_keygrip
,
r_keytime
);
if
(
!
err
&&
part
==
-1
)
err
=
parse_key_parameter_part
(
ctrl
,
secondary
,
1
,
0
,
r_subalgo
,
r_subsize
,
r_subkeyuse
,
r_subcurve
,
r_subversion
,
r_subkeygrip
,
r_subkeytime
);
}
else
if
(
part
==
1
)
{
/* If we have SECONDARY, use that part. If there is only one
* part consider this to be the subkey algo. In case a
* SUGGESTED_USE has been given and the usage of the secondary
* part does not match SUGGESTED_USE try again using the primary
* part. Note that when falling back to the primary key we need
* to force clearing the cert usage. */
if
(
secondary
)
{
err
=
parse_key_parameter_part
(
ctrl
,
secondary
,
1
,
0
,
r_algo
,
r_size
,
r_keyuse
,
r_curve
,
r_version
,
r_keygrip
,
r_keytime
);
if
(
!
err
&&
suggested_use
&&
r_keyuse
&&
!
(
suggested_use
&
*
r_keyuse
))
err
=
parse_key_parameter_part
(
ctrl
,
primary
,
1
,
1
/*(clear cert)*/
,
r_algo
,
r_size
,
r_keyuse
,
r_curve
,
r_version
,
r_keygrip
,
r_keytime
);
}
else
err
=
parse_key_parameter_part
(
ctrl
,
primary
,
1
,
0
,
r_algo
,
r_size
,
r_keyuse
,
r_curve
,
r_version
,
r_keygrip
,
r_keytime
);
}
xfree
(
primary
);
return
err
;
}
/* Append R to the linked list PARA. */
static
void
append_to_parameter
(
struct
para_data_s
*
para
,
struct
para_data_s
*
r
)
{
log_assert
(
para
);
while
(
para
->
next
)
para
=
para
->
next
;
para
->
next
=
r
;
}
/* Release the parameter list R. */
static
void
release_parameter_list
(
struct
para_data_s
*
r
)
{
struct
para_data_s
*
r2
;
for
(;
r
;
r
=
r2
)
{
r2
=
r
->
next
;
if
(
r
->
key
==
pPASSPHRASE
&&
*
r
->
u
.
value
)
wipememory
(
r
->
u
.
value
,
strlen
(
r
->
u
.
value
));
xfree
(
r
);
}
}
static
struct
para_data_s
*
get_parameter
(
struct
para_data_s
*
para
,
enum
para_name
key
)
{
struct
para_data_s
*
r
;
for
(
r
=
para
;
r
&&
r
->
key
!=
key
;
r
=
r
->
next
)
;
return
r
;
}
static
const
char
*
get_parameter_value
(
struct
para_data_s
*
para
,
enum
para_name
key
)
{
struct
para_data_s
*
r
=
get_parameter
(
para
,
key
);
return
(
r
&&
*
r
->
u
.
value
)
?
r
->
u
.
value
:
NULL
;
}
/* This is similar to get_parameter_value but also returns the empty
string. This is required so that quick_generate_keypair can use an
empty Passphrase to specify no-protection. */
static
const
char
*
get_parameter_passphrase
(
struct
para_data_s
*
para
)
{
struct
para_data_s
*
r
=
get_parameter
(
para
,
pPASSPHRASE
);
return
r
?
r
->
u
.
value
:
NULL
;
}
static
int
get_parameter_algo
(
ctrl_t
ctrl
,
struct
para_data_s
*
para
,
enum
para_name
key
,
int
*
r_default
)
{
int
i
;
struct
para_data_s
*
r
=
get_parameter
(
para
,
key
);
if
(
r_default
)
*
r_default
=
0
;
if
(
!
r
)
return
-1
;
/* Note that we need to handle the ECC algorithms specified as
strings directly because Libgcrypt folds them all to ECC. */
if
(
!
ascii_strcasecmp
(
r
->
u
.
value
,
"default"
))
{
/* Note: If you change this default algo, remember to change it
* also in gpg.c:gpgconf_list. */
/* FIXME: We only allow the algo here and have a separate thing
* for the curve etc. That is a ugly but demanded for backward
* compatibility with the batch key generation. It would be
* better to make full use of parse_key_parameter_string. */
parse_key_parameter_string
(
ctrl
,
NULL
,
0
,
0
,
&
i
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
if
(
r_default
)
*
r_default
=
1
;
}
else
if
(
digitp
(
r
->
u
.
value
))
i
=
atoi
(
r
->
u
.
value
);
else
if
(
!
strcmp
(
r
->
u
.
value
,
"ELG-E"
)
||
!
strcmp
(
r
->
u
.
value
,
"ELG"
))
i
=
PUBKEY_ALGO_ELGAMAL_E
;
else
if
(
!
ascii_strcasecmp
(
r
->
u
.
value
,
"EdDSA"
))
i
=
PUBKEY_ALGO_EDDSA
;
else
if
(
!
ascii_strcasecmp
(
r
->
u
.
value
,
"ECDSA"
))
i
=
PUBKEY_ALGO_ECDSA
;
else
if
(
!
ascii_strcasecmp
(
r
->
u
.
value
,
"ECDH"
))
i
=
PUBKEY_ALGO_ECDH
;
else
i
=
map_gcry_pk_to_openpgp
(
gcry_pk_map_name
(
r
->
u
.
value
));
if
(
i
==
PUBKEY_ALGO_RSA_E
||
i
==
PUBKEY_ALGO_RSA_S
)
i
=
0
;
/* we don't want to allow generation of these algorithms */
return
i
;
}
/* Parse a usage string. The usage keywords "auth", "sign", "encr"
* may be delimited by space, tab, or comma. On error -1 is returned
* instead of the usage flags. */
static
int
parse_usagestr
(
const
char
*
usagestr
)
{
gpg_error_t
err
;
char
**
tokens
=
NULL
;
const
char
*
s
;
int
i
;
unsigned
int
use
=
0
;
tokens
=
strtokenize
(
usagestr
,
"
\t
,"
);
if
(
!
tokens
)
{
err
=
gpg_error_from_syserror
();
log_error
(
"strtokenize failed: %s
\n
"
,
gpg_strerror
(
err
));
return
-1
;
}
for
(
i
=
0
;
(
s
=
tokens
[
i
]);
i
++
)
{
if
(
!*
s
)
;
else
if
(
!
ascii_strcasecmp
(
s
,
"sign"
))
use
|=
PUBKEY_USAGE_SIG
;
else
if
(
!
ascii_strcasecmp
(
s
,
"encrypt"
)
||
!
ascii_strcasecmp
(
s
,
"encr"
))
use
|=
PUBKEY_USAGE_ENC
;
else
if
(
!
ascii_strcasecmp
(
s
,
"auth"
))
use
|=
PUBKEY_USAGE_AUTH
;
else
if
(
!
ascii_strcasecmp
(
s
,
"cert"
))
use
|=
PUBKEY_USAGE_CERT
;
else
{
xfree
(
tokens
);
return
-1
;
/* error */
}
}
xfree
(
tokens
);
return
use
;
}
/*
* Parse the usage parameter and set the keyflags. Returns -1 on
* error, 0 for no usage given or 1 for usage available.
*/
static
int
parse_parameter_usage
(
const
char
*
fname
,
struct
para_data_s
*
para
,
enum
para_name
key
)
{
struct
para_data_s
*
r
=
get_parameter
(
para
,
key
);
int
i
;
if
(
!
r
)
return
0
;
/* none (this is an optional parameter)*/
i
=
parse_usagestr
(
r
->
u
.
value
);
if
(
i
==
-1
)
{
log_error
(
"%s:%d: invalid usage list
\n
"
,
fname
,
r
->
lnr
);
return
-1
;
/* error */
}
r
->
u
.
usage
=
i
;
return
1
;
}
static
int
parse_revocation_key
(
const
char
*
fname
,
struct
para_data_s
*
para
,
enum
para_name
key
)
{
struct
para_data_s
*
r
=
get_parameter
(
para
,
key
);
struct
revocation_key
revkey
;
char
*
pn
;
int
i
;
if
(
!
r
)
return
0
;
/* none (this is an optional parameter) */
pn
=
r
->
u
.
value
;
revkey
.
class
=
0x80
;
revkey
.
algid
=
atoi
(
pn
);
if
(
!
revkey
.
algid
)
goto
fail
;
/* Skip to the fpr */
while
(
*
pn
&&
*
pn
!=
':'
)
pn
++
;
if
(
*
pn
!=
':'
)
goto
fail
;
pn
++
;
for
(
i
=
0
;
i
<
MAX_FINGERPRINT_LEN
&&
*
pn
;
i
++
,
pn
+=
2
)
{
int
c
=
hextobyte
(
pn
);
if
(
c
==
-1
)
goto
fail
;
revkey
.
fpr
[
i
]
=
c
;
}
if
(
i
!=
20
&&
i
!=
32
)
goto
fail
;
/* skip to the tag */
while
(
*
pn
&&
*
pn
!=
's'
&&
*
pn
!=
'S'
)
pn
++
;
if
(
ascii_strcasecmp
(
pn
,
"sensitive"
)
==
0
)
revkey
.
class
|=
0x40
;
memcpy
(
&
r
->
u
.
revkey
,
&
revkey
,
sizeof
(
struct
revocation_key
));
return
0
;
fail
:
log_error
(
"%s:%d: invalid revocation key
\n
"
,
fname
,
r
->
lnr
);
return
-1
;
/* error */
}
static
u32
get_parameter_u32
(
struct
para_data_s
*
para
,
enum
para_name
key
)
{
struct
para_data_s
*
r
=
get_parameter
(
para
,
key
);
if
(
!
r
)
return
0
;
if
(
r
->
key
==
pKEYCREATIONDATE
||
r
->
key
==
pSUBKEYCREATIONDATE
||
r
->
key
==
pAUTHKEYCREATIONDATE
)
return
r
->
u
.
creation
;
if
(
r
->
key
==
pKEYEXPIRE
||
r
->
key
==
pSUBKEYEXPIRE
)
return
r
->
u
.
expire
;
if
(
r
->
key
==
pKEYUSAGE
||
r
->
key
==
pSUBKEYUSAGE
)
return
r
->
u
.
usage
;
return
(
unsigned
int
)
strtoul
(
r
->
u
.
value
,
NULL
,
10
);
}
static
unsigned
int
get_parameter_uint
(
struct
para_data_s
*
para
,
enum
para_name
key
)
{
return
get_parameter_u32
(
para
,
key
);
}
static
struct
revocation_key
*
get_parameter_revkey
(
struct
para_data_s
*
para
,
enum
para_name
key
)
{
struct
para_data_s
*
r
=
get_parameter
(
para
,
key
);
return
r
?
&
r
->
u
.
revkey
:
NULL
;
}
static
int
get_parameter_bool
(
struct
para_data_s
*
para
,
enum
para_name
key
)
{
struct
para_data_s
*
r
=
get_parameter
(
para
,
key
);
return
(
r
&&
r
->
u
.
abool
);
}
static
int
proc_parameter_file
(
ctrl_t
ctrl
,
struct
para_data_s
*
para
,
const
char
*
fname
,
struct
output_control_s
*
outctrl
,
int
card
)
{
struct
para_data_s
*
r
;
const
char
*
s1
,
*
s2
,
*
s3
;
size_t
n
;
char
*
p
;
int
is_default
=
0
;
int
have_user_id
=
0
;
int
err
,
algo
;
/* Check that we have all required parameters. */
r
=
get_parameter
(
para
,
pKEYTYPE
);
if
(
r
)
{
algo
=
get_parameter_algo
(
ctrl
,
para
,
pKEYTYPE
,
&
is_default
);
if
(
openpgp_pk_test_algo2
(
algo
,
PUBKEY_USAGE_SIG
))
{
log_error
(
"%s:%d: invalid algorithm
\n
"
,
fname
,
r
->
lnr
);
return
-1
;
}
}
else
{
log_error
(
"%s: no Key-Type specified
\n
"
,
fname
);
return
-1
;
}
err
=
parse_parameter_usage
(
fname
,
para
,
pKEYUSAGE
);
if
(
!
err
)
{
/* Default to algo capabilities if key-usage is not provided and
no default algorithm has been requested. */
r
=
xmalloc_clear
(
sizeof
(
*
r
));
r
->
key
=
pKEYUSAGE
;
r
->
u
.
usage
=
(
is_default
?
(
PUBKEY_USAGE_CERT
|
PUBKEY_USAGE_SIG
)
:
openpgp_pk_algo_usage
(
algo
));
append_to_parameter
(
para
,
r
);
}
else
if
(
err
==
-1
)
return
-1
;
else
{
r
=
get_parameter
(
para
,
pKEYUSAGE
);
if
(
r
&&
(
r
->
u
.
usage
&
~
openpgp_pk_algo_usage
(
algo
)))
{
log_error
(
"%s:%d: specified Key-Usage not allowed for algo %d
\n
"
,
fname
,
r
->
lnr
,
algo
);
return
-1
;
}
}
is_default
=
0
;
r
=
get_parameter
(
para
,
pSUBKEYTYPE
);
if
(
r
)
{
algo
=
get_parameter_algo
(
ctrl
,
para
,
pSUBKEYTYPE
,
&
is_default
);
if
(
openpgp_pk_test_algo
(
algo
))
{
log_error
(
"%s:%d: invalid algorithm
\n
"
,
fname
,
r
->
lnr
);
return
-1
;
}
err
=
parse_parameter_usage
(
fname
,
para
,
pSUBKEYUSAGE
);
if
(
!
err
)
{
/* Default to algo capabilities if subkey-usage is not
provided */
r
=
xmalloc_clear
(
sizeof
(
*
r
));
r
->
key
=
pSUBKEYUSAGE
;
r
->
u
.
usage
=
(
is_default
?
PUBKEY_USAGE_ENC
:
openpgp_pk_algo_usage
(
algo
));
append_to_parameter
(
para
,
r
);
}
else
if
(
err
==
-1
)
return
-1
;
else
{
r
=
get_parameter
(
para
,
pSUBKEYUSAGE
);
if
(
r
&&
(
r
->
u
.
usage
&
~
openpgp_pk_algo_usage
(
algo
)))
{
log_error
(
"%s:%d: specified Subkey-Usage not allowed"
" for algo %d
\n
"
,
fname
,
r
->
lnr
,
algo
);
return
-1
;
}
}
}
if
(
get_parameter_value
(
para
,
pUSERID
)
)
have_user_id
=
1
;
else
{
/* create the formatted user ID */
s1
=
get_parameter_value
(
para
,
pNAMEREAL
);
s2
=
get_parameter_value
(
para
,
pNAMECOMMENT
);
s3
=
get_parameter_value
(
para
,
pNAMEEMAIL
);
if
(
s1
||
s2
||
s3
)
{
n
=
(
s1
?
strlen
(
s1
)
:
0
)
+
(
s2
?
strlen
(
s2
)
:
0
)
+
(
s3
?
strlen
(
s3
)
:
0
);
r
=
xmalloc_clear
(
sizeof
*
r
+
n
+
20
);
r
->
key
=
pUSERID
;
p
=
r
->
u
.
value
;
if
(
s1
)
p
=
stpcpy
(
p
,
s1
);
if
(
s2
)
p
=
stpcpy
(
stpcpy
(
stpcpy
(
p
,
" ("
),
s2
),
")"
);
if
(
s3
)
{
/* If we have only the email part, do not add the space
* and the angle brackets. */
if
(
*
r
->
u
.
value
)
p
=
stpcpy
(
stpcpy
(
stpcpy
(
p
,
" <"
),
s3
),
">"
);
else
p
=
stpcpy
(
p
,
s3
);
}
append_to_parameter
(
para
,
r
);
have_user_id
=
1
;
}
}
if
(
!
have_user_id
)
{
log_error
(
"%s: no User-ID specified
\n
"
,
fname
);
return
-1
;
}
/* Set preferences, if any. */
keygen_set_std_prefs
(
get_parameter_value
(
para
,
pPREFERENCES
),
0
);
/* Set keyserver, if any. */
s1
=
get_parameter_value
(
para
,
pKEYSERVER
);
if
(
s1
)
{
struct
keyserver_spec
*
spec
;
spec
=
parse_keyserver_uri
(
s1
,
1
);
if
(
spec
)
{
free_keyserver_spec
(
spec
);
opt
.
def_keyserver_url
=
s1
;
}
else
{
r
=
get_parameter
(
para
,
pKEYSERVER
);
log_error
(
"%s:%d: invalid keyserver url
\n
"
,
fname
,
r
->
lnr
);
return
-1
;
}
}
/* Set revoker, if any. */
if
(
parse_revocation_key
(
fname
,
para
,
pREVOKER
))
return
-1
;
/* Make KEYCREATIONDATE from Creation-Date. We ignore this if the
* key has been taken from a card and a keycreationtime has already
* been set. This is so that we don't generate a key with a
* fingerprint different from the one stored on the OpenPGP card. */
r
=
get_parameter
(
para
,
pCREATIONDATE
);
if
(
r
&&
*
r
->
u
.
value
&&
!
(
get_parameter_bool
(
para
,
pCARDKEY
)
&&
get_parameter_u32
(
para
,
pKEYCREATIONDATE
)))
{
u32
seconds
;
seconds
=
parse_creation_string
(
r
->
u
.
value
);
if
(
!
seconds
)
{
log_error
(
"%s:%d: invalid creation date
\n
"
,
fname
,
r
->
lnr
);
return
-1
;
}
r
->
u
.
creation
=
seconds
;
r
->
key
=
pKEYCREATIONDATE
;
/* Change that entry. */
}
/* Make KEYEXPIRE from Expire-Date. */
r
=
get_parameter
(
para
,
pEXPIREDATE
);
if
(
r
&&
*
r
->
u
.
value
)
{
u32
seconds
;
seconds
=
parse_expire_string
(
r
->
u
.
value
);
if
(
seconds
==
(
u32
)
-1
)
{
log_error
(
"%s:%d: invalid expire date
\n
"
,
fname
,
r
->
lnr
);
return
-1
;
}
r
->
u
.
expire
=
seconds
;
r
->
key
=
pKEYEXPIRE
;
/* change hat entry */
/* also set it for the subkey */
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pSUBKEYEXPIRE
;
r
->
u
.
expire
=
seconds
;
append_to_parameter
(
para
,
r
);
}
do_generate_keypair
(
ctrl
,
para
,
outctrl
,
card
);
return
0
;
}
/****************
* Kludge to allow non interactive key generation controlled
* by a parameter file.
* Note, that string parameters are expected to be in UTF-8
*/
static
void
read_parameter_file
(
ctrl_t
ctrl
,
const
char
*
fname
)
{
static
struct
{
const
char
*
name
;
enum
para_name
key
;
}
keywords
[]
=
{
{
"Key-Type"
,
pKEYTYPE
},
{
"Key-Length"
,
pKEYLENGTH
},
{
"Key-Curve"
,
pKEYCURVE
},
{
"Key-Usage"
,
pKEYUSAGE
},
{
"Subkey-Type"
,
pSUBKEYTYPE
},
{
"Subkey-Length"
,
pSUBKEYLENGTH
},
{
"Subkey-Curve"
,
pSUBKEYCURVE
},
{
"Subkey-Usage"
,
pSUBKEYUSAGE
},
{
"Name-Real"
,
pNAMEREAL
},
{
"Name-Email"
,
pNAMEEMAIL
},
{
"Name-Comment"
,
pNAMECOMMENT
},
{
"Expire-Date"
,
pEXPIREDATE
},
{
"Creation-Date"
,
pCREATIONDATE
},
{
"Passphrase"
,
pPASSPHRASE
},
{
"Preferences"
,
pPREFERENCES
},
{
"Revoker"
,
pREVOKER
},
{
"Handle"
,
pHANDLE
},
{
"Keyserver"
,
pKEYSERVER
},
{
"Keygrip"
,
pKEYGRIP
},
{
"Key-Grip"
,
pKEYGRIP
},
{
"Subkey-grip"
,
pSUBKEYGRIP
},
{
"Key-Version"
,
pVERSION
},
{
"Subkey-Version"
,
pSUBVERSION
},
{
NULL
,
0
}
};
IOBUF
fp
;
byte
*
line
;
unsigned
int
maxlen
,
nline
;
char
*
p
;
int
lnr
;
const
char
*
err
=
NULL
;
struct
para_data_s
*
para
,
*
r
;
int
i
;
struct
output_control_s
outctrl
;
memset
(
&
outctrl
,
0
,
sizeof
(
outctrl
)
);
outctrl
.
pub
.
afx
=
new_armor_context
();
if
(
!
fname
||
!*
fname
)
fname
=
"-"
;
fp
=
iobuf_open
(
fname
);
if
(
fp
&&
is_secured_file
(
iobuf_get_fd
(
fp
)))
{
iobuf_close
(
fp
);
fp
=
NULL
;
gpg_err_set_errno
(
EPERM
);
}
if
(
!
fp
)
{
log_error
(
_
(
"can't open '%s': %s
\n
"
),
fname
,
strerror
(
errno
)
);
return
;
}
iobuf_ioctl
(
fp
,
IOBUF_IOCTL_NO_CACHE
,
1
,
NULL
);
lnr
=
0
;
err
=
NULL
;
para
=
NULL
;
maxlen
=
1024
;
line
=
NULL
;
nline
=
0
;
while
(
iobuf_read_line
(
fp
,
&
line
,
&
nline
,
&
maxlen
)
)
{
char
*
keyword
,
*
value
;
lnr
++
;
if
(
!
maxlen
)
{
err
=
"line too long"
;
break
;
}
for
(
p
=
line
;
isspace
(
*
(
byte
*
)
p
);
p
++
)
;
if
(
!*
p
||
*
p
==
'#'
)
continue
;
keyword
=
p
;
if
(
*
keyword
==
'%'
)
{
for
(
;
!
isspace
(
*
(
byte
*
)
p
);
p
++
)
;
if
(
*
p
)
*
p
++
=
0
;
for
(
;
isspace
(
*
(
byte
*
)
p
);
p
++
)
;
value
=
p
;
trim_trailing_ws
(
value
,
strlen
(
value
)
);
if
(
!
ascii_strcasecmp
(
keyword
,
"%echo"
)
)
log_info
(
"%s
\n
"
,
value
);
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%dry-run"
)
)
outctrl
.
dryrun
=
1
;
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%ask-passphrase"
)
)
;
/* Dummy for backward compatibility. */
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%no-ask-passphrase"
)
)
;
/* Dummy for backward compatibility. */
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%no-protection"
)
)
outctrl
.
keygen_flags
|=
KEYGEN_FLAG_NO_PROTECTION
;
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%transient-key"
)
)
outctrl
.
keygen_flags
|=
KEYGEN_FLAG_TRANSIENT_KEY
;
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%commit"
)
)
{
outctrl
.
lnr
=
lnr
;
if
(
proc_parameter_file
(
ctrl
,
para
,
fname
,
&
outctrl
,
0
))
print_status_key_not_created
(
get_parameter_value
(
para
,
pHANDLE
));
release_parameter_list
(
para
);
para
=
NULL
;
}
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%pubring"
)
)
{
if
(
outctrl
.
pub
.
fname
&&
!
strcmp
(
outctrl
.
pub
.
fname
,
value
)
)
;
/* still the same file - ignore it */
else
{
xfree
(
outctrl
.
pub
.
newfname
);
outctrl
.
pub
.
newfname
=
xstrdup
(
value
);
outctrl
.
use_files
=
1
;
}
}
else
if
(
!
ascii_strcasecmp
(
keyword
,
"%secring"
)
)
{
/* Ignore this command. */
}
else
log_info
(
"skipping control '%s' (%s)
\
n
", keyword, value )
;
continue
;
}
if
(
!
(
p
=
strchr
(
p
,
':'
))
||
p
==
keyword
)
{
err
=
"missing colon"
;
break
;
}
if
(
*
p
)
*
p
++
=
0
;
for
(
;
isspace
(
*
(
byte
*
)
p
);
p
++
)
;
if
(
!*
p
)
{
err
=
"missing argument"
;
break
;
}
value
=
p
;
trim_trailing_ws
(
value
,
strlen
(
value
)
);
for
(
i
=
0
;
keywords
[
i
].
name
;
i
++
)
{
if
(
!
ascii_strcasecmp
(
keywords
[
i
].
name
,
keyword
)
)
break
;
}
if
(
!
keywords
[
i
].
name
)
{
err
=
"unknown keyword"
;
break
;
}
if
(
keywords
[
i
].
key
!=
pKEYTYPE
&&
!
para
)
{
err
=
"parameter block does not start with
\"
Key-Type
\"
"
;
break
;
}
if
(
keywords
[
i
].
key
==
pKEYTYPE
&&
para
)
{
outctrl
.
lnr
=
lnr
;
if
(
proc_parameter_file
(
ctrl
,
para
,
fname
,
&
outctrl
,
0
))
print_status_key_not_created
(
get_parameter_value
(
para
,
pHANDLE
));
release_parameter_list
(
para
);
para
=
NULL
;
}
else
{
for
(
r
=
para
;
r
;
r
=
r
->
next
)
{
if
(
r
->
key
==
keywords
[
i
].
key
)
break
;
}
if
(
r
)
{
err
=
"duplicate keyword"
;
break
;
}
}
if
(
!
opt
.
flags
.
rfc4880bis
&&
(
keywords
[
i
].
key
==
pVERSION
||
keywords
[
i
].
key
==
pSUBVERSION
))
;
/* Ignore version unless --rfc4880bis is active. */
else
{
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
value
)
);
r
->
lnr
=
lnr
;
r
->
key
=
keywords
[
i
].
key
;
strcpy
(
r
->
u
.
value
,
value
);
r
->
next
=
para
;
para
=
r
;
}
}
if
(
err
)
log_error
(
"%s:%d: %s
\n
"
,
fname
,
lnr
,
err
);
else
if
(
iobuf_error
(
fp
)
)
{
log_error
(
"%s:%d: read error
\n
"
,
fname
,
lnr
);
}
else
if
(
para
)
{
outctrl
.
lnr
=
lnr
;
if
(
proc_parameter_file
(
ctrl
,
para
,
fname
,
&
outctrl
,
0
))
print_status_key_not_created
(
get_parameter_value
(
para
,
pHANDLE
));
}
if
(
outctrl
.
use_files
)
{
/* close open streams */
iobuf_close
(
outctrl
.
pub
.
stream
);
/* Must invalidate that ugly cache to actually close it. */
if
(
outctrl
.
pub
.
fname
)
iobuf_ioctl
(
NULL
,
IOBUF_IOCTL_INVALIDATE_CACHE
,
0
,
(
char
*
)
outctrl
.
pub
.
fname
);
xfree
(
outctrl
.
pub
.
fname
);
xfree
(
outctrl
.
pub
.
newfname
);
}
xfree
(
line
);
release_parameter_list
(
para
);
iobuf_close
(
fp
);
release_armor_context
(
outctrl
.
pub
.
afx
);
}
/* Helper for quick_generate_keypair. */
static
struct
para_data_s
*
quickgen_set_para
(
struct
para_data_s
*
para
,
int
for_subkey
,
int
algo
,
int
nbits
,
const
char
*
curve
,
unsigned
int
use
,
int
version
,
const
char
*
keygrip
,
u32
keytime
)
{
struct
para_data_s
*
r
;
r
=
xmalloc_clear
(
sizeof
*
r
+
30
);
r
->
key
=
for_subkey
?
pSUBKEYUSAGE
:
pKEYUSAGE
;
if
(
use
)
snprintf
(
r
->
u
.
value
,
30
,
"%s%s%s%s"
,
(
use
&
PUBKEY_USAGE_ENC
)
?
"encr "
:
""
,
(
use
&
PUBKEY_USAGE_SIG
)
?
"sign "
:
""
,
(
use
&
PUBKEY_USAGE_AUTH
)
?
"auth "
:
""
,
(
use
&
PUBKEY_USAGE_CERT
)
?
"cert "
:
""
);
else
strcpy
(
r
->
u
.
value
,
for_subkey
?
"encr"
:
"sign"
);
r
->
next
=
para
;
para
=
r
;
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
for_subkey
?
pSUBKEYTYPE
:
pKEYTYPE
;
snprintf
(
r
->
u
.
value
,
20
,
"%d"
,
algo
);
r
->
next
=
para
;
para
=
r
;
if
(
keygrip
)
{
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
keygrip
));
r
->
key
=
for_subkey
?
pSUBKEYGRIP
:
pKEYGRIP
;
strcpy
(
r
->
u
.
value
,
keygrip
);
r
->
next
=
para
;
para
=
r
;
}
else
if
(
curve
)
{
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
curve
));
r
->
key
=
for_subkey
?
pSUBKEYCURVE
:
pKEYCURVE
;
strcpy
(
r
->
u
.
value
,
curve
);
r
->
next
=
para
;
para
=
r
;
}
else
{
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
for_subkey
?
pSUBKEYLENGTH
:
pKEYLENGTH
;
sprintf
(
r
->
u
.
value
,
"%u"
,
nbits
);
r
->
next
=
para
;
para
=
r
;
}
if
(
opt
.
flags
.
rfc4880bis
)
{
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
for_subkey
?
pSUBVERSION
:
pVERSION
;
snprintf
(
r
->
u
.
value
,
20
,
"%d"
,
version
);
r
->
next
=
para
;
para
=
r
;
}
if
(
keytime
)
{
r
=
xmalloc_clear
(
sizeof
*
r
);
r
->
key
=
for_subkey
?
pSUBKEYCREATIONDATE
:
pKEYCREATIONDATE
;
r
->
u
.
creation
=
keytime
;
r
->
next
=
para
;
para
=
r
;
}
return
para
;
}
/*
* Unattended generation of a standard key.
*/
void
quick_generate_keypair
(
ctrl_t
ctrl
,
const
char
*
uid
,
const
char
*
algostr
,
const
char
*
usagestr
,
const
char
*
expirestr
)
{
gpg_error_t
err
;
struct
para_data_s
*
para
=
NULL
;
struct
para_data_s
*
r
;
struct
output_control_s
outctrl
;
int
use_tty
;
u32
keytime
=
0
;
memset
(
&
outctrl
,
0
,
sizeof
outctrl
);
use_tty
=
(
!
opt
.
batch
&&
!
opt
.
answer_yes
&&
!*
algostr
&&
!*
usagestr
&&
!*
expirestr
&&
!
cpr_enabled
()
&&
gnupg_isatty
(
fileno
(
stdin
))
&&
gnupg_isatty
(
fileno
(
stdout
))
&&
gnupg_isatty
(
fileno
(
stderr
)));
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
uid
));
r
->
key
=
pUSERID
;
strcpy
(
r
->
u
.
value
,
uid
);
r
->
next
=
para
;
para
=
r
;
uid
=
trim_spaces
(
r
->
u
.
value
);
if
(
!*
uid
||
(
!
opt
.
allow_freeform_uid
&&
!
is_valid_user_id
(
uid
)))
{
log_error
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
GPG_ERR_INV_USER_ID
));
goto
leave
;
}
/* If gpg is directly used on the console ask whether a key with the
given user id shall really be created. */
if
(
use_tty
)
{
tty_printf
(
_
(
"About to create a key for:
\n
\"
%s
\"\n\n
"
),
uid
);
if
(
!
cpr_get_answer_is_yes_def
(
"quick_keygen.okay"
,
_
(
"Continue? (Y/n) "
),
1
))
goto
leave
;
}
/* Check whether such a user ID already exists. */
{
KEYDB_HANDLE
kdbhd
;
KEYDB_SEARCH_DESC
desc
;
memset
(
&
desc
,
0
,
sizeof
desc
);
desc
.
mode
=
KEYDB_SEARCH_MODE_EXACT
;
desc
.
u
.
name
=
uid
;
kdbhd
=
keydb_new
(
ctrl
);
if
(
!
kdbhd
)
goto
leave
;
err
=
keydb_search
(
kdbhd
,
&
desc
,
1
,
NULL
);
keydb_release
(
kdbhd
);
if
(
gpg_err_code
(
err
)
!=
GPG_ERR_NOT_FOUND
)
{
log_info
(
_
(
"A key for
\"
%s
\"
already exists
\n
"
),
uid
);
if
(
opt
.
answer_yes
)
;
else
if
(
!
use_tty
||
!
cpr_get_answer_is_yes_def
(
"quick_keygen.force"
,
_
(
"Create anyway? (y/N)
"), 0))
{
write_status_error
(
"genkey"
,
gpg_error
(
304
));
log_inc_errorcount
();
/* we used log_info */
goto
leave
;
}
log_info
(
_
(
"creating anyway
\n
"
));
}
}
if
(
!*
expirestr
||
strcmp
(
expirestr
,
"-"
)
==
0
)
expirestr
=
default_expiration_interval
;
if
((
!*
algostr
||
!
ascii_strcasecmp
(
algostr
,
"default"
)
||
!
ascii_strcasecmp
(
algostr
,
"future-default"
)
||
!
ascii_strcasecmp
(
algostr
,
"futuredefault"
)
||
!
ascii_strcasecmp
(
algostr
,
"card"
))
&&
(
!*
usagestr
||
!
ascii_strcasecmp
(
usagestr
,
"default"
)
||
!
strcmp
(
usagestr
,
"-"
)))
{
/* Use default key parameters. */
int
algo
,
subalgo
,
version
,
subversion
;
unsigned
int
size
,
subsize
;
unsigned
int
keyuse
,
subkeyuse
;
const
char
*
curve
,
*
subcurve
;
char
*
keygrip
,
*
subkeygrip
;
u32
subkeytime
;
err
=
parse_key_parameter_string
(
ctrl
,
algostr
,
-1
,
0
,
&
algo
,
&
size
,
&
keyuse
,
&
curve
,
&
version
,
&
keygrip
,
&
keytime
,
&
subalgo
,
&
subsize
,
&
subkeyuse
,
&
subcurve
,
&
subversion
,
&
subkeygrip
,
&
subkeytime
);
if
(
err
)
{
log_error
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
err
));
goto
leave
;
}
para
=
quickgen_set_para
(
para
,
0
,
algo
,
size
,
curve
,
keyuse
,
version
,
keygrip
,
keytime
);
if
(
subalgo
)
para
=
quickgen_set_para
(
para
,
1
,
subalgo
,
subsize
,
subcurve
,
subkeyuse
,
subversion
,
subkeygrip
,
subkeytime
);
if
(
*
expirestr
)
{
u32
expire
;
expire
=
parse_expire_string
(
expirestr
);
if
(
expire
==
(
u32
)
-1
)
{
err
=
gpg_error
(
GPG_ERR_INV_VALUE
);
log_error
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
err
));
goto
leave
;
}
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYEXPIRE
;
r
->
u
.
expire
=
expire
;
r
->
next
=
para
;
para
=
r
;
}
xfree
(
keygrip
);
xfree
(
subkeygrip
);
}
else
{
/* Extended unattended mode. Creates only the primary key. */
int
algo
,
version
;
unsigned
int
use
;
u32
expire
;
unsigned
int
nbits
;
const
char
*
curve
;
char
*
keygrip
;
err
=
parse_algo_usage_expire
(
ctrl
,
0
,
algostr
,
usagestr
,
expirestr
,
&
algo
,
&
use
,
&
expire
,
&
nbits
,
&
curve
,
&
version
,
&
keygrip
,
&
keytime
);
if
(
err
)
{
log_error
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
err
)
);
goto
leave
;
}
para
=
quickgen_set_para
(
para
,
0
,
algo
,
nbits
,
curve
,
use
,
version
,
keygrip
,
keytime
);
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYEXPIRE
;
r
->
u
.
expire
=
expire
;
r
->
next
=
para
;
para
=
r
;
xfree
(
keygrip
);
}
/* If the pinentry loopback mode is not and we have a static
passphrase (i.e. set with --passphrase{,-fd,-file} while in batch
mode), we use that passphrase for the new key. */
if
(
opt
.
pinentry_mode
!=
PINENTRY_MODE_LOOPBACK
&&
have_static_passphrase
())
{
const
char
*
s
=
get_static_passphrase
();
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
s
));
r
->
key
=
pPASSPHRASE
;
strcpy
(
r
->
u
.
value
,
s
);
r
->
next
=
para
;
para
=
r
;
}
/* If KEYTIME is set we know that the key has been taken from the
* card. Store that flag in the parameters. */
if
(
keytime
)
{
r
=
xmalloc_clear
(
sizeof
*
r
);
r
->
key
=
pCARDKEY
;
r
->
u
.
abool
=
1
;
r
->
next
=
para
;
para
=
r
;
}
proc_parameter_file
(
ctrl
,
para
,
"[internal]"
,
&
outctrl
,
0
);
leave
:
release_parameter_list
(
para
);
}
/*
* Generate a keypair (fname is only used in batch mode) If
* CARD_SERIALNO is not NULL the function will create the keys on an
* OpenPGP Card. If CARD_BACKUP_KEY has been set and CARD_SERIALNO is
* NOT NULL, the encryption key for the card is generated on the host,
* imported to the card and a backup file created by gpg-agent. If
* FULL is not set only the basic prompts are used (except for batch
* mode).
*/
void
generate_keypair
(
ctrl_t
ctrl
,
int
full
,
const
char
*
fname
,
const
char
*
card_serialno
,
int
card_backup_key
)
{
gpg_error_t
err
;
unsigned
int
nbits
;
char
*
uid
=
NULL
;
int
algo
;
unsigned
int
use
;
int
both
=
0
;
u32
expire
;
struct
para_data_s
*
para
=
NULL
;
struct
para_data_s
*
r
;
struct
output_control_s
outctrl
;
#ifndef ENABLE_CARD_SUPPORT
(
void
)
card_backup_key
;
#endif
memset
(
&
outctrl
,
0
,
sizeof
(
outctrl
)
);
if
(
opt
.
batch
&&
card_serialno
)
{
/* We don't yet support unattended key generation with a card
* serial number. */
log_error
(
_
(
"can't do this in batch mode
\n
"
));
print_further_info
(
"key generation with card serial number"
);
return
;
}
if
(
opt
.
batch
)
{
read_parameter_file
(
ctrl
,
fname
);
return
;
}
if
(
card_serialno
)
{
#ifdef ENABLE_CARD_SUPPORT
struct
agent_card_info_s
info
;
memset
(
&
info
,
0
,
sizeof
(
info
));
err
=
agent_scd_getattr
(
"KEY-ATTR"
,
&
info
);
if
(
err
)
{
log_error
(
_
(
"error getting current key info: %s
\n
"
),
gpg_strerror
(
err
));
return
;
}
r
=
xcalloc
(
1
,
sizeof
*
r
+
strlen
(
card_serialno
)
);
r
->
key
=
pSERIALNO
;
strcpy
(
r
->
u
.
value
,
card_serialno
);
r
->
next
=
para
;
para
=
r
;
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
info
.
key_attr
[
0
].
algo
);
r
->
next
=
para
;
para
=
r
;
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pKEYUSAGE
;
strcpy
(
r
->
u
.
value
,
"sign"
);
r
->
next
=
para
;
para
=
r
;
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pSUBKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
info
.
key_attr
[
1
].
algo
);
r
->
next
=
para
;
para
=
r
;
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pSUBKEYUSAGE
;
strcpy
(
r
->
u
.
value
,
"encrypt"
);
r
->
next
=
para
;
para
=
r
;
if
(
info
.
key_attr
[
1
].
algo
==
PUBKEY_ALGO_RSA
)
{
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pSUBKEYLENGTH
;
sprintf
(
r
->
u
.
value
,
"%u"
,
info
.
key_attr
[
1
].
nbits
);
r
->
next
=
para
;
para
=
r
;
}
else
if
(
info
.
key_attr
[
1
].
algo
==
PUBKEY_ALGO_ECDSA
||
info
.
key_attr
[
1
].
algo
==
PUBKEY_ALGO_EDDSA
||
info
.
key_attr
[
1
].
algo
==
PUBKEY_ALGO_ECDH
)
{
r
=
xcalloc
(
1
,
sizeof
*
r
+
strlen
(
info
.
key_attr
[
1
].
curve
));
r
->
key
=
pSUBKEYCURVE
;
strcpy
(
r
->
u
.
value
,
info
.
key_attr
[
1
].
curve
);
r
->
next
=
para
;
para
=
r
;
}
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pAUTHKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
info
.
key_attr
[
2
].
algo
);
r
->
next
=
para
;
para
=
r
;
if
(
card_backup_key
)
{
r
=
xcalloc
(
1
,
sizeof
*
r
+
1
);
r
->
key
=
pCARDBACKUPKEY
;
strcpy
(
r
->
u
.
value
,
"1"
);
r
->
next
=
para
;
para
=
r
;
}
#endif
/*ENABLE_CARD_SUPPORT*/
}
else
if
(
full
)
/* Full featured key generation. */
{
int
subkey_algo
;
char
*
key_from_hexgrip
=
NULL
;
int
cardkey
;
u32
keytime
;
algo
=
ask_algo
(
ctrl
,
0
,
&
subkey_algo
,
&
use
,
&
key_from_hexgrip
,
&
cardkey
,
&
keytime
);
if
(
key_from_hexgrip
)
{
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
algo
);
r
->
next
=
para
;
para
=
r
;
if
(
use
)
{
r
=
xmalloc_clear
(
sizeof
*
r
+
25
);
r
->
key
=
pKEYUSAGE
;
sprintf
(
r
->
u
.
value
,
"%s%s%s"
,
(
use
&
PUBKEY_USAGE_SIG
)
?
"sign "
:
""
,
(
use
&
PUBKEY_USAGE_ENC
)
?
"encrypt "
:
""
,
(
use
&
PUBKEY_USAGE_AUTH
)
?
"auth"
:
""
);
r
->
next
=
para
;
para
=
r
;
}
r
=
xmalloc_clear
(
sizeof
*
r
+
40
);
r
->
key
=
pKEYGRIP
;
strcpy
(
r
->
u
.
value
,
key_from_hexgrip
);
r
->
next
=
para
;
para
=
r
;
r
=
xmalloc_clear
(
sizeof
*
r
);
r
->
key
=
pCARDKEY
;
r
->
u
.
abool
=
cardkey
;
r
->
next
=
para
;
para
=
r
;
if
(
cardkey
)
{
r
=
xmalloc_clear
(
sizeof
*
r
);
r
->
key
=
pKEYCREATIONDATE
;
r
->
u
.
creation
=
keytime
;
r
->
next
=
para
;
para
=
r
;
}
xfree
(
key_from_hexgrip
);
}
else
{
const
char
*
curve
=
NULL
;
if
(
subkey_algo
)
{
/* Create primary and subkey at once. */
both
=
1
;
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
{
curve
=
ask_curve
(
&
algo
,
&
subkey_algo
,
NULL
);
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
algo
);
r
->
next
=
para
;
para
=
r
;
nbits
=
0
;
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
curve
));
r
->
key
=
pKEYCURVE
;
strcpy
(
r
->
u
.
value
,
curve
);
r
->
next
=
para
;
para
=
r
;
}
else
{
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
algo
);
r
->
next
=
para
;
para
=
r
;
nbits
=
ask_keysize
(
algo
,
0
);
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYLENGTH
;
sprintf
(
r
->
u
.
value
,
"%u"
,
nbits
);
r
->
next
=
para
;
para
=
r
;
}
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYUSAGE
;
strcpy
(
r
->
u
.
value
,
"sign"
);
r
->
next
=
para
;
para
=
r
;
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pSUBKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
subkey_algo
);
r
->
next
=
para
;
para
=
r
;
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pSUBKEYUSAGE
;
strcpy
(
r
->
u
.
value
,
"encrypt"
);
r
->
next
=
para
;
para
=
r
;
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
{
if
(
algo
==
PUBKEY_ALGO_EDDSA
&&
subkey_algo
==
PUBKEY_ALGO_ECDH
)
{
/* Need to switch to a different curve for the
encryption key. */
if
(
!
strcmp
(
curve
,
"Ed25519"
))
curve
=
"Curve25519"
;
else
curve
=
"X448"
;
}
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
curve
));
r
->
key
=
pSUBKEYCURVE
;
strcpy
(
r
->
u
.
value
,
curve
);
r
->
next
=
para
;
para
=
r
;
}
}
else
/* Create only a single key. */
{
/* For ECC we need to ask for the curve before storing the
algo because ask_curve may change the algo. */
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
{
curve
=
ask_curve
(
&
algo
,
NULL
,
NULL
);
r
=
xmalloc_clear
(
sizeof
*
r
+
strlen
(
curve
));
r
->
key
=
pKEYCURVE
;
strcpy
(
r
->
u
.
value
,
curve
);
r
->
next
=
para
;
para
=
r
;
}
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
pKEYTYPE
;
sprintf
(
r
->
u
.
value
,
"%d"
,
algo
);
r
->
next
=
para
;
para
=
r
;
if
(
use
)
{
r
=
xmalloc_clear
(
sizeof
*
r
+
25
);
r
->
key
=
pKEYUSAGE
;
sprintf
(
r
->
u
.
value
,
"%s%s%s"
,
(
use
&
PUBKEY_USAGE_SIG
)
?
"sign "
:
""
,
(
use
&
PUBKEY_USAGE_ENC
)
?
"encrypt "
:
""
,
(
use
&
PUBKEY_USAGE_AUTH
)
?
"auth"
:
""
);
r
->
next
=
para
;
para
=
r
;
}
nbits
=
0
;
}
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
{
/* The curve has already been set. */
}
else
{
nbits
=
ask_keysize
(
both
?
subkey_algo
:
algo
,
nbits
);
r
=
xmalloc_clear
(
sizeof
*
r
+
20
);
r
->
key
=
both
?
pSUBKEYLENGTH
:
pKEYLENGTH
;
sprintf
(
r
->
u
.
value
,
"%u"
,
nbits
);
r
->
next
=
para
;
para
=
r
;
}
}
}
else
/* Default key generation. */
{
int
subalgo
,
version
,
subversion
;
unsigned
int
size
,
subsize
;
unsigned
int
keyuse
,
subkeyuse
;
const
char
*
curve
,
*
subcurve
;
char
*
keygrip
,
*
subkeygrip
;
u32
keytime
,
subkeytime
;
tty_printf
(
_
(
"Note: Use
\"
%s %s
\"
"
" for a full featured key generation dialog.
\n
"
),
#if USE_GPG2_HACK
GPG_NAME
"2"
#else
GPG_NAME
#endif
,
"--full-generate-key"
);
err
=
parse_key_parameter_string
(
ctrl
,
NULL
,
-1
,
0
,
&
algo
,
&
size
,
&
keyuse
,
&
curve
,
&
version
,
&
keygrip
,
&
keytime
,
&
subalgo
,
&
subsize
,
&
subkeyuse
,
&
subcurve
,
&
subversion
,
&
subkeygrip
,
&
subkeytime
);
if
(
err
)
{
log_error
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
err
));
return
;
}
para
=
quickgen_set_para
(
para
,
0
,
algo
,
size
,
curve
,
keyuse
,
version
,
keygrip
,
keytime
);
if
(
subalgo
)
para
=
quickgen_set_para
(
para
,
1
,
subalgo
,
subsize
,
subcurve
,
subkeyuse
,
subversion
,
subkeygrip
,
subkeytime
);
xfree
(
keygrip
);
xfree
(
subkeygrip
);
}
expire
=
full
?
ask_expire_interval
(
0
,
NULL
)
:
parse_expire_string
(
default_expiration_interval
);
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pKEYEXPIRE
;
r
->
u
.
expire
=
expire
;
r
->
next
=
para
;
para
=
r
;
r
=
xcalloc
(
1
,
sizeof
*
r
+
20
);
r
->
key
=
pSUBKEYEXPIRE
;
r
->
u
.
expire
=
expire
;
r
->
next
=
para
;
para
=
r
;
uid
=
ask_user_id
(
0
,
full
,
NULL
);
if
(
!
uid
)
{
log_error
(
_
(
"Key generation canceled.
\n
"
));
release_parameter_list
(
para
);
return
;
}
r
=
xcalloc
(
1
,
sizeof
*
r
+
strlen
(
uid
));
r
->
key
=
pUSERID
;
strcpy
(
r
->
u
.
value
,
uid
);
r
->
next
=
para
;
para
=
r
;
proc_parameter_file
(
ctrl
,
para
,
"[internal]"
,
&
outctrl
,
!!
card_serialno
);
release_parameter_list
(
para
);
}
/* Create and delete a dummy packet to start off a list of kbnodes. */
static
void
start_tree
(
KBNODE
*
tree
)
{
PACKET
*
pkt
;
pkt
=
xmalloc_clear
(
sizeof
(
*
pkt
));
pkt
->
pkttype
=
PKT_NONE
;
*
tree
=
new_kbnode
(
pkt
);
delete_kbnode
(
*
tree
);
}
/* Write the *protected* secret key to the file. */
static
gpg_error_t
card_write_key_to_backup_file
(
PKT_public_key
*
sk
,
const
char
*
backup_dir
)
{
gpg_error_t
err
=
0
;
int
rc
;
char
keyid_buffer
[
2
*
8
+
1
];
char
name_buffer
[
50
];
char
*
fname
;
IOBUF
fp
;
mode_t
oldmask
;
PACKET
*
pkt
=
NULL
;
format_keyid
(
pk_keyid
(
sk
),
KF_LONG
,
keyid_buffer
,
sizeof
(
keyid_buffer
));
snprintf
(
name_buffer
,
sizeof
name_buffer
,
"sk_%s.gpg"
,
keyid_buffer
);
fname
=
make_filename
(
backup_dir
,
name_buffer
,
NULL
);
/* Note that the umask call is not anymore needed because
iobuf_create now takes care of it. However, it does not harm
and thus we keep it. */
oldmask
=
umask
(
077
);
if
(
is_secured_filename
(
fname
))
{
fp
=
NULL
;
gpg_err_set_errno
(
EPERM
);
}
else
fp
=
iobuf_create
(
fname
,
1
);
umask
(
oldmask
);
if
(
!
fp
)
{
err
=
gpg_error_from_syserror
();
log_error
(
_
(
"can't create backup file '%s': %s
\n
"
),
fname
,
strerror
(
errno
)
);
goto
leave
;
}
pkt
=
xcalloc
(
1
,
sizeof
*
pkt
);
pkt
->
pkttype
=
PKT_SECRET_KEY
;
pkt
->
pkt
.
secret_key
=
sk
;
rc
=
build_packet
(
fp
,
pkt
);
if
(
rc
)
{
log_error
(
"build packet failed: %s
\n
"
,
gpg_strerror
(
rc
));
iobuf_cancel
(
fp
);
}
else
{
char
*
fprbuf
;
iobuf_close
(
fp
);
iobuf_ioctl
(
NULL
,
IOBUF_IOCTL_INVALIDATE_CACHE
,
0
,
(
char
*
)
fname
);
log_info
(
_
(
"Note: backup of card key saved to '%s'
\n
"
),
fname
);
fprbuf
=
hexfingerprint
(
sk
,
NULL
,
0
);
if
(
!
fprbuf
)
{
err
=
gpg_error_from_syserror
();
goto
leave
;
}
write_status_text_and_buffer
(
STATUS_BACKUP_KEY_CREATED
,
fprbuf
,
fname
,
strlen
(
fname
),
0
);
xfree
(
fprbuf
);
}
leave
:
xfree
(
pkt
);
xfree
(
fname
);
return
err
;
}
/* Store key to card and make a backup file in OpenPGP format. */
static
gpg_error_t
card_store_key_with_backup
(
ctrl_t
ctrl
,
PKT_public_key
*
sub_psk
,
const
char
*
backup_dir
)
{
PKT_public_key
*
sk
;
gnupg_isotime_t
timestamp
;
gpg_error_t
err
;
char
*
hexgrip
;
int
rc
;
struct
agent_card_info_s
info
;
gcry_cipher_hd_t
cipherhd
=
NULL
;
char
*
cache_nonce
=
NULL
;
void
*
kek
=
NULL
;
size_t
keklen
;
sk
=
copy_public_key
(
NULL
,
sub_psk
);
if
(
!
sk
)
return
gpg_error_from_syserror
();
epoch2isotime
(
timestamp
,
(
time_t
)
sk
->
timestamp
);
err
=
hexkeygrip_from_pk
(
sk
,
&
hexgrip
);
if
(
err
)
return
err
;
memset
(
&
info
,
0
,
sizeof
(
info
));
rc
=
agent_scd_getattr
(
"SERIALNO"
,
&
info
);
if
(
rc
)
return
(
gpg_error_t
)
rc
;
rc
=
agent_keytocard
(
hexgrip
,
2
,
1
,
info
.
serialno
,
timestamp
);
xfree
(
info
.
serialno
);
if
(
rc
)
{
err
=
(
gpg_error_t
)
rc
;
goto
leave
;
}
err
=
agent_keywrap_key
(
ctrl
,
1
,
&
kek
,
&
keklen
);
if
(
err
)
{
log_error
(
"error getting the KEK: %s
\n
"
,
gpg_strerror
(
err
));
goto
leave
;
}
err
=
gcry_cipher_open
(
&
cipherhd
,
GCRY_CIPHER_AES128
,
GCRY_CIPHER_MODE_AESWRAP
,
0
);
if
(
!
err
)
err
=
gcry_cipher_setkey
(
cipherhd
,
kek
,
keklen
);
if
(
err
)
{
log_error
(
"error setting up an encryption context: %s
\n
"
,
gpg_strerror
(
err
));
goto
leave
;
}
err
=
receive_seckey_from_agent
(
ctrl
,
cipherhd
,
0
,
&
cache_nonce
,
hexgrip
,
sk
);
if
(
err
)
{
log_error
(
"error getting secret key from agent: %s
\n
"
,
gpg_strerror
(
err
));
goto
leave
;
}
err
=
card_write_key_to_backup_file
(
sk
,
backup_dir
);
if
(
err
)
log_error
(
"writing card key to backup file: %s
\n
"
,
gpg_strerror
(
err
));
else
/* Remove secret key data in agent side. */
agent_scd_learn
(
NULL
,
1
);
leave
:
xfree
(
cache_nonce
);
gcry_cipher_close
(
cipherhd
);
xfree
(
kek
);
xfree
(
hexgrip
);
free_public_key
(
sk
);
return
err
;
}
static
void
do_generate_keypair
(
ctrl_t
ctrl
,
struct
para_data_s
*
para
,
struct
output_control_s
*
outctrl
,
int
card
)
{
gpg_error_t
err
;
KBNODE
pub_root
=
NULL
;
const
char
*
s
;
PKT_public_key
*
pri_psk
=
NULL
;
PKT_public_key
*
sub_psk
=
NULL
;
struct
revocation_key
*
revkey
;
int
did_sub
=
0
;
u32
keytimestamp
,
subkeytimestamp
,
authkeytimestamp
,
signtimestamp
;
char
*
cache_nonce
=
NULL
;
int
algo
;
u32
expire
;
const
char
*
key_from_hexgrip
=
NULL
;
int
cardkey
;
unsigned
int
keygen_flags
;
if
(
outctrl
->
dryrun
)
{
log_info
(
"dry-run mode - key generation skipped
\n
"
);
return
;
}
if
(
outctrl
->
use_files
)
{
if
(
outctrl
->
pub
.
newfname
)
{
iobuf_close
(
outctrl
->
pub
.
stream
);
outctrl
->
pub
.
stream
=
NULL
;
if
(
outctrl
->
pub
.
fname
)
iobuf_ioctl
(
NULL
,
IOBUF_IOCTL_INVALIDATE_CACHE
,
0
,
(
char
*
)
outctrl
->
pub
.
fname
);
xfree
(
outctrl
->
pub
.
fname
);
outctrl
->
pub
.
fname
=
outctrl
->
pub
.
newfname
;
outctrl
->
pub
.
newfname
=
NULL
;
if
(
is_secured_filename
(
outctrl
->
pub
.
fname
)
)
{
outctrl
->
pub
.
stream
=
NULL
;
gpg_err_set_errno
(
EPERM
);
}
else
outctrl
->
pub
.
stream
=
iobuf_create
(
outctrl
->
pub
.
fname
,
0
);
if
(
!
outctrl
->
pub
.
stream
)
{
log_error
(
_
(
"can't create '%s': %s
\n
"
),
outctrl
->
pub
.
newfname
,
strerror
(
errno
)
);
return
;
}
if
(
opt
.
armor
)
{
outctrl
->
pub
.
afx
->
what
=
1
;
push_armor_filter
(
outctrl
->
pub
.
afx
,
outctrl
->
pub
.
stream
);
}
}
log_assert
(
outctrl
->
pub
.
stream
);
if
(
opt
.
verbose
)
log_info
(
_
(
"writing public key to '%s'
\n
"
),
outctrl
->
pub
.
fname
);
}
/* We create the packets as a tree of kbnodes. Because the
structure we create is known in advance we simply generate a
linked list. The first packet is a dummy packet which we flag as
deleted. The very first packet must always be a KEY packet. */
start_tree
(
&
pub_root
);
cardkey
=
get_parameter_bool
(
para
,
pCARDKEY
);
/* In the case that the keys are created from the card we need to
* take the timestamps from the card. Only in this case a
* pSUBKEYCREATIONDATE or pAUTHKEYCREATIONDATE might be defined and
* then we need to use that so that the fingerprint of the subkey
* also matches the pre-computed and stored one on the card. In
* this case we also use the current time to create the
* self-signatures. */
keytimestamp
=
get_parameter_u32
(
para
,
pKEYCREATIONDATE
);
if
(
!
keytimestamp
)
keytimestamp
=
make_timestamp
();
subkeytimestamp
=
cardkey
?
get_parameter_u32
(
para
,
pSUBKEYCREATIONDATE
)
:
0
;
if
(
!
subkeytimestamp
)
subkeytimestamp
=
keytimestamp
;
authkeytimestamp
=
cardkey
?
get_parameter_u32
(
para
,
pAUTHKEYCREATIONDATE
)
:
0
;
if
(
!
authkeytimestamp
)
authkeytimestamp
=
keytimestamp
;
signtimestamp
=
cardkey
?
make_timestamp
()
:
keytimestamp
;
/* log_debug ("XXX: cardkey ..: %d\n", cardkey); */
/* log_debug ("XXX: keytime ..: %s\n", isotimestamp (keytimestamp)); */
/* log_debug ("XXX: subkeytime: %s\n", isotimestamp (subkeytimestamp)); */
/* log_debug ("XXX: authkeytim: %s\n", isotimestamp (authkeytimestamp)); */
/* log_debug ("XXX: signtime .: %s\n", isotimestamp (signtimestamp)); */
/* Fixme: Check that this comment is still valid:
Note that, depending on the backend (i.e. the used scdaemon
version), the card key generation may update TIMESTAMP for each
key. Thus we need to pass TIMESTAMP to all signing function to
make sure that the binding signature is done using the timestamp
of the corresponding (sub)key and not that of the primary key.
An alternative implementation could tell the signing function the
node of the subkey but that is more work than just to pass the
current timestamp. */
algo
=
get_parameter_algo
(
ctrl
,
para
,
pKEYTYPE
,
NULL
);
expire
=
get_parameter_u32
(
para
,
pKEYEXPIRE
);
key_from_hexgrip
=
get_parameter_value
(
para
,
pKEYGRIP
);
if
(
cardkey
&&
!
key_from_hexgrip
)
BUG
();
keygen_flags
=
outctrl
->
keygen_flags
;
if
(
get_parameter_uint
(
para
,
pVERSION
)
==
5
)
keygen_flags
|=
KEYGEN_FLAG_CREATE_V5_KEY
;
if
(
key_from_hexgrip
)
err
=
do_create_from_keygrip
(
ctrl
,
algo
,
key_from_hexgrip
,
cardkey
,
pub_root
,
keytimestamp
,
expire
,
0
,
keygen_flags
);
else
if
(
!
card
)
err
=
do_create
(
algo
,
get_parameter_uint
(
para
,
pKEYLENGTH
),
get_parameter_value
(
para
,
pKEYCURVE
),
pub_root
,
keytimestamp
,
expire
,
0
,
keygen_flags
,
get_parameter_passphrase
(
para
),
&
cache_nonce
,
NULL
);
else
err
=
gen_card_key
(
1
,
algo
,
1
,
pub_root
,
&
keytimestamp
,
expire
,
keygen_flags
);
/* Get the pointer to the generated public key packet. */
if
(
!
err
)
{
pri_psk
=
pub_root
->
next
->
pkt
->
pkt
.
public_key
;
log_assert
(
pri_psk
);
/* Make sure a few fields are correctly set up before going
further. */
pri_psk
->
flags
.
primary
=
1
;
keyid_from_pk
(
pri_psk
,
NULL
);
/* We don't use pk_keyid to get keyid, because it also asserts
that main_keyid is set! */
keyid_copy
(
pri_psk
->
main_keyid
,
pri_psk
->
keyid
);
}
if
(
!
err
&&
(
revkey
=
get_parameter_revkey
(
para
,
pREVOKER
)))
err
=
write_direct_sig
(
ctrl
,
pub_root
,
pri_psk
,
revkey
,
signtimestamp
,
cache_nonce
);
if
(
!
err
&&
(
s
=
get_parameter_value
(
para
,
pUSERID
)))
{
err
=
write_uid
(
pub_root
,
s
);
if
(
!
err
)
err
=
write_selfsigs
(
ctrl
,
pub_root
,
pri_psk
,
get_parameter_uint
(
para
,
pKEYUSAGE
),
signtimestamp
,
cache_nonce
);
}
/* Write the auth key to the card before the encryption key. This
is a partial workaround for a PGP bug (as of this writing, all
versions including 8.1), that causes it to try and encrypt to
the most recent subkey regardless of whether that subkey is
actually an encryption type. In this case, the auth key is an
RSA key so it succeeds. */
if
(
!
err
&&
card
&&
get_parameter
(
para
,
pAUTHKEYTYPE
))
{
err
=
gen_card_key
(
3
,
get_parameter_algo
(
ctrl
,
para
,
pAUTHKEYTYPE
,
NULL
),
0
,
pub_root
,
&
authkeytimestamp
,
expire
,
keygen_flags
);
if
(
!
err
)
err
=
write_keybinding
(
ctrl
,
pub_root
,
pri_psk
,
NULL
,
PUBKEY_USAGE_AUTH
,
signtimestamp
,
cache_nonce
);
}
if
(
!
err
&&
get_parameter
(
para
,
pSUBKEYTYPE
))
{
int
subkey_algo
=
get_parameter_algo
(
ctrl
,
para
,
pSUBKEYTYPE
,
NULL
);
s
=
NULL
;
key_from_hexgrip
=
get_parameter_value
(
para
,
pSUBKEYGRIP
);
keygen_flags
=
outctrl
->
keygen_flags
;
if
(
get_parameter_uint
(
para
,
pSUBVERSION
)
==
5
)
keygen_flags
|=
KEYGEN_FLAG_CREATE_V5_KEY
;
if
(
key_from_hexgrip
)
err
=
do_create_from_keygrip
(
ctrl
,
subkey_algo
,
key_from_hexgrip
,
cardkey
,
pub_root
,
subkeytimestamp
,
get_parameter_u32
(
para
,
pSUBKEYEXPIRE
),
1
,
keygen_flags
);
else
if
(
!
card
||
(
s
=
get_parameter_value
(
para
,
pCARDBACKUPKEY
)))
{
err
=
do_create
(
subkey_algo
,
get_parameter_uint
(
para
,
pSUBKEYLENGTH
),
get_parameter_value
(
para
,
pSUBKEYCURVE
),
pub_root
,
subkeytimestamp
,
get_parameter_u32
(
para
,
pSUBKEYEXPIRE
),
1
,
s
?
KEYGEN_FLAG_NO_PROTECTION
:
keygen_flags
,
get_parameter_passphrase
(
para
),
&
cache_nonce
,
NULL
);
/* Get the pointer to the generated public subkey packet. */
if
(
!
err
)
{
kbnode_t
node
;
for
(
node
=
pub_root
;
node
;
node
=
node
->
next
)
if
(
node
->
pkt
->
pkttype
==
PKT_PUBLIC_SUBKEY
)
sub_psk
=
node
->
pkt
->
pkt
.
public_key
;
log_assert
(
sub_psk
);
if
(
s
)
err
=
card_store_key_with_backup
(
ctrl
,
sub_psk
,
gnupg_homedir
());
}
}
else
{
err
=
gen_card_key
(
2
,
subkey_algo
,
0
,
pub_root
,
&
subkeytimestamp
,
expire
,
keygen_flags
);
}
if
(
!
err
)
err
=
write_keybinding
(
ctrl
,
pub_root
,
pri_psk
,
sub_psk
,
get_parameter_uint
(
para
,
pSUBKEYUSAGE
),
signtimestamp
,
cache_nonce
);
did_sub
=
1
;
}
if
(
!
err
&&
outctrl
->
use_files
)
/* Direct write to specified files. */
{
err
=
write_keyblock
(
outctrl
->
pub
.
stream
,
pub_root
);
if
(
err
)
log_error
(
"can't write public key: %s
\n
"
,
gpg_strerror
(
err
));
}
else
if
(
!
err
)
/* Write to the standard keyrings. */
{
KEYDB_HANDLE
pub_hd
;
pub_hd
=
keydb_new
(
ctrl
);
if
(
!
pub_hd
)
err
=
gpg_error_from_syserror
();
else
{
err
=
keydb_locate_writable
(
pub_hd
);
if
(
err
)
log_error
(
_
(
"no writable public keyring found: %s
\n
"
),
gpg_strerror
(
err
));
}
if
(
!
err
&&
opt
.
verbose
)
{
log_info
(
_
(
"writing public key to '%s'
\n
"
),
keydb_get_resource_name
(
pub_hd
));
}
if
(
!
err
)
{
err
=
keydb_insert_keyblock
(
pub_hd
,
pub_root
);
if
(
err
)
log_error
(
_
(
"error writing public keyring '%s': %s
\n
"
),
keydb_get_resource_name
(
pub_hd
),
gpg_strerror
(
err
));
}
keydb_release
(
pub_hd
);
if
(
!
err
)
{
int
no_enc_rsa
;
PKT_public_key
*
pk
;
no_enc_rsa
=
((
get_parameter_algo
(
ctrl
,
para
,
pKEYTYPE
,
NULL
)
==
PUBKEY_ALGO_RSA
)
&&
get_parameter_uint
(
para
,
pKEYUSAGE
)
&&
!
((
get_parameter_uint
(
para
,
pKEYUSAGE
)
&
PUBKEY_USAGE_ENC
))
);
pk
=
find_kbnode
(
pub_root
,
PKT_PUBLIC_KEY
)
->
pkt
->
pkt
.
public_key
;
keyid_from_pk
(
pk
,
pk
->
main_keyid
);
register_trusted_keyid
(
pk
->
main_keyid
);
update_ownertrust
(
ctrl
,
pk
,
((
get_ownertrust
(
ctrl
,
pk
)
&
~
TRUST_MASK
)
|
TRUST_ULTIMATE
));
gen_standard_revoke
(
ctrl
,
pk
,
cache_nonce
);
/* Get rid of the first empty packet. */
commit_kbnode
(
&
pub_root
);
if
(
!
opt
.
batch
)
{
tty_printf
(
_
(
"public and secret key created and signed.
\n
"
)
);
tty_printf
(
"
\n
"
);
merge_keys_and_selfsig
(
ctrl
,
pub_root
);
list_keyblock_direct
(
ctrl
,
pub_root
,
0
,
1
,
opt
.
fingerprint
||
opt
.
with_fingerprint
,
1
);
}
if
(
!
opt
.
batch
&&
(
get_parameter_algo
(
ctrl
,
para
,
pKEYTYPE
,
NULL
)
==
PUBKEY_ALGO_DSA
||
no_enc_rsa
)
&&
!
get_parameter
(
para
,
pSUBKEYTYPE
)
)
{
tty_printf
(
_
(
"Note that this key cannot be used for "
"encryption. You may want to use
\n
"
"the command
\"
--edit-key
\"
to generate a "
"subkey for this purpose.
\n
"
)
);
}
}
}
if
(
err
)
{
if
(
opt
.
batch
)
log_error
(
"key generation failed: %s
\n
"
,
gpg_strerror
(
err
)
);
else
tty_printf
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
err
)
);
write_status_error
(
card
?
"card_key_generate"
:
"key_generate"
,
err
);
print_status_key_not_created
(
get_parameter_value
(
para
,
pHANDLE
)
);
}
else
{
PKT_public_key
*
pk
=
find_kbnode
(
pub_root
,
PKT_PUBLIC_KEY
)
->
pkt
->
pkt
.
public_key
;
print_status_key_created
(
did_sub
?
'B'
:
'P'
,
pk
,
get_parameter_value
(
para
,
pHANDLE
));
}
release_kbnode
(
pub_root
);
xfree
(
cache_nonce
);
}
static
gpg_error_t
parse_algo_usage_expire
(
ctrl_t
ctrl
,
int
for_subkey
,
const
char
*
algostr
,
const
char
*
usagestr
,
const
char
*
expirestr
,
int
*
r_algo
,
unsigned
int
*
r_usage
,
u32
*
r_expire
,
unsigned
int
*
r_nbits
,
const
char
**
r_curve
,
int
*
r_version
,
char
**
r_keygrip
,
u32
*
r_keytime
)
{
gpg_error_t
err
;
int
algo
;
unsigned
int
use
,
nbits
;
u32
expire
;
int
wantuse
;
int
version
=
4
;
const
char
*
curve
=
NULL
;
*
r_curve
=
NULL
;
if
(
r_keygrip
)
*
r_keygrip
=
NULL
;
if
(
r_keytime
)
*
r_keytime
=
0
;
nbits
=
0
;
/* Parse the algo string. */
if
(
algostr
&&
*
algostr
==
'&'
&&
strlen
(
algostr
)
==
41
)
{
/* Take algo from existing key. */
algo
=
check_keygrip
(
ctrl
,
algostr
+
1
);
/* FIXME: We need the curve name as well. */
return
gpg_error
(
GPG_ERR_NOT_IMPLEMENTED
);
}
err
=
parse_key_parameter_string
(
ctrl
,
algostr
,
for_subkey
?
1
:
0
,
usagestr
?
parse_usagestr
(
usagestr
)
:
0
,
&
algo
,
&
nbits
,
&
use
,
&
curve
,
&
version
,
r_keygrip
,
r_keytime
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
);
if
(
err
)
{
if
(
r_keygrip
)
{
xfree
(
*
r_keygrip
);
*
r_keygrip
=
NULL
;
}
return
err
;
}
/* Parse the usage string. */
if
(
!
usagestr
||
!*
usagestr
||
!
ascii_strcasecmp
(
usagestr
,
"default"
)
||
!
strcmp
(
usagestr
,
"-"
))
;
/* Keep usage from parse_key_parameter_string. */
else
if
((
wantuse
=
parse_usagestr
(
usagestr
))
!=
-1
)
use
=
wantuse
;
else
{
if
(
r_keygrip
)
{
xfree
(
*
r_keygrip
);
*
r_keygrip
=
NULL
;
}
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
/* Make sure a primary key has the CERT usage. */
if
(
!
for_subkey
)
use
|=
PUBKEY_USAGE_CERT
;
/* Check that usage is possible. NB: We have the same check in
* parse_key_parameter_string but need it here again in case the
* separate usage value has been given. */
if
(
/**/
((
use
&
(
PUBKEY_USAGE_SIG
|
PUBKEY_USAGE_AUTH
|
PUBKEY_USAGE_CERT
))
&&
!
pubkey_get_nsig
(
algo
))
||
((
use
&
PUBKEY_USAGE_ENC
)
&&
!
pubkey_get_nenc
(
algo
))
||
(
for_subkey
&&
(
use
&
PUBKEY_USAGE_CERT
)))
{
if
(
r_keygrip
)
{
xfree
(
*
r_keygrip
);
*
r_keygrip
=
NULL
;
}
return
gpg_error
(
GPG_ERR_WRONG_KEY_USAGE
);
}
/* Parse the expire string. */
expire
=
parse_expire_string
(
expirestr
);
if
(
expire
==
(
u32
)
-1
)
{
if
(
r_keygrip
)
{
xfree
(
*
r_keygrip
);
*
r_keygrip
=
NULL
;
}
return
gpg_error
(
GPG_ERR_INV_VALUE
);
}
if
(
curve
)
*
r_curve
=
curve
;
*
r_algo
=
algo
;
*
r_usage
=
use
;
*
r_expire
=
expire
;
*
r_nbits
=
nbits
;
*
r_version
=
version
;
return
0
;
}
/* Add a new subkey to an existing key. Returns 0 if a new key has
been generated and put into the keyblocks. If any of ALGOSTR,
USAGESTR, or EXPIRESTR is NULL interactive mode is used. */
gpg_error_t
generate_subkeypair
(
ctrl_t
ctrl
,
kbnode_t
keyblock
,
const
char
*
algostr
,
const
char
*
usagestr
,
const
char
*
expirestr
)
{
gpg_error_t
err
=
0
;
int
interactive
;
kbnode_t
node
;
PKT_public_key
*
pri_psk
=
NULL
;
PKT_public_key
*
sub_psk
=
NULL
;
int
algo
;
unsigned
int
use
;
u32
expire
;
unsigned
int
nbits
=
0
;
const
char
*
curve
=
NULL
;
u32
cur_time
;
char
*
key_from_hexgrip
=
NULL
;
u32
keytime
=
0
;
int
cardkey
=
0
;
char
*
hexgrip
=
NULL
;
char
*
serialno
=
NULL
;
char
*
cache_nonce
=
NULL
;
char
*
passwd_nonce
=
NULL
;
int
keygen_flags
=
0
;
interactive
=
(
!
algostr
||
!
usagestr
||
!
expirestr
);
/* Break out the primary key. */
node
=
find_kbnode
(
keyblock
,
PKT_PUBLIC_KEY
);
if
(
!
node
)
{
log_error
(
"Oops; primary key missing in keyblock!
\n
"
);
err
=
gpg_error
(
GPG_ERR_BUG
);
goto
leave
;
}
pri_psk
=
node
->
pkt
->
pkt
.
public_key
;
cur_time
=
make_timestamp
();
if
(
pri_psk
->
timestamp
>
cur_time
)
{
ulong
d
=
pri_psk
->
timestamp
-
cur_time
;
log_info
(
d
==
1
?
_
(
"key has been created %lu second "
"in future (time warp or clock problem)
\n
"
)
:
_
(
"key has been created %lu seconds "
"in future (time warp or clock problem)
\n
"
),
d
);
if
(
!
opt
.
ignore_time_conflict
)
{
err
=
gpg_error
(
GPG_ERR_TIME_CONFLICT
);
goto
leave
;
}
}
if
(
pri_psk
->
version
<
4
)
{
log_info
(
_
(
"Note: creating subkeys for v3 keys "
"is not OpenPGP compliant
\n
"
));
err
=
gpg_error
(
GPG_ERR_CONFLICT
);
goto
leave
;
}
err
=
hexkeygrip_from_pk
(
pri_psk
,
&
hexgrip
);
if
(
err
)
goto
leave
;
if
(
agent_get_keyinfo
(
NULL
,
hexgrip
,
&
serialno
,
NULL
))
{
if
(
interactive
)
tty_printf
(
_
(
"Secret parts of primary key are not available.
\n
"
));
else
log_info
(
_
(
"Secret parts of primary key are not available.
\n
"
));
err
=
gpg_error
(
GPG_ERR_NO_SECKEY
);
goto
leave
;
}
if
(
serialno
)
{
if
(
interactive
)
tty_printf
(
_
(
"Secret parts of primary key are stored on-card.
\n
"
));
else
log_info
(
_
(
"Secret parts of primary key are stored on-card.
\n
"
));
}
if
(
interactive
)
{
algo
=
ask_algo
(
ctrl
,
1
,
NULL
,
&
use
,
&
key_from_hexgrip
,
&
cardkey
,
&
keytime
);
log_assert
(
algo
);
if
(
key_from_hexgrip
)
nbits
=
0
;
else
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
curve
=
ask_curve
(
&
algo
,
NULL
,
NULL
);
else
nbits
=
ask_keysize
(
algo
,
0
);
expire
=
ask_expire_interval
(
0
,
NULL
);
if
(
!
cpr_enabled
()
&&
!
cpr_get_answer_is_yes
(
"keygen.sub.okay"
,
_
(
"Really create? (y/N) "
)))
{
err
=
gpg_error
(
GPG_ERR_CANCELED
);
goto
leave
;
}
}
else
/* Unattended mode. */
{
int
version
;
err
=
parse_algo_usage_expire
(
ctrl
,
1
,
algostr
,
usagestr
,
expirestr
,
&
algo
,
&
use
,
&
expire
,
&
nbits
,
&
curve
,
&
version
,
&
key_from_hexgrip
,
&
keytime
);
if
(
err
)
goto
leave
;
if
(
version
==
5
)
keygen_flags
|=
KEYGEN_FLAG_CREATE_V5_KEY
;
}
/* Verify the passphrase now so that we get a cache item for the
* primary key passphrase. The agent also returns a passphrase
* nonce, which we can use to set the passphrase for the subkey to
* that of the primary key. */
{
char
*
desc
=
gpg_format_keydesc
(
ctrl
,
pri_psk
,
FORMAT_KEYDESC_NORMAL
,
1
);
err
=
agent_passwd
(
ctrl
,
hexgrip
,
desc
,
1
/*=verify*/
,
&
cache_nonce
,
&
passwd_nonce
);
xfree
(
desc
);
if
(
gpg_err_code
(
err
)
==
GPG_ERR_NOT_IMPLEMENTED
&&
gpg_err_source
(
err
)
==
GPG_ERR_SOURCE_GPGAGENT
)
err
=
0
;
/* Very likely that the key is on a card. */
if
(
err
)
goto
leave
;
}
/* Start creation. */
if
(
key_from_hexgrip
)
{
err
=
do_create_from_keygrip
(
ctrl
,
algo
,
key_from_hexgrip
,
cardkey
,
keyblock
,
keytime
?
keytime
:
cur_time
,
expire
,
1
,
keygen_flags
);
}
else
{
const
char
*
passwd
;
/* If the pinentry loopback mode is not and we have a static
passphrase (i.e. set with --passphrase{,-fd,-file} while in batch
mode), we use that passphrase for the new subkey. */
if
(
opt
.
pinentry_mode
!=
PINENTRY_MODE_LOOPBACK
&&
have_static_passphrase
())
passwd
=
get_static_passphrase
();
else
passwd
=
NULL
;
err
=
do_create
(
algo
,
nbits
,
curve
,
keyblock
,
cur_time
,
expire
,
1
,
keygen_flags
,
passwd
,
&
cache_nonce
,
&
passwd_nonce
);
}
if
(
err
)
goto
leave
;
/* Get the pointer to the generated public subkey packet. */
for
(
node
=
keyblock
;
node
;
node
=
node
->
next
)
if
(
node
->
pkt
->
pkttype
==
PKT_PUBLIC_SUBKEY
)
sub_psk
=
node
->
pkt
->
pkt
.
public_key
;
/* Write the binding signature. */
err
=
write_keybinding
(
ctrl
,
keyblock
,
pri_psk
,
sub_psk
,
use
,
cur_time
,
cache_nonce
);
if
(
err
)
goto
leave
;
print_status_key_created
(
'S'
,
sub_psk
,
NULL
);
leave
:
xfree
(
key_from_hexgrip
);
xfree
(
hexgrip
);
xfree
(
serialno
);
xfree
(
cache_nonce
);
xfree
(
passwd_nonce
);
if
(
err
)
log_error
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
err
)
);
return
err
;
}
#ifdef ENABLE_CARD_SUPPORT
/* Generate a subkey on a card. */
gpg_error_t
generate_card_subkeypair
(
ctrl_t
ctrl
,
kbnode_t
pub_keyblock
,
int
keyno
,
const
char
*
serialno
)
{
gpg_error_t
err
=
0
;
kbnode_t
node
;
PKT_public_key
*
pri_pk
=
NULL
;
unsigned
int
use
;
u32
expire
;
u32
cur_time
;
struct
para_data_s
*
para
=
NULL
;
PKT_public_key
*
sub_pk
=
NULL
;
int
algo
;
struct
agent_card_info_s
info
;
int
keygen_flags
=
0
;
/* FIXME!!! */
log_assert
(
keyno
>=
1
&&
keyno
<=
3
);
memset
(
&
info
,
0
,
sizeof
(
info
));
err
=
agent_scd_getattr
(
"KEY-ATTR"
,
&
info
);
if
(
err
)
{
log_error
(
_
(
"error getting current key info: %s
\n
"
),
gpg_strerror
(
err
));
return
err
;
}
algo
=
info
.
key_attr
[
keyno
-1
].
algo
;
para
=
xtrycalloc
(
1
,
sizeof
*
para
+
strlen
(
serialno
)
);
if
(
!
para
)
{
err
=
gpg_error_from_syserror
();
goto
leave
;
}
para
->
key
=
pSERIALNO
;
strcpy
(
para
->
u
.
value
,
serialno
);
/* Break out the primary secret key */
node
=
find_kbnode
(
pub_keyblock
,
PKT_PUBLIC_KEY
);
if
(
!
node
)
{
log_error
(
"Oops; public key lost!
\n
"
);
err
=
gpg_error
(
GPG_ERR_INTERNAL
);
goto
leave
;
}
pri_pk
=
node
->
pkt
->
pkt
.
public_key
;
cur_time
=
make_timestamp
();
if
(
pri_pk
->
timestamp
>
cur_time
)
{
ulong
d
=
pri_pk
->
timestamp
-
cur_time
;
log_info
(
d
==
1
?
_
(
"key has been created %lu second "
"in future (time warp or clock problem)
\n
"
)
:
_
(
"key has been created %lu seconds "
"in future (time warp or clock problem)
\n
"
),
d
);
if
(
!
opt
.
ignore_time_conflict
)
{
err
=
gpg_error
(
GPG_ERR_TIME_CONFLICT
);
goto
leave
;
}
}
if
(
pri_pk
->
version
<
4
)
{
log_info
(
_
(
"Note: creating subkeys for v3 keys "
"is not OpenPGP compliant
\n
"
));
err
=
gpg_error
(
GPG_ERR_NOT_SUPPORTED
);
goto
leave
;
}
expire
=
ask_expire_interval
(
0
,
NULL
);
if
(
keyno
==
1
)
use
=
PUBKEY_USAGE_SIG
;
else
if
(
keyno
==
2
)
use
=
PUBKEY_USAGE_ENC
;
else
use
=
PUBKEY_USAGE_AUTH
;
if
(
!
cpr_enabled
()
&&
!
cpr_get_answer_is_yes
(
"keygen.cardsub.okay"
,
_
(
"Really create? (y/N) "
)))
{
err
=
gpg_error
(
GPG_ERR_CANCELED
);
goto
leave
;
}
/* Note, that depending on the backend, the card key generation may
update CUR_TIME. */
err
=
gen_card_key
(
keyno
,
algo
,
0
,
pub_keyblock
,
&
cur_time
,
expire
,
keygen_flags
);
/* Get the pointer to the generated public subkey packet. */
if
(
!
err
)
{
for
(
node
=
pub_keyblock
;
node
;
node
=
node
->
next
)
if
(
node
->
pkt
->
pkttype
==
PKT_PUBLIC_SUBKEY
)
sub_pk
=
node
->
pkt
->
pkt
.
public_key
;
log_assert
(
sub_pk
);
err
=
write_keybinding
(
ctrl
,
pub_keyblock
,
pri_pk
,
sub_pk
,
use
,
cur_time
,
NULL
);
}
leave
:
if
(
err
)
log_error
(
_
(
"Key generation failed: %s
\n
"
),
gpg_strerror
(
err
)
);
else
print_status_key_created
(
'S'
,
sub_pk
,
NULL
);
release_parameter_list
(
para
);
return
err
;
}
#endif
/* !ENABLE_CARD_SUPPORT */
/*
* Write a keyblock to an output stream
*/
static
int
write_keyblock
(
IOBUF
out
,
KBNODE
node
)
{
for
(
;
node
;
node
=
node
->
next
)
{
if
(
!
is_deleted_kbnode
(
node
))
{
int
rc
=
build_packet
(
out
,
node
->
pkt
);
if
(
rc
)
{
log_error
(
"build_packet(%d) failed: %s
\n
"
,
node
->
pkt
->
pkttype
,
gpg_strerror
(
rc
)
);
return
rc
;
}
}
}
return
0
;
}
/* Note that timestamp is an in/out arg.
* FIXME: Does not yet support v5 keys. */
static
gpg_error_t
gen_card_key
(
int
keyno
,
int
algo
,
int
is_primary
,
kbnode_t
pub_root
,
u32
*
timestamp
,
u32
expireval
,
int
keygen_flags
)
{
#ifdef ENABLE_CARD_SUPPORT
gpg_error_t
err
;
PACKET
*
pkt
;
PKT_public_key
*
pk
;
char
keyid
[
10
];
unsigned
char
*
public
;
gcry_sexp_t
s_key
;
snprintf
(
keyid
,
DIM
(
keyid
),
"OPENPGP.%d"
,
keyno
);
pk
=
xtrycalloc
(
1
,
sizeof
*
pk
);
if
(
!
pk
)
return
gpg_error_from_syserror
();
pkt
=
xtrycalloc
(
1
,
sizeof
*
pkt
);
if
(
!
pkt
)
{
xfree
(
pk
);
return
gpg_error_from_syserror
();
}
/* Note: SCD knows the serialnumber, thus there is no point in passing it. */
err
=
agent_scd_genkey
(
keyno
,
1
,
timestamp
);
/* The code below is not used because we force creation of
* the a card key (3rd arg).
* if (gpg_err_code (rc) == GPG_ERR_EEXIST)
* {
* tty_printf ("\n");
* log_error ("WARNING: key does already exists!\n");
* tty_printf ("\n");
* if ( cpr_get_answer_is_yes( "keygen.card.replace_key",
* _("Replace existing key? ")))
* rc = agent_scd_genkey (keyno, 1, timestamp);
* }
*/
if
(
err
)
{
log_error
(
"key generation failed: %s
\n
"
,
gpg_strerror
(
err
));
xfree
(
pkt
);
xfree
(
pk
);
return
err
;
}
/* Send the READKEY command so that the agent creates a shadow key for
card key. We need to do that now so that we are able to create
the self-signatures. */
err
=
agent_readkey
(
NULL
,
1
,
keyid
,
&
public
);
if
(
err
)
return
err
;
err
=
gcry_sexp_sscan
(
&
s_key
,
NULL
,
public
,
gcry_sexp_canon_len
(
public
,
0
,
NULL
,
NULL
));
xfree
(
public
);
if
(
err
)
return
err
;
if
(
algo
==
PUBKEY_ALGO_RSA
)
err
=
key_from_sexp
(
pk
->
pkey
,
s_key
,
"public-key"
,
"ne"
);
else
if
(
algo
==
PUBKEY_ALGO_ECDSA
||
algo
==
PUBKEY_ALGO_EDDSA
||
algo
==
PUBKEY_ALGO_ECDH
)
err
=
ecckey_from_sexp
(
pk
->
pkey
,
s_key
,
algo
);
else
err
=
gpg_error
(
GPG_ERR_PUBKEY_ALGO
);
gcry_sexp_release
(
s_key
);
if
(
err
)
{
log_error
(
"key_from_sexp failed: %s
\n
"
,
gpg_strerror
(
err
)
);
free_public_key
(
pk
);
return
err
;
}
pk
->
timestamp
=
*
timestamp
;
pk
->
version
=
(
keygen_flags
&
KEYGEN_FLAG_CREATE_V5_KEY
)
?
5
:
4
;
if
(
expireval
)
pk
->
expiredate
=
pk
->
timestamp
+
expireval
;
pk
->
pubkey_algo
=
algo
;
pkt
->
pkttype
=
is_primary
?
PKT_PUBLIC_KEY
:
PKT_PUBLIC_SUBKEY
;
pkt
->
pkt
.
public_key
=
pk
;
add_kbnode
(
pub_root
,
new_kbnode
(
pkt
));
return
0
;
#else
(
void
)
keyno
;
(
void
)
is_primary
;
(
void
)
pub_root
;
(
void
)
timestamp
;
(
void
)
expireval
;
return
gpg_error
(
GPG_ERR_NOT_SUPPORTED
);
#endif
/*!ENABLE_CARD_SUPPORT*/
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 10, 9:10 AM (11 h, 36 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f4/9c/8d1fe6ecb9275c464c01d3b78f56
Attached To
rG GnuPG
Event Timeline
Log In to Comment