1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
#!/usr/bin/env python
# Copyright (C) 2017 g10 Code GmbH
#
# This file is part of GPGME.
#
# GPGME 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 2 of the License, or
# (at your option) any later version.
#
# GPGME 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 Lesser General
# Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import, print_function, unicode_literals
import gpg
import itertools
import time
import support
support.assert_gpg_version((2, 1, 1))
del absolute_import, print_function, unicode_literals
with support.EphemeralContext() as ctx:
uid_counter = 0
def make_uid():
global uid_counter
uid_counter += 1
return "user{0}@invalid.example.org".format(uid_counter)
def make_key():
uids = [make_uid() for i in range(3)]
res = ctx.create_key(uids[0], certify=True)
key = ctx.get_key(res.fpr)
for u in uids[1:]:
ctx.key_add_uid(key, u)
return key, uids
def check_sigs(key, expected_sigs):
keys = list(
ctx.keylist(
key.fpr,
mode=(gpg.constants.keylist.mode.LOCAL |
gpg.constants.keylist.mode.SIGS)))
assert len(keys) == 1
key_uids = {
uid.uid: [s for s in uid.signatures]
for uid in keys[0].uids
}
expected = list(expected_sigs)
while key_uids and expected:
uid, signing_key, func = expected[0]
match = False
for i, s in enumerate(key_uids[uid]):
if signing_key.fpr.endswith(s.keyid):
if func:
func(s)
match = True
break
if match:
expected.pop(0)
key_uids[uid].pop(i)
if not key_uids[uid]:
del key_uids[uid]
assert not key_uids, "Superfluous signatures: {0}".format(key_uids)
assert not expected, "Missing signatures: {0}".format(expected)
# Simplest case. Sign without any options.
key_a, uids_a = make_key()
key_b, uids_b = make_key()
ctx.signers = [key_a]
def exportable_non_expiring(s):
assert s.exportable
assert s.expires == 0
check_sigs(key_b,
itertools.product(uids_b, [key_b], [exportable_non_expiring]))
ctx.key_sign(key_b)
check_sigs(
key_b,
itertools.product(uids_b, [key_b, key_a], [exportable_non_expiring]))
# Create a non-exportable signature, and explicitly name all uids.
key_c, uids_c = make_key()
ctx.signers = [key_a, key_b]
def non_exportable_non_expiring(s):
assert s.exportable == 0
assert s.expires == 0
ctx.key_sign(key_c, local=True, uids=uids_c)
check_sigs(
key_c,
list(itertools.product(uids_c, [key_c], [exportable_non_expiring])) +
list(
itertools.product(uids_c, [key_b, key_a],
[non_exportable_non_expiring])))
# Create a non-exportable, expiring signature for a single uid.
key_d, uids_d = make_key()
ctx.signers = [key_c]
expires_in = 600
slack = 10
def non_exportable_expiring(s):
assert s.exportable == 0
assert abs(time.time() + expires_in - s.expires) < slack
ctx.key_sign(key_d, local=True, expires_in=expires_in, uids=uids_d[0])
check_sigs(
key_d,
list(itertools.product(uids_d, [key_d], [exportable_non_expiring])) +
list(
itertools.product(uids_d[:1], [key_c], [non_exportable_expiring])))
# Now sign the second in the same fashion, but use a singleton list.
ctx.key_sign(key_d, local=True, expires_in=expires_in, uids=uids_d[1:2])
check_sigs(
key_d,
list(itertools.product(uids_d, [key_d], [exportable_non_expiring])) +
list(
itertools.product(uids_d[:2], [key_c], [non_exportable_expiring])))
|