Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623775
gpgcompose.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
80 KB
Subscribers
None
gpgcompose.c
View Options
/* gpgcompose.c - Create OpenPGP messages by hand.
* Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
*/
#include
<config.h>
#include
<errno.h>
#include
"gpg.h"
#include
"packet.h"
#include
"keydb.h"
#include
"main.h"
#include
"options.h"
static
int
do_debug
;
#define debug(fmt, ...) \
do { if (do_debug) log_debug (fmt, ##__VA_ARGS__); } while (0)
/* --encryption, for instance, adds a filter in front of out. There
is an operator (--encryption-pop) to end this. We use the
following infrastructure to make it easy to pop the state. */
struct
filter
{
void
*
func
;
void
*
context
;
int
pkttype
;
int
partial_block_mode
;
struct
filter
*
next
;
};
static
struct
filter
*
filters
;
static
void
filter_push
(
iobuf_t
out
,
void
*
func
,
void
*
context
,
int
type
,
int
partial_block_mode
)
{
gpg_error_t
err
;
struct
filter
*
f
=
xmalloc_clear
(
sizeof
(
*
f
));
f
->
next
=
filters
;
f
->
func
=
func
;
f
->
context
=
context
;
f
->
pkttype
=
type
;
f
->
partial_block_mode
=
partial_block_mode
;
filters
=
f
;
err
=
iobuf_push_filter
(
out
,
func
,
context
);
if
(
err
)
log_fatal
(
"Adding filter: %s
\n
"
,
gpg_strerror
(
err
));
}
static
void
filter_pop
(
iobuf_t
out
,
int
expected_type
)
{
gpg_error_t
err
;
struct
filter
*
f
=
filters
;
log_assert
(
f
);
if
(
f
->
pkttype
!=
expected_type
)
log_fatal
(
"Attempted to pop a %s container, "
"but current container is a %s container.
\n
"
,
pkttype_str
(
f
->
pkttype
),
pkttype_str
(
expected_type
));
if
(
f
->
pkttype
==
PKT_ENCRYPTED
||
f
->
pkttype
==
PKT_ENCRYPTED_MDC
)
{
err
=
iobuf_pop_filter
(
out
,
f
->
func
,
f
->
context
);
if
(
err
)
log_fatal
(
"Popping encryption filter: %s
\n
"
,
gpg_strerror
(
err
));
}
else
log_fatal
(
"FILTERS appears to be corrupted.
\n
"
);
if
(
f
->
partial_block_mode
)
iobuf_set_partial_body_length_mode
(
out
,
0
);
filters
=
f
->
next
;
xfree
(
f
);
}
/* Return if CIPHER_ID is a valid cipher. */
static
int
valid_cipher
(
int
cipher_id
)
{
return
(
cipher_id
==
CIPHER_ALGO_IDEA
||
cipher_id
==
CIPHER_ALGO_3DES
||
cipher_id
==
CIPHER_ALGO_CAST5
||
cipher_id
==
CIPHER_ALGO_BLOWFISH
||
cipher_id
==
CIPHER_ALGO_AES
||
cipher_id
==
CIPHER_ALGO_AES192
||
cipher_id
==
CIPHER_ALGO_AES256
||
cipher_id
==
CIPHER_ALGO_TWOFISH
||
cipher_id
==
CIPHER_ALGO_CAMELLIA128
||
cipher_id
==
CIPHER_ALGO_CAMELLIA192
||
cipher_id
==
CIPHER_ALGO_CAMELLIA256
);
}
/* Parse a session key encoded as a string of the form x:HEXDIGITS
where x is the algorithm id. (This is the format emitted by gpg
--show-session-key.) */
struct
session_key
{
int
algo
;
int
keylen
;
char
*
key
;
};
static
struct
session_key
parse_session_key
(
const
char
*
option
,
char
*
p
,
int
require_algo
)
{
char
*
tail
;
struct
session_key
sk
;
memset
(
&
sk
,
0
,
sizeof
(
sk
));
/* Check for the optional "cipher-id:" at the start of the
string. */
errno
=
0
;
sk
.
algo
=
strtol
(
p
,
&
tail
,
10
);
if
(
!
errno
&&
tail
&&
*
tail
==
':'
)
{
if
(
!
valid_cipher
(
sk
.
algo
))
log_info
(
"%s: %d is not a known cipher (but using anyways)
\n
"
,
option
,
sk
.
algo
);
p
=
tail
+
1
;
}
else
if
(
require_algo
)
log_fatal
(
"%s: Session key must have the form algo:HEXCHARACTERS.
\n
"
,
option
);
else
sk
.
algo
=
0
;
/* Ignore a leading 0x. */
if
(
p
[
0
]
==
'0'
&&
p
[
1
]
==
'x'
)
p
+=
2
;
if
(
strlen
(
p
)
%
2
!=
0
)
log_fatal
(
"%s: session key must consist of an even number of hexadecimal characters.
\n
"
,
option
);
sk
.
keylen
=
strlen
(
p
)
/
2
;
sk
.
key
=
xmalloc
(
sk
.
keylen
);
if
(
hex2bin
(
p
,
sk
.
key
,
sk
.
keylen
)
==
-1
)
log_fatal
(
"%s: Session key must only contain hexadecimal characters
\n
"
,
option
);
return
sk
;
}
/* A callback.
OPTION_STR is the option that was matched. ARGC is the number of
arguments following the option and ARGV are those arguments.
(Thus, argv[0] is the first string following the option and
argv[-1] is the option.)
COOKIE is the opaque value passed to process_options. */
typedef
int
(
*
option_prcessor_t
)
(
const
char
*
option_str
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
struct
option
{
/* The option that this matches. This must start with "--" or be
the empty string. The empty string matches bare arguments. */
const
char
*
option
;
/* The function to call to process this option. */
option_prcessor_t
func
;
/* Documentation. */
const
char
*
help
;
};
/* Merge two lists of options. Note: this makes a shallow copy! The
caller must xfree() the result. */
static
struct
option
*
merge_options
(
struct
option
a
[],
struct
option
b
[])
{
int
i
,
j
;
struct
option
*
c
;
for
(
i
=
0
;
a
[
i
].
option
;
i
++
)
;
for
(
j
=
0
;
b
[
j
].
option
;
j
++
)
;
c
=
xmalloc
((
i
+
j
+
1
)
*
sizeof
(
struct
option
));
memcpy
(
c
,
a
,
i
*
sizeof
(
struct
option
));
memcpy
(
&
c
[
i
],
b
,
j
*
sizeof
(
struct
option
));
c
[
i
+
j
].
option
=
NULL
;
if
(
a
[
i
].
help
&&
b
[
j
].
help
)
c
[
i
+
j
].
help
=
xasprintf
(
"%s
\n\n
%s"
,
a
[
i
].
help
,
b
[
j
].
help
);
else
if
(
a
[
i
].
help
)
c
[
i
+
j
].
help
=
a
[
i
].
help
;
else
if
(
b
[
j
].
help
)
c
[
i
+
j
].
help
=
b
[
j
].
help
;
return
c
;
}
/* Returns whether ARG is an option. All options start with --. */
static
int
is_option
(
const
char
*
arg
)
{
return
arg
[
0
]
==
'-'
&&
arg
[
1
]
==
'-'
;
}
/* OPTIONS is a NULL terminated array of struct option:s. Finds the
entry that is the same as ARG. Returns -1 if no entry is found.
The empty string option matches bare arguments. */
static
int
match_option
(
const
struct
option
options
[],
const
char
*
arg
)
{
int
i
;
int
bare_arg
=
!
is_option
(
arg
);
for
(
i
=
0
;
options
[
i
].
option
;
i
++
)
if
((
!
bare_arg
&&
strcmp
(
options
[
i
].
option
,
arg
)
==
0
)
/* Non-options match the empty string. */
||
(
bare_arg
&&
options
[
i
].
option
[
0
]
==
'\0'
))
return
i
;
return
-1
;
}
static
void
show_help
(
struct
option
options
[])
{
int
i
;
int
max_length
=
0
;
int
space
;
for
(
i
=
0
;
options
[
i
].
option
;
i
++
)
{
const
char
*
option
=
options
[
i
].
option
[
0
]
?
options
[
i
].
option
:
"ARG"
;
int
l
=
strlen
(
option
);
if
(
l
>
max_length
)
max_length
=
l
;
}
space
=
72
-
(
max_length
+
2
);
if
(
space
<
40
)
space
=
40
;
for
(
i
=
0
;
;
i
++
)
{
char
*
option
=
options
[
i
].
option
;
char
*
help
=
options
[
i
].
help
;
int
l
;
int
j
;
char
*
tmp
;
char
*
formatted
;
char
*
p
;
char
*
newline
;
if
(
!
option
&&
!
help
)
break
;
if
(
option
)
{
const
char
*
o
=
option
[
0
]
?
option
:
"ARG"
;
l
=
strlen
(
o
);
fprintf
(
stderr
,
"%s"
,
o
);
}
if
(
!
help
)
{
fputc
(
'\n'
,
stderr
);
continue
;
}
if
(
option
)
for
(
j
=
l
;
j
<
max_length
+
2
;
j
++
)
fputc
(
' '
,
stderr
);
#define BOLD_START "\033[1m"
#define NORMAL_RESTORE "\033[0m"
#define BOLD(x) BOLD_START x NORMAL_RESTORE
if
(
!
option
||
options
[
i
].
func
)
tmp
=
(
char
*
)
help
;
else
tmp
=
xasprintf
(
"%s "
BOLD
(
"(Unimplemented.)"
),
help
);
if
(
!
option
)
space
=
72
;
formatted
=
format_text
(
tmp
,
0
,
space
,
space
+
4
);
if
(
tmp
!=
help
)
xfree
(
tmp
);
if
(
!
option
)
{
fprintf
(
stderr
,
"
\n
%s
\n
"
,
formatted
);
break
;
}
for
(
p
=
formatted
;
p
&&
*
p
;
p
=
(
*
newline
==
'\0'
)
?
newline
:
newline
+
1
)
{
newline
=
strchr
(
p
,
'\n'
);
if
(
!
newline
)
newline
=
&
p
[
strlen
(
p
)];
l
=
(
size_t
)
newline
-
(
size_t
)
p
;
if
(
p
!=
formatted
)
for
(
j
=
0
;
j
<
max_length
+
2
;
j
++
)
fputc
(
' '
,
stderr
);
fwrite
(
p
,
l
,
1
,
stderr
);
fputc
(
'\n'
,
stderr
);
}
xfree
(
formatted
);
}
}
/* Return value is number of consumed argv elements. */
static
int
process_options
(
const
char
*
parent_option
,
struct
option
break_options
[],
struct
option
local_options
[],
void
*
lcookie
,
struct
option
global_options
[],
void
*
gcookie
,
int
argc
,
char
*
argv
[])
{
int
i
;
for
(
i
=
0
;
i
<
argc
;
i
++
)
{
int
j
;
struct
option
*
option
;
void
*
cookie
;
int
bare_arg
;
option_prcessor_t
func
;
int
consumed
;
if
(
break_options
)
{
j
=
match_option
(
break_options
,
argv
[
i
]);
if
(
j
!=
-1
)
/* Match. Break out. */
return
i
;
}
j
=
match_option
(
local_options
,
argv
[
i
]);
if
(
j
==
-1
)
{
if
(
global_options
)
j
=
match_option
(
global_options
,
argv
[
i
]);
if
(
j
==
-1
)
{
if
(
strcmp
(
argv
[
i
],
"--help"
)
==
0
)
{
if
(
!
global_options
)
show_help
(
local_options
);
else
{
struct
option
*
combined
=
merge_options
(
local_options
,
global_options
);
show_help
(
combined
);
xfree
(
combined
);
}
g10_exit
(
0
);
}
if
(
parent_option
)
log_fatal
(
"%s: Unknown option: %s
\n
"
,
parent_option
,
argv
[
i
]);
else
log_fatal
(
"Unknown option: %s
\n
"
,
argv
[
i
]);
}
option
=
&
global_options
[
j
];
cookie
=
gcookie
;
}
else
{
option
=
&
local_options
[
j
];
cookie
=
lcookie
;
}
bare_arg
=
strcmp
(
option
->
option
,
""
)
==
0
;
func
=
option
->
func
;
if
(
!
func
)
{
if
(
bare_arg
)
log_fatal
(
"Bare arguments unimplemented.
\n
"
);
else
log_fatal
(
"Unimplemented option: %s
\n
"
,
option
->
option
);
}
consumed
=
func
(
bare_arg
?
parent_option
:
argv
[
i
],
argc
-
i
-
!
bare_arg
,
&
argv
[
i
+
!
bare_arg
],
cookie
);
i
+=
consumed
;
if
(
bare_arg
)
i
--
;
}
return
i
;
}
/* The keys, subkeys, user ids and user attributes in the order that
they were added. */
PACKET
components
[
20
];
/* The number of components. */
int
ncomponents
;
static
int
add_component
(
int
pkttype
,
void
*
component
)
{
int
i
=
ncomponents
++
;
log_assert
(
i
<
sizeof
(
components
)
/
sizeof
(
components
[
0
]));
log_assert
(
pkttype
==
PKT_PUBLIC_KEY
||
pkttype
==
PKT_PUBLIC_SUBKEY
||
pkttype
==
PKT_SECRET_KEY
||
pkttype
==
PKT_SECRET_SUBKEY
||
pkttype
==
PKT_USER_ID
||
pkttype
==
PKT_ATTRIBUTE
);
components
[
i
].
pkttype
=
pkttype
;
components
[
i
].
pkt
.
generic
=
component
;
return
i
;
}
static
void
dump_component
(
PACKET
*
pkt
)
{
struct
kbnode_struct
kbnode
;
if
(
!
do_debug
)
return
;
memset
(
&
kbnode
,
0
,
sizeof
(
kbnode
));
kbnode
.
pkt
=
pkt
;
dump_kbnode
(
&
kbnode
);
}
/* Returns the first primary key in COMPONENTS or NULL if there is
none. */
static
PKT_public_key
*
primary_key
(
void
)
{
int
i
;
for
(
i
=
0
;
i
<
ncomponents
;
i
++
)
if
(
components
[
i
].
pkttype
==
PKT_PUBLIC_KEY
)
return
components
[
i
].
pkt
.
public_key
;
return
NULL
;
}
/* The last session key (updated when adding a SK-ESK, PK-ESK or SED
packet. */
static
DEK
session_key
;
static
int
user_id
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
public_key
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
sk_esk
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
pk_esk
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
encrypted
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
encrypted_pop
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
literal
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
signature
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
int
copy
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
);
static
struct
option
major_options
[]
=
{
{
"--user-id"
,
user_id
,
"Create a user id packet."
},
{
"--public-key"
,
public_key
,
"Create a public key packet."
},
{
"--private-key"
,
NULL
,
"Create a private key packet."
},
{
"--public-subkey"
,
public_key
,
"Create a subkey packet."
},
{
"--private-subkey"
,
NULL
,
"Create a private subkey packet."
},
{
"--sk-esk"
,
sk_esk
,
"Create a symmetric-key encrypted session key packet."
},
{
"--pk-esk"
,
pk_esk
,
"Create a public-key encrypted session key packet."
},
{
"--encrypted"
,
encrypted
,
"Create a symmetrically encrypted data packet."
},
{
"--encrypted-mdc"
,
encrypted
,
"Create a symmetrically encrypted and integrity protected data packet."
},
{
"--encrypted-pop"
,
encrypted_pop
,
"Pop an encryption container."
},
{
"--compressed"
,
NULL
,
"Create a compressed data packet."
},
{
"--literal"
,
literal
,
"Create a literal (plaintext) data packet."
},
{
"--signature"
,
signature
,
"Create a signature packet."
},
{
"--onepass-sig"
,
NULL
,
"Create a one-pass signature packet."
},
{
"--copy"
,
copy
,
"Copy the specified file."
},
{
NULL
,
NULL
,
"To get more information about a given command, use:
\n\n
"
" $ gpgcompose --command --help to list a command's options."
},
};
static
struct
option
global_options
[]
=
{
{
NULL
,
NULL
,
NULL
},
};
/* Make our lives easier and use a static limit for the user name.
10k is way more than enough anyways... */
const
int
user_id_max_len
=
10
*
1024
;
static
int
user_id_name
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
PKT_user_id
*
uid
=
cookie
;
int
l
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s USER_ID
\n
"
,
option
);
if
(
uid
->
len
)
log_fatal
(
"Attempt to set user id multiple times.
\n
"
);
l
=
strlen
(
argv
[
0
]);
if
(
l
>
user_id_max_len
)
log_fatal
(
"user id too long (max: %d)
\n
"
,
user_id_max_len
);
memcpy
(
uid
->
name
,
argv
[
0
],
l
);
uid
->
name
[
l
]
=
0
;
uid
->
len
=
l
;
return
1
;
}
static
struct
option
user_id_options
[]
=
{
{
""
,
user_id_name
,
"Set the user id. This is usually in the format "
"
\"
Name (comment) <email@example.org>
\"
"
},
{
NULL
,
NULL
,
"Example:
\n\n
"
" $ gpgcompose --user-id
\"
USERID
\"
| gpg2 --list-packets"
}
};
static
int
user_id
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
iobuf_t
out
=
cookie
;
gpg_error_t
err
;
PKT_user_id
*
uid
=
xmalloc_clear
(
sizeof
(
*
uid
)
+
user_id_max_len
);
int
c
=
add_component
(
PKT_USER_ID
,
uid
);
int
processed
;
processed
=
process_options
(
option
,
major_options
,
user_id_options
,
uid
,
global_options
,
NULL
,
argc
,
argv
);
if
(
!
uid
->
len
)
log_fatal
(
"%s: user id not given"
,
option
);
err
=
build_packet
(
out
,
&
components
[
c
]);
if
(
err
)
log_fatal
(
"Serializing user id packet: %s
\n
"
,
gpg_strerror
(
err
));
debug
(
"Wrote user id packet:
\n
"
);
dump_component
(
&
components
[
c
]);
return
processed
;
}
static
int
pk_search_terms
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
gpg_error_t
err
;
KEYDB_HANDLE
hd
;
KEYDB_SEARCH_DESC
desc
;
kbnode_t
kb
;
PKT_public_key
*
pk
=
cookie
;
PKT_public_key
*
pk_ref
;
int
i
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s KEYID
\n
"
,
option
);
if
(
pk
->
pubkey_algo
)
log_fatal
(
"%s: multiple keys provided
\n
"
,
option
);
err
=
classify_user_id
(
argv
[
0
],
&
desc
,
0
);
if
(
err
)
log_fatal
(
"search terms '%s': %s
\n
"
,
argv
[
0
],
gpg_strerror
(
err
));
hd
=
keydb_new
();
err
=
keydb_search
(
hd
,
&
desc
,
1
,
NULL
);
if
(
err
)
log_fatal
(
"looking up '%s': %s
\n
"
,
argv
[
0
],
gpg_strerror
(
err
));
err
=
keydb_get_keyblock
(
hd
,
&
kb
);
if
(
err
)
log_fatal
(
"retrieving keyblock for '%s': %s
\n
"
,
argv
[
0
],
gpg_strerror
(
err
));
keydb_release
(
hd
);
pk_ref
=
kb
->
pkt
->
pkt
.
public_key
;
/* Copy the timestamp (if not already set), algo and public key
parameters. */
if
(
!
pk
->
timestamp
)
pk
->
timestamp
=
pk_ref
->
timestamp
;
pk
->
pubkey_algo
=
pk_ref
->
pubkey_algo
;
for
(
i
=
0
;
i
<
pubkey_get_npkey
(
pk
->
pubkey_algo
);
i
++
)
pk
->
pkey
[
i
]
=
gcry_mpi_copy
(
pk_ref
->
pkey
[
i
]);
release_kbnode
(
kb
);
return
1
;
}
static
int
pk_timestamp
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
PKT_public_key
*
pk
=
cookie
;
char
*
tail
=
NULL
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s TIMESTAMP
\n
"
,
option
);
errno
=
0
;
pk
->
timestamp
=
parse_timestamp
(
argv
[
0
],
&
tail
);
if
(
errno
||
(
tail
&&
*
tail
))
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
return
1
;
}
#define TIMESTAMP_HELP \
"Either as seconds since the epoch or as an ISO 8601 formatted " \
"string (yyyymmddThhmmss, where the T is a literal)."
static
struct
option
pk_options
[]
=
{
{
"--timestamp"
,
pk_timestamp
,
"The creation time. "
TIMESTAMP_HELP
},
{
""
,
pk_search_terms
,
"The key to copy the creation time and public key parameters from."
},
{
NULL
,
NULL
,
"Example:
\n\n
"
" $ gpgcompose --public-key $KEYID --user-id
\"
USERID
\"
\\\n
"
" | gpg2 --list-packets"
}
};
static
int
public_key
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
gpg_error_t
err
;
iobuf_t
out
=
cookie
;
PKT_public_key
*
pk
;
int
c
;
int
processed
;
int
t
=
(
strcmp
(
option
,
"--public-key"
)
==
0
?
PKT_PUBLIC_KEY
:
PKT_PUBLIC_SUBKEY
);
(
void
)
option
;
pk
=
xmalloc_clear
(
sizeof
(
*
pk
));
pk
->
version
=
4
;
c
=
add_component
(
t
,
pk
);
processed
=
process_options
(
option
,
major_options
,
pk_options
,
pk
,
global_options
,
NULL
,
argc
,
argv
);
if
(
!
pk
->
pubkey_algo
)
log_fatal
(
"%s: key to extract public key parameters from not given"
,
option
);
/* Clear the keyid in case we updated one of the relevant fields
after accessing it. */
pk
->
keyid
[
0
]
=
pk
->
keyid
[
1
]
=
0
;
err
=
build_packet
(
out
,
&
components
[
c
]);
if
(
err
)
log_fatal
(
"serializing %s packet: %s
\n
"
,
t
==
PKT_PUBLIC_KEY
?
"public key"
:
"subkey"
,
gpg_strerror
(
err
));
debug
(
"Wrote %s packet:
\n
"
,
t
==
PKT_PUBLIC_KEY
?
"public key"
:
"subkey"
);
dump_component
(
&
components
[
c
]);
return
processed
;
}
struct
siginfo
{
/* Key with which to sign. */
kbnode_t
issuer_kb
;
PKT_public_key
*
issuer_pk
;
/* Overrides the issuer's key id. */
u32
issuer_keyid
[
2
];
/* Sets the issuer's keyid to the primary key's key id. */
int
issuer_keyid_self
;
/* Key to sign. */
PKT_public_key
*
pk
;
/* Subkey to sign. */
PKT_public_key
*
sk
;
/* User id to sign. */
PKT_user_id
*
uid
;
int
class
;
int
digest_algo
;
u32
timestamp
;
u32
key_expiration
;
byte
*
cipher_algorithms
;
int
cipher_algorithms_len
;
byte
*
digest_algorithms
;
int
digest_algorithms_len
;
byte
*
compress_algorithms
;
int
compress_algorithms_len
;
u32
expiration
;
int
exportable_set
;
int
exportable
;
int
revocable_set
;
int
revocable
;
int
trust_level_set
;
byte
trust_args
[
2
];
char
*
trust_scope
;
struct
revocation_key
*
revocation_key
;
int
nrevocation_keys
;
struct
notation
*
notations
;
byte
*
key_server_preferences
;
int
key_server_preferences_len
;
char
*
key_server
;
int
primary_user_id_set
;
int
primary_user_id
;
char
*
policy_uri
;
byte
*
key_flags
;
int
key_flags_len
;
char
*
signers_user_id
;
byte
reason_for_revocation_code
;
char
*
reason_for_revocation
;
byte
*
features
;
int
features_len
;
/* Whether to corrupt the signature. */
int
corrupt
;
};
static
int
sig_issuer
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
gpg_error_t
err
;
KEYDB_HANDLE
hd
;
KEYDB_SEARCH_DESC
desc
;
struct
siginfo
*
si
=
cookie
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s KEYID
\n
"
,
option
);
if
(
si
->
issuer_pk
)
log_fatal
(
"%s: multiple keys provided
\n
"
,
option
);
err
=
classify_user_id
(
argv
[
0
],
&
desc
,
0
);
if
(
err
)
log_fatal
(
"search terms '%s': %s
\n
"
,
argv
[
0
],
gpg_strerror
(
err
));
hd
=
keydb_new
();
err
=
keydb_search
(
hd
,
&
desc
,
1
,
NULL
);
if
(
err
)
log_fatal
(
"looking up '%s': %s
\n
"
,
argv
[
0
],
gpg_strerror
(
err
));
err
=
keydb_get_keyblock
(
hd
,
&
si
->
issuer_kb
);
if
(
err
)
log_fatal
(
"retrieving keyblock for '%s': %s
\n
"
,
argv
[
0
],
gpg_strerror
(
err
));
keydb_release
(
hd
);
si
->
issuer_pk
=
si
->
issuer_kb
->
pkt
->
pkt
.
public_key
;
return
1
;
}
static
int
sig_issuer_keyid
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
gpg_error_t
err
;
KEYDB_SEARCH_DESC
desc
;
struct
siginfo
*
si
=
cookie
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s KEYID|self
\n
"
,
option
);
if
(
si
->
issuer_keyid
[
0
]
||
si
->
issuer_keyid
[
1
]
||
si
->
issuer_keyid_self
)
log_fatal
(
"%s given multiple times.
\n
"
,
option
);
if
(
strcasecmp
(
argv
[
0
],
"self"
)
==
0
)
{
si
->
issuer_keyid_self
=
1
;
return
1
;
}
err
=
classify_user_id
(
argv
[
0
],
&
desc
,
0
);
if
(
err
)
log_fatal
(
"search terms '%s': %s
\n
"
,
argv
[
0
],
gpg_strerror
(
err
));
if
(
desc
.
mode
!=
KEYDB_SEARCH_MODE_LONG_KID
)
log_fatal
(
"%s is not a valid long key id.
\n
"
,
argv
[
0
]);
keyid_copy
(
si
->
issuer_keyid
,
desc
.
u
.
kid
);
return
1
;
}
static
int
sig_pk
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
i
;
char
*
tail
=
NULL
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s COMPONENT_INDEX
\n
"
,
option
);
errno
=
0
;
i
=
strtoul
(
argv
[
0
],
&
tail
,
10
);
if
(
errno
||
(
tail
&&
*
tail
))
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
if
(
i
>=
ncomponents
)
log_fatal
(
"%d: No such component (have %d components so far)
\n
"
,
i
,
ncomponents
);
if
(
!
(
components
[
i
].
pkttype
==
PKT_PUBLIC_KEY
||
components
[
i
].
pkttype
==
PKT_PUBLIC_SUBKEY
))
log_fatal
(
"Component %d is not a public key or a subkey."
,
i
);
if
(
strcmp
(
option
,
"--pk"
)
==
0
)
{
if
(
si
->
pk
)
log_fatal
(
"%s already given.
\n
"
,
option
);
si
->
pk
=
components
[
i
].
pkt
.
public_key
;
}
else
if
(
strcmp
(
option
,
"--sk"
)
==
0
)
{
if
(
si
->
sk
)
log_fatal
(
"%s already given.
\n
"
,
option
);
si
->
sk
=
components
[
i
].
pkt
.
public_key
;
}
else
log_fatal
(
"Cannot handle %s
\n
"
,
option
);
return
1
;
}
static
int
sig_user_id
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
i
;
char
*
tail
=
NULL
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s COMPONENT_INDEX
\n
"
,
option
);
if
(
si
->
uid
)
log_fatal
(
"%s already given.
\n
"
,
option
);
errno
=
0
;
i
=
strtoul
(
argv
[
0
],
&
tail
,
10
);
if
(
errno
||
(
tail
&&
*
tail
))
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
if
(
i
>=
ncomponents
)
log_fatal
(
"%d: No such component (have %d components so far)
\n
"
,
i
,
ncomponents
);
if
(
!
(
components
[
i
].
pkttype
!=
PKT_USER_ID
||
components
[
i
].
pkttype
==
PKT_ATTRIBUTE
))
log_fatal
(
"Component %d is not a public key or a subkey."
,
i
);
si
->
uid
=
components
[
i
].
pkt
.
user_id
;
return
1
;
}
static
int
sig_class
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
i
;
char
*
tail
=
NULL
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s CLASS
\n
"
,
option
);
errno
=
0
;
i
=
strtoul
(
argv
[
0
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
))
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
si
->
class
=
i
;
return
1
;
}
static
int
sig_digest
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
i
;
char
*
tail
=
NULL
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s DIGEST_ALGO
\n
"
,
option
);
errno
=
0
;
i
=
strtoul
(
argv
[
0
],
&
tail
,
10
);
if
(
errno
||
(
tail
&&
*
tail
))
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
si
->
digest_algo
=
i
;
return
1
;
}
static
int
sig_timestamp
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
char
*
tail
=
NULL
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s TIMESTAMP
\n
"
,
option
);
errno
=
0
;
si
->
timestamp
=
parse_timestamp
(
argv
[
0
],
&
tail
);
if
(
errno
||
(
tail
&&
*
tail
))
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
return
1
;
}
static
int
sig_expiration
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
is_expiration
=
strcmp
(
option
,
"--expiration"
)
==
0
;
u32
*
i
=
is_expiration
?
&
si
->
expiration
:
&
si
->
key_expiration
;
if
(
!
is_expiration
)
log_assert
(
strcmp
(
option
,
"--key-expiration"
)
==
0
);
if
(
argc
==
0
)
log_fatal
(
"Usage: %s DURATION
\n
"
,
option
);
*
i
=
parse_expire_string
(
argv
[
0
]);
if
(
*
i
==
(
u32
)
-1
)
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
return
1
;
}
static
int
sig_int_list
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
nvalues
=
1
;
char
*
values
=
xmalloc
(
nvalues
*
sizeof
(
values
[
0
]));
char
*
tail
=
argv
[
0
];
int
i
;
byte
**
a
;
int
*
n
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s VALUE[,VALUE...]
\n
"
,
option
);
for
(
i
=
0
;
tail
&&
*
tail
;
i
++
)
{
int
v
;
char
*
old_tail
=
tail
;
errno
=
0
;
v
=
strtol
(
tail
,
&
tail
,
0
);
if
(
errno
||
old_tail
==
tail
||
(
tail
&&
!
(
*
tail
==
','
||
*
tail
==
0
)))
log_fatal
(
"Invalid value passed to %s (%s). "
"Expected a list of comma separated numbers
\n
"
,
option
,
argv
[
0
]);
if
(
!
(
0
<=
v
&&
v
<=
255
))
log_fatal
(
"%s: %d is out of range (Expected: 0-255)
\n
"
,
option
,
v
);
if
(
i
==
nvalues
)
{
nvalues
*=
2
;
values
=
xrealloc
(
values
,
nvalues
*
sizeof
(
values
[
0
]));
}
values
[
i
]
=
v
;
if
(
*
tail
==
','
)
tail
++
;
else
log_assert
(
*
tail
==
0
);
}
if
(
strcmp
(
"--cipher-algos"
,
option
)
==
0
)
{
a
=
&
si
->
cipher_algorithms
;
n
=
&
si
->
cipher_algorithms_len
;
}
else
if
(
strcmp
(
"--digest-algos"
,
option
)
==
0
)
{
a
=
&
si
->
digest_algorithms
;
n
=
&
si
->
digest_algorithms_len
;
}
else
if
(
strcmp
(
"--compress-algos"
,
option
)
==
0
)
{
a
=
&
si
->
compress_algorithms
;
n
=
&
si
->
compress_algorithms_len
;
}
else
log_fatal
(
"Cannot handle %s
\n
"
,
option
);
if
(
*
a
)
log_fatal
(
"Option %s given multiple times.
\n
"
,
option
);
*
a
=
values
;
*
n
=
i
;
return
1
;
}
static
int
sig_flag
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
range
[
2
]
=
{
0
,
255
};
char
*
tail
;
int
v
;
if
(
strcmp
(
option
,
"--primary-user-id"
)
==
0
)
range
[
1
]
=
1
;
if
(
argc
<=
1
)
{
if
(
range
[
0
]
==
0
&&
range
[
1
]
==
1
)
log_fatal
(
"Usage: %s 0|1
\n
"
,
option
);
else
log_fatal
(
"Usage: %s %d-%d
\n
"
,
option
,
range
[
0
],
range
[
1
]);
}
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
(
range
[
0
]
<=
v
&&
v
<=
range
[
1
]))
log_fatal
(
"Invalid value passed to %s (%s). Expected %d-%d
\n
"
,
option
,
argv
[
0
],
range
[
0
],
range
[
1
]);
if
(
strcmp
(
option
,
"--exportable"
)
==
0
)
{
si
->
exportable_set
=
1
;
si
->
exportable
=
v
;
}
else
if
(
strcmp
(
option
,
"--revocable"
)
==
0
)
{
si
->
revocable_set
=
1
;
si
->
revocable
=
v
;
}
else
if
(
strcmp
(
option
,
"--primary-user-id"
)
==
0
)
{
si
->
primary_user_id_set
=
1
;
si
->
primary_user_id
=
v
;
}
else
log_fatal
(
"Cannot handle %s
\n
"
,
option
);
return
1
;
}
static
int
sig_trust_level
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
i
;
char
*
tail
;
if
(
argc
<=
1
)
log_fatal
(
"Usage: %s DEPTH TRUST_AMOUNT
\n
"
,
option
);
for
(
i
=
0
;
i
<
sizeof
(
si
->
trust_args
)
/
sizeof
(
si
->
trust_args
[
0
]);
i
++
)
{
int
v
;
errno
=
0
;
v
=
strtol
(
argv
[
i
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
(
0
<=
v
&&
v
<=
255
))
log_fatal
(
"Invalid value passed to %s (%s). Expected 0-255
\n
"
,
option
,
argv
[
i
]);
si
->
trust_args
[
i
]
=
v
;
}
si
->
trust_level_set
=
1
;
return
2
;
}
static
int
sig_string_arg
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
char
*
p
=
argv
[
0
];
char
**
s
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s STRING
\n
"
,
option
);
if
(
strcmp
(
option
,
"--trust-scope"
)
==
0
)
s
=
&
si
->
trust_scope
;
else
if
(
strcmp
(
option
,
"--key-server"
)
==
0
)
s
=
&
si
->
key_server
;
else
if
(
strcmp
(
option
,
"--signers-user-id"
)
==
0
)
s
=
&
si
->
signers_user_id
;
else
if
(
strcmp
(
option
,
"--policy-uri"
)
==
0
)
s
=
&
si
->
policy_uri
;
else
log_fatal
(
"Cannot handle %s
\n
"
,
option
);
if
(
*
s
)
log_fatal
(
"%s already given.
\n
"
,
option
);
*
s
=
xstrdup
(
p
);
return
1
;
}
static
int
sig_revocation_key
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
gpg_error_t
err
;
struct
siginfo
*
si
=
cookie
;
int
v
;
char
*
tail
;
PKT_public_key
pk
;
struct
revocation_key
*
revkey
;
if
(
argc
<
2
)
log_fatal
(
"Usage: %s CLASS KEYID
\n
"
,
option
);
memset
(
&
pk
,
0
,
sizeof
(
pk
));
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
16
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
(
0
<=
v
&&
v
<=
255
))
log_fatal
(
"%s: Invalid class value (%s). Expected 0-255
\n
"
,
option
,
argv
[
0
]);
pk
.
req_usage
=
PUBKEY_USAGE_SIG
;
err
=
get_pubkey_byname
(
NULL
,
NULL
,
&
pk
,
argv
[
1
],
NULL
,
NULL
,
1
,
1
);
if
(
err
)
log_fatal
(
"looking up key %s: %s
\n
"
,
argv
[
1
],
gpg_strerror
(
err
));
si
->
nrevocation_keys
++
;
si
->
revocation_key
=
xrealloc
(
si
->
revocation_key
,
si
->
nrevocation_keys
*
sizeof
(
*
si
->
revocation_key
));
revkey
=
&
si
->
revocation_key
[
si
->
nrevocation_keys
-
1
];
revkey
->
class
=
v
;
revkey
->
algid
=
pk
.
pubkey_algo
;
fingerprint_from_pk
(
&
pk
,
revkey
->
fpr
,
NULL
);
release_public_key_parts
(
&
pk
);
return
2
;
}
static
int
sig_notation
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
is_blob
=
strcmp
(
option
,
"--notation"
)
!=
0
;
struct
notation
*
notation
;
char
*
p
=
argv
[
0
];
int
p_free
=
0
;
char
*
data
;
int
data_size
;
int
data_len
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s [!<]name=value
\n
"
,
option
);
if
((
p
[
0
]
==
'!'
&&
p
[
1
]
==
'<'
)
||
p
[
0
]
==
'<'
)
/* Read from a file. */
{
char
*
filename
=
NULL
;
iobuf_t
in
;
int
prefix
;
if
(
p
[
0
]
==
'<'
)
p
++
;
else
{
/* Remove the '<', which string_to_notation does not
understand, and preserve the '!'. */
p
=
xstrdup
(
&
p
[
1
]);
p_free
=
1
;
p
[
0
]
=
'!'
;
}
filename
=
strchr
(
p
,
'='
);
if
(
!
filename
)
log_fatal
(
"No value specified. Usage: %s [!<]name=value
\n
"
,
option
);
filename
++
;
prefix
=
(
size_t
)
filename
-
(
size_t
)
p
;
errno
=
0
;
in
=
iobuf_open
(
filename
);
if
(
!
in
)
log_fatal
(
"Opening '%s': %s
\n
"
,
filename
,
errno
?
strerror
(
errno
)
:
"unknown error"
);
/* A notation can be at most about a few dozen bytes short of
64k. Since this is relatively small, we just allocate that
much instead of trying to dynamically size a buffer. */
data_size
=
64
*
1024
;
data
=
xmalloc
(
data_size
);
log_assert
(
prefix
<=
data_size
);
memcpy
(
data
,
p
,
prefix
);
data_len
=
iobuf_read
(
in
,
&
data
[
prefix
],
data_size
-
prefix
-
1
);
if
(
data_len
==
-1
)
/* EOF => 0 bytes read. */
data_len
=
0
;
if
(
data_len
==
data_size
-
prefix
-
1
)
/* Technically, we should do another read and check for EOF,
but what's one byte more or less? */
log_fatal
(
"Notation data doesn't fit in the packet.
\n
"
);
iobuf_close
(
in
);
/* NUL terminate it. */
data
[
prefix
+
data_len
]
=
0
;
if
(
p_free
)
xfree
(
p
);
p
=
data
;
p_free
=
1
;
data
=
&
p
[
prefix
];
if
(
is_blob
)
p
[
prefix
-
1
]
=
0
;
}
else
if
(
is_blob
)
{
data
=
strchr
(
p
,
'='
);
if
(
!
data
)
{
data
=
p
;
data_len
=
0
;
}
else
{
p
=
xstrdup
(
p
);
p_free
=
1
;
data
=
strchr
(
p
,
'='
);
log_assert
(
data
);
/* NUL terminate the name. */
*
data
=
0
;
data
++
;
data_len
=
strlen
(
data
);
}
}
if
(
is_blob
)
notation
=
blob_to_notation
(
p
,
data
,
data_len
);
else
notation
=
string_to_notation
(
p
,
1
);
if
(
!
notation
)
log_fatal
(
"creating notation: an unknown error occured.
\n
"
);
notation
->
next
=
si
->
notations
;
si
->
notations
=
notation
;
if
(
p_free
)
xfree
(
p
);
return
1
;
}
static
int
sig_big_endian_arg
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
char
*
p
=
argv
[
0
];
int
i
;
int
l
;
char
*
bytes
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s HEXDIGITS
\n
"
,
option
);
/* Skip a leading "0x". */
if
(
p
[
0
]
==
'0'
&&
p
[
1
]
==
'x'
)
p
+=
2
;
for
(
i
=
0
;
i
<
strlen
(
p
);
i
++
)
if
(
!
hexdigitp
(
&
p
[
i
]))
log_fatal
(
"%s: argument ('%s') must consist of hex digits.
\n
"
,
option
,
p
);
if
(
strlen
(
p
)
%
2
!=
0
)
log_fatal
(
"%s: argument ('%s') must contain an even number of hex digits.
\n
"
,
option
,
p
);
l
=
strlen
(
p
)
/
2
;
bytes
=
xmalloc
(
l
);
hex2bin
(
p
,
bytes
,
l
);
if
(
strcmp
(
option
,
"--key-server-preferences"
)
==
0
)
{
if
(
si
->
key_server_preferences
)
log_fatal
(
"%s given multiple times.
\n
"
,
option
);
si
->
key_server_preferences
=
bytes
;
si
->
key_server_preferences_len
=
l
;
}
else
if
(
strcmp
(
option
,
"--key-flags"
)
==
0
)
{
if
(
si
->
key_flags
)
log_fatal
(
"%s given multiple times.
\n
"
,
option
);
si
->
key_flags
=
bytes
;
si
->
key_flags_len
=
l
;
}
else
if
(
strcmp
(
option
,
"--features"
)
==
0
)
{
if
(
si
->
features
)
log_fatal
(
"%s given multiple times.
\n
"
,
option
);
si
->
features
=
bytes
;
si
->
features_len
=
l
;
}
else
log_fatal
(
"Cannot handle %s
\n
"
,
option
);
return
1
;
}
static
int
sig_reason_for_revocation
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
v
;
char
*
tail
;
if
(
argc
<
2
)
log_fatal
(
"Usage: %s REASON_CODE REASON_STRING
\n
"
,
option
);
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
16
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
(
0
<=
v
&&
v
<=
255
))
log_fatal
(
"%s: Invalid reason code (%s). Expected 0-255
\n
"
,
option
,
argv
[
0
]);
if
(
si
->
reason_for_revocation
)
log_fatal
(
"%s given multiple times.
\n
"
,
option
);
si
->
reason_for_revocation_code
=
v
;
si
->
reason_for_revocation
=
xstrdup
(
argv
[
1
]);
return
2
;
}
static
int
sig_corrupt
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
(
void
)
option
;
(
void
)
argc
;
(
void
)
argv
;
(
void
)
cookie
;
si
->
corrupt
=
1
;
return
0
;
}
static
struct
option
sig_options
[]
=
{
{
"--issuer"
,
sig_issuer
,
"The key to use to generate the signature."
},
{
"--issuer-keyid"
,
sig_issuer_keyid
,
"Set the issuer's key id. This is useful for creating a "
"self-signature. As a special case, the value
\"
self
\"
refers "
"to the primary key's key id. "
"(RFC 4880, Section 5.2.3.5)"
},
{
"--pk"
,
sig_pk
,
"The primary keyas an index into the components (keys and uids) "
"created so far where the first component has the index 0."
},
{
"--sk"
,
sig_pk
,
"The subkey as an index into the components (keys and uids) created "
"so far where the first component has the index 0. Only needed for "
"0x18, 0x19, and 0x28 signatures."
},
{
"--user-id"
,
sig_user_id
,
"The user id as an index into the components (keys and uids) created "
"so far where the first component has the index 0. Only needed for "
"0x10-0x13 and 0x30 signatures."
},
{
"--class"
,
sig_class
,
"The signature's class. Valid values are "
"0x10-0x13 (user id and primary-key certification), "
"0x18 (subkey binding), "
"0x19 (primary key binding), "
"0x1f (direct primary key signature), "
"0x20 (key revocation), "
"0x28 (subkey revocation), and "
"0x30 (certification revocation)."
},
{
"--digest"
,
sig_digest
,
"The digest algorithm"
},
{
"--timestamp"
,
sig_timestamp
,
"The signature's creation time. "
TIMESTAMP_HELP
" 0 means now. "
"(RFC 4880, Section 5.2.3.4)"
},
{
"--key-expiration"
,
sig_expiration
,
"The number of days until the associated key expires. To specify "
"seconds, prefix the value with
\"
seconds=
\"
. It is also possible "
"to use 'y', 'm' and 'w' as simple multipliers. For instance, 2y "
"means 2 years, etc. "
"(RFC 4880, Section 5.2.3.6)"
},
{
"--cipher-algos"
,
sig_int_list
,
"A comma separated list of the preferred cipher algorithms (identified by "
"their number, see RFC 4880, Section 9). "
"(RFC 4880, Section 5.2.3.7)"
},
{
"--digest-algos"
,
sig_int_list
,
"A comma separated list of the preferred algorithms (identified by "
"their number, see RFC 4880, Section 9). "
"(RFC 4880, Section 5.2.3.8)"
},
{
"--compress-algos"
,
sig_int_list
,
"A comma separated list of the preferred algorithms (identified by "
"their number, see RFC 4880, Section 9)."
"(RFC 4880, Section 5.2.3.9)"
},
{
"--expiration"
,
sig_expiration
,
"The number of days until the signature expires. To specify seconds, "
"prefix the value with
\"
seconds=
\"
. It is also possible to use 'y', "
"'m' and 'w' as simple multipliers. For instance, 2y means 2 years, "
"etc. "
"(RFC 4880, Section 5.2.3.10)"
},
{
"--exportable"
,
sig_flag
,
"Mark this signature as exportable (1) or local (0). "
"(RFC 4880, Section 5.2.3.11)"
},
{
"--revocable"
,
sig_flag
,
"Mark this signature as revocable (1, revocations are ignored) "
"or non-revocable (0). "
"(RFC 4880, Section 5.2.3.12)"
},
{
"--trust-level"
,
sig_trust_level
,
"Set the trust level. This takes two integer arguments (0-255): "
"the trusted-introducer level and the degree of trust. "
"(RFC 4880, Section 5.2.3.13.)"
},
{
"--trust-scope"
,
sig_string_arg
,
"A regular expression that limits the scope of --trust-level. "
"(RFC 4880, Section 5.2.3.14.)"
},
{
"--revocation-key"
,
sig_revocation_key
,
"Specify a designated revoker. Takes two arguments: the class "
"(normally 0x80 or 0xC0 (sensitive)) and the key id of the "
"designatured revoker. May be given multiple times. "
"(RFC 4880, Section 5.2.3.15)"
},
{
"--notation"
,
sig_notation
,
"Add a human-readable notation of the form
\"
[!<]name=value
\"
where "
"
\"
!
\"
means that the critical flag should be set and
\"
<
\"
means "
"that VALUE is a file to read the data from. "
"(RFC 4880, Section 5.2.3.16)"
},
{
"--notation-binary"
,
sig_notation
,
"Add a binary notation of the form
\"
[!<]name=value
\"
where "
"
\"
!
\"
means that the critical flag should be set and
\"
<
\"
means "
"that VALUE is a file to read the data from. "
"(RFC 4880, Section 5.2.3.16)"
},
{
"--key-server-preferences"
,
sig_big_endian_arg
,
"Big-endian number encoding the key server preferences. "
"(RFC 4880, Section 5.2.3.17)"
},
{
"--key-server"
,
sig_string_arg
,
"The preferred key server. (RFC 4880, Section 5.2.3.18)"
},
{
"--primary-user-id"
,
sig_flag
,
"Sets the primary user id flag. (RFC 4880, Section 5.2.3.19)"
},
{
"--policy-uri"
,
sig_string_arg
,
"URI of a document that describes the issuer's signing policy. "
"(RFC 4880, Section 5.2.3.20)"
},
{
"--key-flags"
,
sig_big_endian_arg
,
"Big-endian number encoding the key flags. "
"(RFC 4880, Section 5.2.3.21)"
},
{
"--signers-user-id"
,
sig_string_arg
,
"The user id (as a string) responsible for the signing. "
"(RFC 4880, Section 5.2.3.22)"
},
{
"--reason-for-revocation"
,
sig_reason_for_revocation
,
"Takes two arguments: a reason for revocation code and a "
"user-provided string. "
"(RFC 4880, Section 5.2.3.23)"
},
{
"--features"
,
sig_big_endian_arg
,
"Big-endian number encoding the feature flags. "
"(RFC 4880, Section 5.2.3.24)"
},
{
"--signature-target"
,
NULL
,
"Takes three arguments: the target signature's public key algorithm "
" (as an integer), the hash algorithm (as an integer) and the hash "
" (as a hexadecimal string). "
"(RFC 4880, Section 5.2.3.25)"
},
{
"--embedded-signature"
,
NULL
,
"An embedded signature. This must be immediately followed by a "
"signature packet (created using --signature ...) or a filename "
"containing the packet."
"(RFC 4880, Section 5.2.3.26)"
},
{
"--hashed"
,
NULL
,
"The following attributes will be placed in the hashed area of "
"the signature. (This is the default and it reset at the end of"
"each signature.)"
},
{
"--unhashed"
,
NULL
,
"The following attributes will be placed in the unhashed area of "
"the signature (and thus not integrity protected)."
},
{
"--corrupt"
,
sig_corrupt
,
"Corrupt the signature."
},
{
NULL
,
NULL
,
"Example:
\n\n
"
" $ gpgcompose --public-key $KEYID --user-id USERID
\\\n
"
" --signature --class 0x10 --issuer $KEYID --issuer-keyid self
\\\n
"
" | gpg2 --list-packets"
}
};
static
int
mksubpkt_callback
(
PKT_signature
*
sig
,
void
*
cookie
)
{
struct
siginfo
*
si
=
cookie
;
int
i
;
if
(
si
->
key_expiration
)
{
char
buf
[
4
];
buf
[
0
]
=
(
si
->
key_expiration
>>
24
)
&
0xff
;
buf
[
1
]
=
(
si
->
key_expiration
>>
16
)
&
0xff
;
buf
[
2
]
=
(
si
->
key_expiration
>>
8
)
&
0xff
;
buf
[
3
]
=
si
->
key_expiration
&
0xff
;
build_sig_subpkt
(
sig
,
SIGSUBPKT_KEY_EXPIRE
,
buf
,
4
);
}
if
(
si
->
cipher_algorithms
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_SYM
,
si
->
cipher_algorithms
,
si
->
cipher_algorithms_len
);
if
(
si
->
digest_algorithms
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_HASH
,
si
->
digest_algorithms
,
si
->
digest_algorithms_len
);
if
(
si
->
compress_algorithms
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_COMPR
,
si
->
compress_algorithms
,
si
->
compress_algorithms_len
);
if
(
si
->
exportable_set
)
{
char
buf
=
si
->
exportable
;
build_sig_subpkt
(
sig
,
SIGSUBPKT_EXPORTABLE
,
&
buf
,
1
);
}
if
(
si
->
trust_level_set
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_TRUST
,
si
->
trust_args
,
sizeof
(
si
->
trust_args
));
if
(
si
->
trust_scope
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_REGEXP
,
si
->
trust_scope
,
strlen
(
si
->
trust_scope
));
for
(
i
=
0
;
i
<
si
->
nrevocation_keys
;
i
++
)
{
struct
revocation_key
*
revkey
=
&
si
->
revocation_key
[
i
];
gpg_error_t
err
=
keygen_add_revkey
(
sig
,
revkey
);
if
(
err
)
{
u32
keyid
[
2
];
keyid_from_fingerprint
(
revkey
->
fpr
,
20
,
keyid
);
log_fatal
(
"adding revocation key %s: %s
\n
"
,
keystr
(
keyid
),
gpg_strerror
(
err
));
}
}
/* keygen_add_revkey sets revocable=0 so be sure to do this after
adding the rev keys. */
if
(
si
->
revocable_set
)
{
char
buf
=
si
->
revocable
;
build_sig_subpkt
(
sig
,
SIGSUBPKT_REVOCABLE
,
&
buf
,
1
);
}
keygen_add_notations
(
sig
,
si
->
notations
);
if
(
si
->
key_server_preferences
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_KS_FLAGS
,
si
->
key_server_preferences
,
si
->
key_server_preferences_len
);
if
(
si
->
key_server
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_PREF_KS
,
si
->
key_server
,
strlen
(
si
->
key_server
));
if
(
si
->
primary_user_id_set
)
{
char
buf
=
si
->
primary_user_id
;
build_sig_subpkt
(
sig
,
SIGSUBPKT_PRIMARY_UID
,
&
buf
,
1
);
}
if
(
si
->
policy_uri
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_POLICY
,
si
->
policy_uri
,
strlen
(
si
->
policy_uri
));
if
(
si
->
key_flags
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_KEY_FLAGS
,
si
->
key_flags
,
si
->
key_flags_len
);
if
(
si
->
signers_user_id
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_SIGNERS_UID
,
si
->
signers_user_id
,
strlen
(
si
->
signers_user_id
));
if
(
si
->
reason_for_revocation
)
{
int
l
=
1
+
strlen
(
si
->
reason_for_revocation
);
char
buf
[
l
];
buf
[
0
]
=
si
->
reason_for_revocation_code
;
memcpy
(
&
buf
[
1
],
si
->
reason_for_revocation
,
l
-
1
);
build_sig_subpkt
(
sig
,
SIGSUBPKT_REVOC_REASON
,
buf
,
l
);
}
if
(
si
->
features
)
build_sig_subpkt
(
sig
,
SIGSUBPKT_FEATURES
,
si
->
features
,
si
->
features_len
);
return
0
;
}
static
int
signature
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
gpg_error_t
err
;
iobuf_t
out
=
cookie
;
struct
siginfo
si
;
int
processed
;
PKT_public_key
*
pk
;
PKT_signature
*
sig
;
PACKET
pkt
;
u32
keyid_orig
[
2
],
keyid
[
2
];
(
void
)
option
;
memset
(
&
si
,
0
,
sizeof
(
si
));
memset
(
&
pkt
,
0
,
sizeof
(
pkt
));
processed
=
process_options
(
option
,
major_options
,
sig_options
,
&
si
,
global_options
,
NULL
,
argc
,
argv
);
if
(
ncomponents
)
{
int
pkttype
=
components
[
ncomponents
-
1
].
pkttype
;
if
(
pkttype
==
PKT_PUBLIC_KEY
)
{
if
(
!
si
.
class
)
/* Direct key sig. */
si
.
class
=
0x1F
;
}
else
if
(
pkttype
==
PKT_PUBLIC_SUBKEY
)
{
if
(
!
si
.
sk
)
si
.
sk
=
components
[
ncomponents
-
1
].
pkt
.
public_key
;
if
(
!
si
.
class
)
/* Subkey binding sig. */
si
.
class
=
0x18
;
}
else
if
(
pkttype
==
PKT_USER_ID
)
{
if
(
!
si
.
uid
)
si
.
uid
=
components
[
ncomponents
-
1
].
pkt
.
user_id
;
if
(
!
si
.
class
)
/* Certification of a user id and public key packet. */
si
.
class
=
0x10
;
}
}
pk
=
NULL
;
if
(
!
si
.
pk
||
!
si
.
issuer_pk
)
/* No primary key specified. Default to the first one that we
find. */
{
int
i
;
for
(
i
=
0
;
i
<
ncomponents
;
i
++
)
if
(
components
[
i
].
pkttype
==
PKT_PUBLIC_KEY
)
{
pk
=
components
[
i
].
pkt
.
public_key
;
break
;
}
}
if
(
!
si
.
pk
)
{
if
(
!
pk
)
log_fatal
(
"%s: no primary key given and no primary key available"
,
"--pk"
);
si
.
pk
=
pk
;
}
if
(
!
si
.
issuer_pk
)
{
if
(
!
pk
)
log_fatal
(
"%s: no issuer key given and no primary key available"
,
"--issuer"
);
si
.
issuer_pk
=
pk
;
}
if
(
si
.
class
==
0x18
||
si
.
class
==
0x19
||
si
.
class
==
0x28
)
/* Requires the primary key and a subkey. */
{
if
(
!
si
.
sk
)
log_fatal
(
"sig class 0x%x requires a subkey (--sk)
\n
"
,
si
.
class
);
}
else
if
(
si
.
class
==
0x10
||
si
.
class
==
0x11
||
si
.
class
==
0x12
||
si
.
class
==
0x13
||
si
.
class
==
0x30
)
/* Requires the primary key and a user id. */
{
if
(
!
si
.
uid
)
log_fatal
(
"sig class 0x%x requires a uid (--uid)
\n
"
,
si
.
class
);
}
else
if
(
si
.
class
==
0x1F
||
si
.
class
==
0x20
)
/* Just requires the primary key. */
;
else
log_fatal
(
"Unsupported signature class: 0x%x
\n
"
,
si
.
class
);
sig
=
xmalloc_clear
(
sizeof
(
*
sig
));
/* Save SI.ISSUER_PK->KEYID. */
keyid_copy
(
keyid_orig
,
pk_keyid
(
si
.
issuer_pk
));
if
(
si
.
issuer_keyid
[
0
]
||
si
.
issuer_keyid
[
1
])
keyid_copy
(
si
.
issuer_pk
->
keyid
,
si
.
issuer_keyid
);
else
if
(
si
.
issuer_keyid_self
)
{
PKT_public_key
*
pripk
=
primary_key
();
if
(
!
pripk
)
log_fatal
(
"--issuer-keyid self given, but no primary key available.
\n
"
);
keyid_copy
(
si
.
issuer_pk
->
keyid
,
pk_keyid
(
pripk
));
}
/* Changing the issuer's key id is fragile. Check to make sure
make_keysig_packet didn't recompute the keyid. */
keyid_copy
(
keyid
,
si
.
issuer_pk
->
keyid
);
err
=
make_keysig_packet
(
&
sig
,
si
.
pk
,
si
.
uid
,
si
.
sk
,
si
.
issuer_pk
,
si
.
class
,
si
.
digest_algo
,
si
.
timestamp
,
si
.
expiration
,
mksubpkt_callback
,
&
si
,
NULL
);
log_assert
(
keyid_cmp
(
keyid
,
si
.
issuer_pk
->
keyid
)
==
0
);
if
(
err
)
log_fatal
(
"Generating signature: %s
\n
"
,
gpg_strerror
(
err
));
/* Restore SI.PK->KEYID. */
keyid_copy
(
si
.
issuer_pk
->
keyid
,
keyid_orig
);
if
(
si
.
corrupt
)
{
/* Set the top 32-bits to 0xBAD0DEAD. */
int
bits
=
gcry_mpi_get_nbits
(
sig
->
data
[
0
]);
gcry_mpi_t
x
=
gcry_mpi_new
(
0
);
gcry_mpi_add_ui
(
x
,
x
,
0xBAD0DEAD
);
gcry_mpi_lshift
(
x
,
x
,
bits
>
32
?
bits
-
32
:
bits
);
gcry_mpi_clear_highbit
(
sig
->
data
[
0
],
bits
>
32
?
bits
-
32
:
0
);
gcry_mpi_add
(
sig
->
data
[
0
],
sig
->
data
[
0
],
x
);
gcry_mpi_release
(
x
);
}
pkt
.
pkttype
=
PKT_SIGNATURE
;
pkt
.
pkt
.
signature
=
sig
;
err
=
build_packet
(
out
,
&
pkt
);
if
(
err
)
log_fatal
(
"serializing public key packet: %s
\n
"
,
gpg_strerror
(
err
));
debug
(
"Wrote signature packet:
\n
"
);
dump_component
(
&
pkt
);
xfree
(
sig
);
release_kbnode
(
si
.
issuer_kb
);
xfree
(
si
.
revocation_key
);
return
processed
;
}
struct
sk_esk_info
{
/* The cipher used for encrypting the session key (when a session
key is used). */
int
cipher
;
/* The cipher used for encryping the SED packet. */
int
sed_cipher
;
/* S2K related data. */
int
hash
;
int
mode
;
int
mode_set
;
byte
salt
[
8
];
int
salt_set
;
int
iterations
;
/* If applying the S2K function to the passphrase is the session key
or if it is the decryption key for the session key. */
int
s2k_is_session_key
;
/* Generate a new, random session key. */
int
new_session_key
;
/* The unencrypted session key. */
int
session_key_len
;
char
*
session_key
;
char
*
password
;
};
static
int
sk_esk_cipher
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
sk_esk_info
*
si
=
cookie
;
char
*
usage
=
"integer|IDEA|3DES|CAST5|BLOWFISH|AES|AES192|AES256|CAMELLIA128|CAMELLIA192|CAMELLIA256"
;
int
cipher
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
strcasecmp
(
argv
[
0
],
"IDEA"
)
==
0
)
cipher
=
CIPHER_ALGO_IDEA
;
else
if
(
strcasecmp
(
argv
[
0
],
"3DES"
)
==
0
)
cipher
=
CIPHER_ALGO_3DES
;
else
if
(
strcasecmp
(
argv
[
0
],
"CAST5"
)
==
0
)
cipher
=
CIPHER_ALGO_CAST5
;
else
if
(
strcasecmp
(
argv
[
0
],
"BLOWFISH"
)
==
0
)
cipher
=
CIPHER_ALGO_BLOWFISH
;
else
if
(
strcasecmp
(
argv
[
0
],
"AES"
)
==
0
)
cipher
=
CIPHER_ALGO_AES
;
else
if
(
strcasecmp
(
argv
[
0
],
"AES192"
)
==
0
)
cipher
=
CIPHER_ALGO_AES192
;
else
if
(
strcasecmp
(
argv
[
0
],
"TWOFISH"
)
==
0
)
cipher
=
CIPHER_ALGO_TWOFISH
;
else
if
(
strcasecmp
(
argv
[
0
],
"CAMELLIA128"
)
==
0
)
cipher
=
CIPHER_ALGO_CAMELLIA128
;
else
if
(
strcasecmp
(
argv
[
0
],
"CAMELLIA192"
)
==
0
)
cipher
=
CIPHER_ALGO_CAMELLIA192
;
else
if
(
strcasecmp
(
argv
[
0
],
"CAMELLIA256"
)
==
0
)
cipher
=
CIPHER_ALGO_CAMELLIA256
;
else
{
char
*
tail
;
int
v
;
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
valid_cipher
(
v
))
log_fatal
(
"Invalid or unsupported value. Usage: %s %s
\n
"
,
option
,
usage
);
cipher
=
v
;
}
if
(
strcmp
(
option
,
"--cipher"
)
==
0
)
{
if
(
si
->
cipher
)
log_fatal
(
"%s given multiple times."
,
option
);
si
->
cipher
=
cipher
;
}
else
if
(
strcmp
(
option
,
"--sed-cipher"
)
==
0
)
{
if
(
si
->
sed_cipher
)
log_fatal
(
"%s given multiple times."
,
option
);
si
->
sed_cipher
=
cipher
;
}
return
1
;
}
static
int
sk_esk_mode
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
sk_esk_info
*
si
=
cookie
;
char
*
usage
=
"integer|simple|salted|iterated"
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
si
->
mode
)
log_fatal
(
"%s given multiple times."
,
option
);
if
(
strcasecmp
(
argv
[
0
],
"simple"
)
==
0
)
si
->
mode
=
0
;
else
if
(
strcasecmp
(
argv
[
0
],
"salted"
)
==
0
)
si
->
mode
=
1
;
else
if
(
strcasecmp
(
argv
[
0
],
"iterated"
)
==
0
)
si
->
mode
=
3
;
else
{
char
*
tail
;
int
v
;
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
(
v
==
0
||
v
==
1
||
v
==
3
))
log_fatal
(
"Invalid or unsupported value. Usage: %s %s
\n
"
,
option
,
usage
);
si
->
mode
=
v
;
}
si
->
mode_set
=
1
;
return
1
;
}
static
int
sk_esk_hash_algorithm
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
sk_esk_info
*
si
=
cookie
;
char
*
usage
=
"integer|MD5|SHA1|RMD160|SHA256|SHA384|SHA512|SHA224"
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
si
->
hash
)
log_fatal
(
"%s given multiple times."
,
option
);
if
(
strcasecmp
(
argv
[
0
],
"MD5"
)
==
0
)
si
->
hash
=
DIGEST_ALGO_MD5
;
else
if
(
strcasecmp
(
argv
[
0
],
"SHA1"
)
==
0
)
si
->
hash
=
DIGEST_ALGO_SHA1
;
else
if
(
strcasecmp
(
argv
[
0
],
"RMD160"
)
==
0
)
si
->
hash
=
DIGEST_ALGO_RMD160
;
else
if
(
strcasecmp
(
argv
[
0
],
"SHA256"
)
==
0
)
si
->
hash
=
DIGEST_ALGO_SHA256
;
else
if
(
strcasecmp
(
argv
[
0
],
"SHA384"
)
==
0
)
si
->
hash
=
DIGEST_ALGO_SHA384
;
else
if
(
strcasecmp
(
argv
[
0
],
"SHA512"
)
==
0
)
si
->
hash
=
DIGEST_ALGO_SHA512
;
else
if
(
strcasecmp
(
argv
[
0
],
"SHA224"
)
==
0
)
si
->
hash
=
DIGEST_ALGO_SHA224
;
else
{
char
*
tail
;
int
v
;
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
(
v
==
DIGEST_ALGO_MD5
||
v
==
DIGEST_ALGO_SHA1
||
v
==
DIGEST_ALGO_RMD160
||
v
==
DIGEST_ALGO_SHA256
||
v
==
DIGEST_ALGO_SHA384
||
v
==
DIGEST_ALGO_SHA512
||
v
==
DIGEST_ALGO_SHA224
))
log_fatal
(
"Invalid or unsupported value. Usage: %s %s
\n
"
,
option
,
usage
);
si
->
hash
=
v
;
}
return
1
;
}
static
int
sk_esk_salt
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
sk_esk_info
*
si
=
cookie
;
char
*
usage
=
"16-HEX-CHARACTERS"
;
char
*
p
=
argv
[
0
];
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
si
->
salt_set
)
log_fatal
(
"%s given multiple times."
,
option
);
if
(
p
[
0
]
==
'0'
&&
p
[
1
]
==
'x'
)
p
+=
2
;
if
(
strlen
(
p
)
!=
16
)
log_fatal
(
"%s: Salt must be exactly 16 hexadecimal characters (have: %zd)
\n
"
,
option
,
strlen
(
p
));
if
(
hex2bin
(
p
,
si
->
salt
,
sizeof
(
si
->
salt
))
==
-1
)
log_fatal
(
"%s: Salt must only contain hexadecimal characters
\n
"
,
option
);
si
->
salt_set
=
1
;
return
1
;
}
static
int
sk_esk_iterations
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
sk_esk_info
*
si
=
cookie
;
char
*
usage
=
"ITERATION-COUNT"
;
char
*
tail
;
int
v
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
)
||
v
<
0
)
log_fatal
(
"%s: Non-negative integer expected.
\n
"
,
option
);
si
->
iterations
=
v
;
return
1
;
}
static
int
sk_esk_session_key
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
sk_esk_info
*
si
=
cookie
;
char
*
usage
=
"HEX-CHARACTERS|auto|none"
;
char
*
p
=
argv
[
0
];
struct
session_key
sk
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
si
->
session_key
||
si
->
s2k_is_session_key
||
si
->
new_session_key
)
log_fatal
(
"%s given multiple times."
,
option
);
if
(
strcasecmp
(
p
,
"none"
)
==
0
)
{
si
->
s2k_is_session_key
=
1
;
return
1
;
}
if
(
strcasecmp
(
p
,
"new"
)
==
0
)
{
si
->
new_session_key
=
1
;
return
1
;
}
if
(
strcasecmp
(
p
,
"auto"
)
==
0
)
return
1
;
sk
=
parse_session_key
(
option
,
p
,
0
);
if
(
si
->
session_key
)
log_fatal
(
"%s given multiple times."
,
option
);
if
(
sk
.
algo
)
si
->
sed_cipher
=
sk
.
algo
;
si
->
session_key_len
=
sk
.
keylen
;
si
->
session_key
=
sk
.
key
;
return
1
;
}
static
int
sk_esk_password
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
sk_esk_info
*
si
=
cookie
;
char
*
usage
=
"PASSWORD"
;
if
(
argc
==
0
)
log_fatal
(
"Usage: --sk-esk %s
\n
"
,
usage
);
if
(
si
->
password
)
log_fatal
(
"%s given multiple times."
,
option
);
si
->
password
=
xstrdup
(
argv
[
0
]);
return
1
;
}
static
struct
option
sk_esk_options
[]
=
{
{
"--cipher"
,
sk_esk_cipher
,
"The encryption algorithm for encrypting the session key. "
"One of IDEA, 3DES, CAST5, BLOWFISH, AES (default), AES192, "
"AES256, TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256."
},
{
"--sed-cipher"
,
sk_esk_cipher
,
"The encryption algorithm for encrypting the SED packet. "
"One of IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, "
"AES256 (default), TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256."
},
{
"--mode"
,
sk_esk_mode
,
"The S2K mode. Either one of the strings
\"
simple
\"
,
\"
salted
\"
"
"or
\"
iterated
\"
or an integer."
},
{
"--hash"
,
sk_esk_hash_algorithm
,
"The hash algorithm to used to derive the key. One of "
"MD5, SHA1 (default), RMD160, SHA256, SHA384, SHA512, or SHA224."
},
{
"--salt"
,
sk_esk_salt
,
"The S2K salt encoded as 16 hexadecimal characters. One needed "
"if the S2K function is in salted or iterated mode."
},
{
"--iterations"
,
sk_esk_iterations
,
"The iteration count. If not provided, a reasonable value is chosen. "
"Note: due to the encoding scheme, not every value is valid. For "
"convenience, the provided value will be rounded appropriately. "
"Only needed if the S2K function is in iterated mode."
},
{
"--session-key"
,
sk_esk_session_key
,
"The session key to be encrypted by the S2K function as a hexadecimal "
"string. If this is
\"
new
\"
, then a new session key is generated."
"If this is
\"
auto
\"
, then either the last session key is "
"used, if the was none, one is generated. If this is
\"
none
\"
, then "
"the session key is the result of applying the S2K algorithms to the "
"password. The session key may be prefaced with an integer and a colon "
"to indicate the cipher to use for the SED packet (making --sed-cipher "
"unnecessary and allowing the direct use of the result of "
"
\"
gpg2 --show-session-key
\"
)."
},
{
""
,
sk_esk_password
,
"The password."
},
{
NULL
,
NULL
,
"Example:
\n\n
"
" $ gpgcompose --sk-esk foobar --encrypted
\\\n
"
" --literal --value foo | gpg2 --list-packets"
}
};
static
int
sk_esk
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
iobuf_t
out
=
cookie
;
gpg_error_t
err
;
int
processed
;
struct
sk_esk_info
si
;
DEK
sesdek
;
DEK
s2kdek
;
PKT_symkey_enc
*
ske
;
PACKET
pkt
;
memset
(
&
si
,
0
,
sizeof
(
si
));
processed
=
process_options
(
option
,
major_options
,
sk_esk_options
,
&
si
,
global_options
,
NULL
,
argc
,
argv
);
if
(
!
si
.
password
)
log_fatal
(
"%s: missing password. Usage: %s PASSWORD"
,
option
,
option
);
/* Fill in defaults, if appropriate. */
if
(
!
si
.
cipher
)
si
.
cipher
=
CIPHER_ALGO_AES
;
if
(
!
si
.
sed_cipher
)
si
.
sed_cipher
=
CIPHER_ALGO_AES256
;
if
(
!
si
.
hash
)
si
.
hash
=
DIGEST_ALGO_SHA1
;
if
(
!
si
.
mode_set
)
/* Salted and iterated. */
si
.
mode
=
3
;
if
(
si
.
mode
!=
0
&&
!
si
.
salt_set
)
/* Generate a salt. */
gcry_randomize
(
si
.
salt
,
8
,
GCRY_STRONG_RANDOM
);
if
(
si
.
mode
==
0
)
{
if
(
si
.
iterations
)
log_info
(
"%s: --iterations provided, but not used for mode=0
\n
"
,
option
);
si
.
iterations
=
0
;
}
else
if
(
!
si
.
iterations
)
si
.
iterations
=
10000
;
memset
(
&
sesdek
,
0
,
sizeof
(
sesdek
));
/* The session key is used to encrypt the SED packet. */
sesdek
.
algo
=
si
.
sed_cipher
;
if
(
si
.
session_key
)
/* Copy the unencrypted session key into SESDEK. */
{
sesdek
.
keylen
=
openpgp_cipher_get_algo_keylen
(
sesdek
.
algo
);
if
(
sesdek
.
keylen
!=
si
.
session_key_len
)
log_fatal
(
"%s: Cipher algorithm requires a %d byte session key, but provided session key is %d bytes."
,
option
,
sesdek
.
keylen
,
si
.
session_key_len
);
log_assert
(
sesdek
.
keylen
<=
sizeof
(
sesdek
.
key
));
memcpy
(
sesdek
.
key
,
si
.
session_key
,
sesdek
.
keylen
);
}
else
if
(
!
si
.
s2k_is_session_key
||
si
.
new_session_key
)
/* We need a session key, but one wasn't provided. Generate it. */
make_session_key
(
&
sesdek
);
/* The encrypted session key needs 1 + SESDEK.KEYLEN bytes of
space. */
ske
=
xmalloc_clear
(
sizeof
(
*
ske
)
+
sesdek
.
keylen
);
ske
->
version
=
4
;
ske
->
cipher_algo
=
si
.
cipher
;
ske
->
s2k
.
mode
=
si
.
mode
;
ske
->
s2k
.
hash_algo
=
si
.
hash
;
log_assert
(
sizeof
(
si
.
salt
)
==
sizeof
(
ske
->
s2k
.
salt
));
memcpy
(
ske
->
s2k
.
salt
,
si
.
salt
,
sizeof
(
ske
->
s2k
.
salt
));
if
(
!
si
.
s2k_is_session_key
)
/* 0 means get the default. */
ske
->
s2k
.
count
=
encode_s2k_iterations
(
si
.
iterations
);
/* Derive the symmetric key that is either the session key or the
key used to encrypt the session key. */
memset
(
&
s2kdek
,
0
,
sizeof
(
s2kdek
));
s2kdek
.
algo
=
si
.
cipher
;
s2kdek
.
keylen
=
openpgp_cipher_get_algo_keylen
(
s2kdek
.
algo
);
err
=
gcry_kdf_derive
(
si
.
password
,
strlen
(
si
.
password
),
ske
->
s2k
.
mode
==
3
?
GCRY_KDF_ITERSALTED_S2K
:
ske
->
s2k
.
mode
==
1
?
GCRY_KDF_SALTED_S2K
:
GCRY_KDF_SIMPLE_S2K
,
ske
->
s2k
.
hash_algo
,
ske
->
s2k
.
salt
,
8
,
S2K_DECODE_COUNT
(
ske
->
s2k
.
count
),
/* The size of the desired key and its
buffer. */
s2kdek
.
keylen
,
s2kdek
.
key
);
if
(
err
)
log_fatal
(
"gcry_kdf_derive failed: %s"
,
gpg_strerror
(
err
));
if
(
si
.
s2k_is_session_key
)
{
ske
->
seskeylen
=
0
;
session_key
=
s2kdek
;
}
else
/* Encrypt the session key using the s2k specifier. */
{
DEK
*
sesdekp
=
&
sesdek
;
/* Now encrypt the session key (or rather, the algorithm used to
encrypt the SED plus the session key) using ENCKEY. */
ske
->
seskeylen
=
1
+
sesdek
.
keylen
;
encrypt_seskey
(
&
s2kdek
,
&
sesdekp
,
ske
->
seskey
);
/* Save the session key for later. */
session_key
=
sesdek
;
}
pkt
.
pkttype
=
PKT_SYMKEY_ENC
;
pkt
.
pkt
.
symkey_enc
=
ske
;
err
=
build_packet
(
out
,
&
pkt
);
if
(
err
)
log_fatal
(
"Serializing sym-key encrypted packet: %s
\n
"
,
gpg_strerror
(
err
));
debug
(
"Wrote sym-key encrypted packet:
\n
"
);
dump_component
(
&
pkt
);
xfree
(
si
.
session_key
);
xfree
(
si
.
password
);
xfree
(
ske
);
return
processed
;
}
struct
pk_esk_info
{
int
session_key_set
;
int
new_session_key
;
int
sed_cipher
;
int
session_key_len
;
char
*
session_key
;
int
throw_keyid
;
char
*
keyid
;
};
static
int
pk_esk_session_key
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
pk_esk_info
*
pi
=
cookie
;
char
*
usage
=
"HEX-CHARACTERS|auto|none"
;
char
*
p
=
argv
[
0
];
struct
session_key
sk
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
pi
->
session_key_set
)
log_fatal
(
"%s given multiple times."
,
option
);
pi
->
session_key_set
=
1
;
if
(
strcasecmp
(
p
,
"new"
)
==
0
)
{
pi
->
new_session_key
=
1
;
return
1
;
}
if
(
strcasecmp
(
p
,
"auto"
)
==
0
)
return
1
;
sk
=
parse_session_key
(
option
,
p
,
0
);
if
(
pi
->
session_key
)
log_fatal
(
"%s given multiple times."
,
option
);
if
(
sk
.
algo
)
pi
->
sed_cipher
=
sk
.
algo
;
pi
->
session_key_len
=
sk
.
keylen
;
pi
->
session_key
=
sk
.
key
;
return
1
;
}
static
int
pk_esk_throw_keyid
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
pk_esk_info
*
pi
=
cookie
;
(
void
)
option
;
(
void
)
argc
;
(
void
)
argv
;
pi
->
throw_keyid
=
1
;
return
0
;
}
static
int
pk_esk_keyid
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
pk_esk_info
*
pi
=
cookie
;
char
*
usage
=
"KEYID"
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
pi
->
keyid
)
log_fatal
(
"Multiple key ids given, but only one is allowed."
);
pi
->
keyid
=
xstrdup
(
argv
[
0
]);
return
1
;
}
static
struct
option
pk_esk_options
[]
=
{
{
"--session-key"
,
pk_esk_session_key
,
"The session key to be encrypted by the S2K function as a hexadecimal "
"string. If this is not given or is
\"
auto
\"
, then the current "
"session key is used. If there is no session key or this is
\"
new
\"
, "
"then a new session key is generated. The session key may be "
"prefaced with an integer and a colon to indicate the cipher to use "
"for the SED packet (making --sed-cipher unnecessary and allowing the "
"direct use of the result of
\"
gpg2 --show-session-key
\"
)."
},
{
"--throw-keyid"
,
pk_esk_throw_keyid
,
"Throw the keyid."
},
{
""
,
pk_esk_keyid
,
"The key id."
},
{
NULL
,
NULL
,
"Example:
\n\n
"
" $ gpgcompose --pk-esk $KEYID --encrypted --literal --value foo
\\\n
"
" | gpg2 --list-packets"
}
};
static
int
pk_esk
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
iobuf_t
out
=
cookie
;
gpg_error_t
err
;
int
processed
;
struct
pk_esk_info
pi
;
PKT_public_key
pk
;
memset
(
&
pi
,
0
,
sizeof
(
pi
));
processed
=
process_options
(
option
,
major_options
,
pk_esk_options
,
&
pi
,
global_options
,
NULL
,
argc
,
argv
);
if
(
!
pi
.
keyid
)
log_fatal
(
"%s: missing keyid. Usage: %s KEYID"
,
option
,
option
);
memset
(
&
pk
,
0
,
sizeof
(
pk
));
pk
.
req_usage
=
PUBKEY_USAGE_ENC
;
err
=
get_pubkey_byname
(
NULL
,
NULL
,
&
pk
,
pi
.
keyid
,
NULL
,
NULL
,
1
,
1
);
if
(
err
)
log_fatal
(
"%s: looking up key %s: %s
\n
"
,
option
,
pi
.
keyid
,
gpg_strerror
(
err
));
if
(
pi
.
sed_cipher
)
/* Have a session key. */
{
session_key
.
algo
=
pi
.
sed_cipher
;
session_key
.
keylen
=
pi
.
session_key_len
;
log_assert
(
session_key
.
keylen
<=
sizeof
(
session_key
.
key
));
memcpy
(
session_key
.
key
,
pi
.
session_key
,
session_key
.
keylen
);
}
if
(
pi
.
new_session_key
||
!
session_key
.
algo
)
{
if
(
!
pi
.
new_session_key
)
/* Default to AES256. */
session_key
.
algo
=
CIPHER_ALGO_AES256
;
make_session_key
(
&
session_key
);
}
err
=
write_pubkey_enc
(
&
pk
,
pi
.
throw_keyid
,
&
session_key
,
out
);
if
(
err
)
log_fatal
(
"%s: writing pk_esk packet for %s: %s
\n
"
,
option
,
pi
.
keyid
,
gpg_strerror
(
err
));
debug
(
"Wrote pk_esk packet for %s
\n
"
,
pi
.
keyid
);
xfree
(
pi
.
keyid
);
xfree
(
pi
.
session_key
);
return
processed
;
}
struct
encinfo
{
int
saw_session_key
;
};
static
int
encrypted_session_key
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
encinfo
*
ei
=
cookie
;
char
*
usage
=
"HEX-CHARACTERS|auto"
;
char
*
p
=
argv
[
0
];
struct
session_key
sk
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s %s
\n
"
,
option
,
usage
);
if
(
ei
->
saw_session_key
)
log_fatal
(
"%s given multiple times."
,
option
);
ei
->
saw_session_key
=
1
;
if
(
strcasecmp
(
p
,
"auto"
)
==
0
)
return
1
;
sk
=
parse_session_key
(
option
,
p
,
1
);
session_key
.
algo
=
sk
.
algo
;
log_assert
(
sk
.
keylen
<=
sizeof
(
session_key
.
key
));
memcpy
(
session_key
.
key
,
sk
.
key
,
sk
.
keylen
);
xfree
(
sk
.
key
);
return
1
;
}
static
struct
option
encrypted_options
[]
=
{
{
"--session-key"
,
encrypted_session_key
,
"The session key to be encrypted by the S2K function as a hexadecimal "
"string. If this is not given or is
\"
auto
\"
, then the last session key "
"is used. If there was none, then an error is raised. The session key "
"must be prefaced with an integer and a colon to indicate the cipher "
"to use (this is format used by
\"
gpg2 --show-session-key
\"
)."
},
{
NULL
,
NULL
,
"After creating the packet, this command clears the current "
"session key.
\n\n
"
"Example: nested encryption packets:
\n\n
"
" $ gpgcompose --sk-esk foo --encrypted-mdc
\\\n
"
" --sk-esk bar --encrypted-mdc
\\\n
"
" --literal --value 123 --encrypted-pop --encrypted-pop | gpg2 -d"
}
};
static
int
encrypted
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
iobuf_t
out
=
cookie
;
int
processed
;
struct
encinfo
ei
;
PKT_encrypted
e
;
cipher_filter_context_t
*
cfx
;
memset
(
&
ei
,
0
,
sizeof
(
ei
));
processed
=
process_options
(
option
,
major_options
,
encrypted_options
,
&
ei
,
global_options
,
NULL
,
argc
,
argv
);
if
(
!
session_key
.
algo
)
log_fatal
(
"%s: no session key configured.
\n
"
,
option
);
memset
(
&
e
,
0
,
sizeof
(
e
));
/* We only need to set E->LEN, E->EXTRALEN (if E->LEN is not
0), and E->NEW_CTB. */
e
.
len
=
0
;
e
.
new_ctb
=
1
;
/* Register the cipher filter. */
cfx
=
xmalloc_clear
(
sizeof
(
*
cfx
));
/* Copy the session key. */
cfx
->
dek
=
xmalloc
(
sizeof
(
*
cfx
->
dek
));
*
cfx
->
dek
=
session_key
;
if
(
do_debug
)
{
char
buf
[
2
*
session_key
.
keylen
+
1
];
debug
(
"session key: algo: %d; keylen: %d; key: %s
\n
"
,
session_key
.
algo
,
session_key
.
keylen
,
bin2hex
(
session_key
.
key
,
session_key
.
keylen
,
buf
));
}
if
(
strcmp
(
option
,
"--encrypted-mdc"
)
==
0
)
cfx
->
dek
->
use_mdc
=
1
;
else
if
(
strcmp
(
option
,
"--encrypted"
)
==
0
)
cfx
->
dek
->
use_mdc
=
0
;
else
log_fatal
(
"%s: option not handled by this function!
\n
"
,
option
);
cfx
->
datalen
=
0
;
filter_push
(
out
,
cipher_filter
,
cfx
,
PKT_ENCRYPTED
,
cfx
->
datalen
==
0
);
debug
(
"Wrote encrypted packet:
\n
"
);
/* Clear the current session key. */
memset
(
&
session_key
,
0
,
sizeof
(
session_key
));
return
processed
;
}
static
int
encrypted_pop
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
iobuf_t
out
=
cookie
;
(
void
)
argc
;
(
void
)
argv
;
if
(
strcmp
(
option
,
"--encrypted-pop"
)
==
0
)
filter_pop
(
out
,
PKT_ENCRYPTED
);
else
if
(
strcmp
(
option
,
"--encrypted-mdc-pop"
)
==
0
)
filter_pop
(
out
,
PKT_ENCRYPTED_MDC
);
else
log_fatal
(
"%s: option not handled by this function!
\n
"
,
option
);
debug
(
"Popped encryption container.
\n
"
);
return
0
;
}
struct
data
{
int
file
;
union
{
char
*
data
;
char
*
filename
;
};
struct
data
*
next
;
};
/* This must be the first member of the struct to be able to use
add_value! */
struct
datahead
{
struct
data
*
head
;
struct
data
**
last_next
;
};
static
int
add_value
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
datahead
*
dh
=
cookie
;
struct
data
*
d
=
xmalloc_clear
(
sizeof
(
struct
data
));
d
->
file
=
strcmp
(
"--file"
,
option
)
==
0
;
if
(
!
d
->
file
)
log_assert
(
strcmp
(
"--value"
,
option
)
==
0
);
if
(
argc
==
0
)
{
if
(
d
->
file
)
log_fatal
(
"Usage: %s FILENAME
\n
"
,
option
);
else
log_fatal
(
"Usage: %s STRING
\n
"
,
option
);
}
if
(
!
dh
->
last_next
)
/* First time through. Initialize DH->LAST_NEXT. */
{
log_assert
(
!
dh
->
head
);
dh
->
last_next
=
&
dh
->
head
;
}
if
(
d
->
file
)
d
->
filename
=
argv
[
0
];
else
d
->
data
=
argv
[
0
];
/* Append it. */
*
dh
->
last_next
=
d
;
dh
->
last_next
=
&
d
->
next
;
return
1
;
}
struct
litinfo
{
/* This must be the first element for add_value to work! */
struct
datahead
data
;
int
timestamp_set
;
u32
timestamp
;
char
mode
;
int
partial_body_length_encoding
;
char
*
name
;
};
static
int
literal_timestamp
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
litinfo
*
li
=
cookie
;
char
*
tail
=
NULL
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s TIMESTAMP
\n
"
,
option
);
errno
=
0
;
li
->
timestamp
=
parse_timestamp
(
argv
[
0
],
&
tail
);
if
(
errno
||
(
tail
&&
*
tail
))
log_fatal
(
"Invalid value passed to %s (%s)
\n
"
,
option
,
argv
[
0
]);
li
->
timestamp_set
=
1
;
return
1
;
}
static
int
literal_mode
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
litinfo
*
li
=
cookie
;
if
(
argc
==
0
||
!
(
strcmp
(
argv
[
0
],
"b"
)
==
0
||
strcmp
(
argv
[
0
],
"t"
)
==
0
||
strcmp
(
argv
[
0
],
"u"
)
==
0
))
log_fatal
(
"Usage: %s [btu]
\n
"
,
option
);
li
->
mode
=
argv
[
0
][
0
];
return
1
;
}
static
int
literal_partial_body_length
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
litinfo
*
li
=
cookie
;
char
*
tail
;
int
v
;
int
range
[
2
]
=
{
0
,
1
};
if
(
argc
<=
1
)
log_fatal
(
"Usage: %s [0|1]
\n
"
,
option
);
errno
=
0
;
v
=
strtol
(
argv
[
0
],
&
tail
,
0
);
if
(
errno
||
(
tail
&&
*
tail
)
||
!
(
range
[
0
]
<=
v
&&
v
<=
range
[
1
]))
log_fatal
(
"Invalid value passed to %s (%s). Expected %d-%d
\n
"
,
option
,
argv
[
0
],
range
[
0
],
range
[
1
]);
li
->
partial_body_length_encoding
=
v
;
return
1
;
}
static
int
literal_name
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
struct
litinfo
*
li
=
cookie
;
if
(
argc
<=
1
)
log_fatal
(
"Usage: %s NAME
\n
"
,
option
);
if
(
strlen
(
argv
[
0
])
>
255
)
log_fatal
(
"%s: name is too long (%zd > 255 characters).
\n
"
,
option
,
strlen
(
argv
[
0
]));
li
->
name
=
argv
[
0
];
return
1
;
}
static
struct
option
literal_options
[]
=
{
{
"--value"
,
add_value
,
"A string to store in the literal packet."
},
{
"--file"
,
add_value
,
"A file to copy into the literal packet."
},
{
"--timestamp"
,
literal_timestamp
,
"The literal packet's time stamp. This defaults to the current time."
},
{
"--mode"
,
literal_mode
,
"The content's mode (normally 'b' (default), 't' or 'u')."
},
{
"--partial-body-length"
,
literal_partial_body_length
,
"Force partial body length encoding."
},
{
"--name"
,
literal_name
,
"The literal's name."
},
{
NULL
,
NULL
,
"Example:
\n\n
"
" $ gpgcompose --literal --value foobar | gpg2 -d"
}
};
static
int
literal
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
iobuf_t
out
=
cookie
;
gpg_error_t
err
;
int
processed
;
struct
litinfo
li
;
PKT_plaintext
*
pt
;
PACKET
pkt
;
struct
data
*
data
;
memset
(
&
li
,
0
,
sizeof
(
li
));
processed
=
process_options
(
option
,
major_options
,
literal_options
,
&
li
,
global_options
,
NULL
,
argc
,
argv
);
if
(
!
li
.
data
.
head
)
log_fatal
(
"%s: no data provided (use --value or --file)"
,
option
);
pt
=
xmalloc_clear
(
sizeof
(
*
pt
)
+
(
li
.
name
?
strlen
(
li
.
name
)
:
0
));
pt
->
new_ctb
=
1
;
if
(
li
.
timestamp_set
)
pt
->
timestamp
=
li
.
timestamp
;
else
/* Default to the current time. */
pt
->
timestamp
=
make_timestamp
();
pt
->
mode
=
li
.
mode
;
if
(
!
pt
->
mode
)
/* Default to binary. */
pt
->
mode
=
'b'
;
if
(
li
.
name
)
{
strcpy
(
pt
->
name
,
li
.
name
);
pt
->
namelen
=
strlen
(
pt
->
name
);
}
pkt
.
pkttype
=
PKT_PLAINTEXT
;
pkt
.
pkt
.
plaintext
=
pt
;
if
(
!
li
.
partial_body_length_encoding
)
/* Compute the amount of data. */
{
pt
->
len
=
0
;
for
(
data
=
li
.
data
.
head
;
data
;
data
=
data
->
next
)
{
if
(
data
->
file
)
{
iobuf_t
in
;
int
overflow
;
off_t
off
;
in
=
iobuf_open
(
data
->
filename
);
if
(
!
in
)
/* An error opening the file. We do error handling
below so just break here. */
{
pt
->
len
=
0
;
break
;
}
off
=
iobuf_get_filelength
(
in
,
&
overflow
);
iobuf_close
(
in
);
if
(
overflow
||
off
==
0
)
/* Length is unknown or there was an error
(unfortunately, iobuf_get_filelength doesn't
distinguish between 0 length files and an error!).
Fall back to partial body mode. */
{
pt
->
len
=
0
;
break
;
}
pt
->
len
+=
off
;
}
else
pt
->
len
+=
strlen
(
data
->
data
);
}
}
err
=
build_packet
(
out
,
&
pkt
);
if
(
err
)
log_fatal
(
"Serializing literal packet: %s
\n
"
,
gpg_strerror
(
err
));
/* Write out the data. */
for
(
data
=
li
.
data
.
head
;
data
;
data
=
data
->
next
)
{
if
(
data
->
file
)
{
iobuf_t
in
;
errno
=
0
;
in
=
iobuf_open
(
data
->
filename
);
if
(
!
in
)
log_fatal
(
"Opening '%s': %s
\n
"
,
data
->
filename
,
errno
?
strerror
(
errno
)
:
"unknown error"
);
iobuf_copy
(
out
,
in
);
if
(
iobuf_error
(
in
))
log_fatal
(
"Reading from %s: %s
\n
"
,
data
->
filename
,
gpg_strerror
(
iobuf_error
(
in
)));
if
(
iobuf_error
(
out
))
log_fatal
(
"Writing literal data from %s: %s
\n
"
,
data
->
filename
,
gpg_strerror
(
iobuf_error
(
out
)));
iobuf_close
(
in
);
}
else
{
err
=
iobuf_write
(
out
,
data
->
data
,
strlen
(
data
->
data
));
if
(
err
)
log_fatal
(
"Writing literal data: %s
\n
"
,
gpg_strerror
(
err
));
}
}
if
(
!
pt
->
len
)
{
/* Disable partial body length mode. */
log_assert
(
pt
->
new_ctb
==
1
);
iobuf_set_partial_body_length_mode
(
out
,
0
);
}
debug
(
"Wrote literal packet:
\n
"
);
dump_component
(
&
pkt
);
while
(
li
.
data
.
head
)
{
data
=
li
.
data
.
head
->
next
;
xfree
(
li
.
data
.
head
);
li
.
data
.
head
=
data
;
}
xfree
(
pt
);
return
processed
;
}
static
int
copy_file
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
char
**
filep
=
cookie
;
if
(
argc
==
0
)
log_fatal
(
"Usage: %s FILENAME
\n
"
,
option
);
*
filep
=
argv
[
0
];
return
1
;
}
static
struct
option
copy_options
[]
=
{
{
""
,
copy_file
,
"Copy the specified file to stdout."
},
{
NULL
,
NULL
,
"Example:
\n\n
"
" $ gpgcompose --copy /etc/hostname
\n\n
"
"This is particularly useful when combined with gpgsplit."
}
};
static
int
copy
(
const
char
*
option
,
int
argc
,
char
*
argv
[],
void
*
cookie
)
{
iobuf_t
out
=
cookie
;
char
*
file
=
NULL
;
iobuf_t
in
;
int
processed
;
processed
=
process_options
(
option
,
major_options
,
copy_options
,
&
file
,
global_options
,
NULL
,
argc
,
argv
);
if
(
!
file
)
log_fatal
(
"Usage: %s FILE
\n
"
,
option
);
errno
=
0
;
in
=
iobuf_open
(
file
);
if
(
!
in
)
log_fatal
(
"Error opening %s: %s.
\n
"
,
file
,
errno
?
strerror
(
errno
)
:
"unknown error"
);
iobuf_copy
(
out
,
in
);
if
(
iobuf_error
(
out
))
log_fatal
(
"Copying data to destination: %s
\n
"
,
gpg_strerror
(
iobuf_error
(
out
)));
if
(
iobuf_error
(
in
))
log_fatal
(
"Reading data from %s: %s
\n
"
,
argv
[
0
],
gpg_strerror
(
iobuf_error
(
in
)));
iobuf_close
(
in
);
return
processed
;
}
int
main
(
int
argc
,
char
*
argv
[])
{
const
char
*
filename
=
"-"
;
iobuf_t
out
;
int
preprocessed
=
1
;
int
processed
;
ctrl_t
ctrl
;
opt
.
homedir
=
default_homedir
();
if
(
!
opt
.
homedir
)
opt
.
homedir
=
""
;
opt
.
ignore_time_conflict
=
1
;
/* Allow notations in the IETF space, for instance. */
opt
.
expert
=
1
;
ctrl
=
xcalloc
(
1
,
sizeof
*
ctrl
);
keydb_add_resource
(
"pubring"
EXTSEP_S
GPGEXT_GPG
,
KEYDB_RESOURCE_FLAG_DEFAULT
);
if
(
argc
==
1
)
/* Nothing to do. */
return
0
;
if
(
strcmp
(
argv
[
1
],
"--output"
)
==
0
||
strcmp
(
argv
[
1
],
"-o"
)
==
0
)
{
filename
=
argv
[
2
];
log_info
(
"Writing to %s
\n
"
,
filename
);
preprocessed
+=
2
;
}
out
=
iobuf_create
(
filename
,
0
);
if
(
!
out
)
log_fatal
(
"Failed to open stdout for writing
\n
"
);
processed
=
process_options
(
NULL
,
NULL
,
major_options
,
out
,
global_options
,
NULL
,
argc
-
preprocessed
,
&
argv
[
preprocessed
]);
if
(
processed
!=
argc
-
preprocessed
)
log_fatal
(
"Didn't process %d options.
\n
"
,
argc
-
preprocessed
-
processed
);
iobuf_close
(
out
);
return
0
;
}
/* Stubs duplicated from gpg.c. */
int
g10_errors_seen
=
0
;
/* Note: This function is used by signal handlers!. */
static
void
emergency_cleanup
(
void
)
{
gcry_control
(
GCRYCTL_TERM_SECMEM
);
}
void
g10_exit
(
int
rc
)
{
gcry_control
(
GCRYCTL_UPDATE_RANDOM_SEED_FILE
);
emergency_cleanup
();
rc
=
rc
?
rc
:
log_get_errorcount
(
0
)
?
2
:
g10_errors_seen
?
1
:
0
;
exit
(
rc
);
}
void
keyedit_menu
(
ctrl_t
ctrl
,
const
char
*
username
,
strlist_t
locusr
,
strlist_t
commands
,
int
quiet
,
int
seckey_check
)
{
(
void
)
ctrl
;
(
void
)
username
;
(
void
)
locusr
;
(
void
)
commands
;
(
void
)
quiet
;
(
void
)
seckey_check
;
}
void
show_basic_key_info
(
KBNODE
keyblock
)
{
(
void
)
keyblock
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Feb 26, 6:58 PM (1 d, 18 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
54/79/b5e9788d246c02e78067b33535a9
Attached To
rG GnuPG
Event Timeline
Log In to Comment