3 ### Testing key-management functionality
5 ### (c) 2019 Straylight/Edgeware
8 ###----- Licensing notice ---------------------------------------------------
10 ### This file is part of the Python interface to Catacomb.
12 ### Catacomb/Python is free software: you can redistribute it and/or
13 ### modify it under the terms of the GNU General Public License as
14 ### published by the Free Software Foundation; either version 2 of the
15 ### License, or (at your option) any later version.
17 ### Catacomb/Python is distributed in the hope that it will be useful, but
18 ### WITHOUT ANY WARRANTY; without even the implied warranty of
19 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ### General Public License for more details.
22 ### You should have received a copy of the GNU General Public License
23 ### along with Catacomb/Python. If not, write to the Free Software
24 ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 ###--------------------------------------------------------------------------
36 ###--------------------------------------------------------------------------
37 class TestKeyError (U
.TestCase
):
39 def test_keyerror(me
):
41 try: C
.KeyFile("notexist", C
.KOPEN_NOFILE
).newkey(1, "foo")
42 except C
.KeyError: e
= SYS
.exc_info()[1]
43 else: me
.fail("expected `catacomb.KeyError'")
44 me
.assertEqual(e
.err
, C
.KERR_READONLY
)
45 me
.assertEqual(e
.errstring
, "Key file is read-only")
46 me
.assertEqual(e
.args
, (C
.KERR_READONLY
,))
47 me
.assertEqual(str(e
),
48 "KERR_READONLY (%d): Key file is read-only" %
51 me
.assertRaises(TypeError, C
.KeyError)
53 e
= C
.KeyError(C
.KERR_DUPID
, token
)
54 me
.assertEqual(e
.err
, C
.KERR_DUPID
)
55 me
.assertEqual(e
.errstring
, "Key id already exists")
56 me
.assertEqual(e
.args
, (C
.KERR_DUPID
, token
))
58 ###--------------------------------------------------------------------------
59 class TestKeyFile (U
.TestCase
):
63 kf
= C
.KeyFile("t/keyring")
65 ## Check basic attributes.
66 me
.assertEqual(kf
.name
, "t/keyring")
67 me
.assertEqual(kf
.modifiedp
, False)
68 me
.assertEqual(kf
.writep
, False)
69 me
.assertEqual(kf
.filep
, False)
72 me
.assertEqual(set(k
.type for k
in T
.itervalues(kf
)),
73 set(["rsa", "ec", "ec-param", "twofish"]))
74 me
.assertEqual(len(kf
), 4)
78 me
.assertEqual(k
.type, "rsa")
79 me
.assertEqual(k
.id, 0x8599dbab)
80 me
.assertEqual(type(k
.data
), C
.KeyDataStructured
)
81 me
.assertEqual(set(k
.data
), set(["e", "n", "private"]))
82 priv
= k
.data
["private"]
83 me
.assertEqual(type(priv
), C
.KeyDataEncrypted
)
84 me
.assertRaises(C
.KeyError, priv
.unlock
, T
.bin("wrong secret"))
85 priv
= priv
.unlock(T
.bin("very secret"))
86 me
.assertEqual(type(priv
), C
.KeyDataStructured
)
87 me
.assertEqual(set(priv
),
88 set(["p", "q", "d", "d-mod-p", "d-mod-q", "q-inv"]))
89 me
.assertEqual(k
.data
["n"].mp
, priv
["p"].mp
*priv
["q"].mp
)
91 ## This key has an attribute. Poke about at them.
93 me
.assertEqual(len(a
), 1)
94 me
.assertEqual(set(a
), set(["attr"]))
95 me
.assertEqual(a
["attr"], "value")
96 me
.assertRaises(KeyError, lambda: a
["notexist"])
97 me
.assertEqual(a
.get("attr"), "value")
98 me
.assertEqual(a
.get("notexist"), None)
100 ## Check fingerprinting while we're here.
101 for filter in ["-secret", "none"]:
102 h
= C
.sha256(); me
.assertTrue(k
.fingerprint(h
, filter)); fp0
= h
.done()
104 h
.hash(T
.bin("catacomb-key-fingerprint:")) \
106 .hashbuf8(T
.bin(k
.type))
107 h
.hash(k
.data
.encode(filter))
108 for a
in sorted(T
.iterkeys(k
.attr
)):
109 h
.hashbuf8(T
.bin(a
)).hashbuf16(T
.bin(k
.attr
[a
]))
111 me
.assertEqual(fp0
, fp1
)
113 ## Try `ec-param'. This should be fairly easy.
115 me
.assertEqual(k
.tag
, None)
116 me
.assertEqual(k
.id, 0x4a4e1ee7)
117 me
.assertEqual(type(k
.data
), C
.KeyDataStructured
)
118 me
.assertEqual(set(k
.data
), set(["curve"]))
119 curve
= k
.data
["curve"]
120 me
.assertEqual(type(curve
), C
.KeyDataString
)
121 me
.assertEqual(curve
.str, "nist-p256")
123 ## Check qualified-tag lookups.
124 me
.assertRaises(C
.KeyError, kf
.qtag
, "notexist.curve")
125 me
.assertRaises(C
.KeyError, kf
.qtag
, "ec-param.notexist")
126 t
, k
, kd
= kf
.qtag("ec-param.curve")
127 me
.assertEqual(t
, "4a4e1ee7:ec-param.curve")
128 me
.assertEqual(k
.type, "ec-param")
129 me
.assertEqual(type(kd
), C
.KeyDataString
)
130 me
.assertEqual(kd
.str, "nist-p256")
132 ## Try `ec'. A little trickier.
134 me
.assertEqual(k
.tag
, None)
135 me
.assertEqual(k
.id, 0xbd761d35)
136 me
.assertEqual(type(k
.data
), C
.KeyDataStructured
)
137 me
.assertEqual(set(k
.data
), set(["curve", "p", "private"]))
138 curve
= k
.data
["curve"]
139 me
.assertEqual(type(curve
), C
.KeyDataString
)
140 me
.assertEqual(curve
.str, "nist-p256")
141 einfo
= C
.eccurves
[curve
.str]
142 me
.assertEqual(type(k
.data
["p"]), C
.KeyDataECPt
)
144 priv
= k
.data
["private"]
145 me
.assertEqual(type(priv
), C
.KeyDataEncrypted
)
146 me
.assertRaises(C
.KeyError, priv
.unlock
, T
.bin("wrong secret"))
147 priv
= priv
.unlock(T
.bin("super secret"))
148 me
.assertEqual(type(priv
), C
.KeyDataStructured
)
149 me
.assertEqual(set(priv
), set(["x"]))
151 me
.assertEqual(x
*einfo
.G
, X
)
153 ## Finish with `twofish'.
154 k
= kf
.byid(0x60090be2)
155 me
.assertEqual(k
.tag
, None)
156 me
.assertEqual(k
.type, "twofish")
157 me
.assertEqual(type(k
.data
), C
.KeyDataEncrypted
)
158 me
.assertRaises(C
.KeyError, k
.data
.unlock
, T
.bin("wrong secret"))
159 kd
= k
.data
.unlock(T
.bin("not secret"))
160 me
.assertEqual(type(kd
), C
.KeyDataBinary
)
161 me
.assertEqual(kd
.bin
, C
.bytes("d337b98eea24425826df202a6a3d1ef8"
162 "377b71923fe1179451564776da29bb84"))
164 ## Check unsuccessful searches.
165 me
.assertRaises(KeyError, lambda: kf
["notexist"])
166 me
.assertRaises(C
.KeyError, kf
.bytag
, "notexist")
167 me
.assertRaises(C
.KeyError, kf
.bytag
, 12345)
168 me
.assertEqual(kf
.bytag("notexist", fail
= False), None)
169 me
.assertRaises(C
.KeyError, kf
.bytype
, "notexist")
170 me
.assertRaises(TypeError, kf
.bytype
, 12345)
171 me
.assertEqual(kf
.bytype("notexist", fail
= False), None)
172 me
.assertRaises(C
.KeyError, kf
.byid
, 0x12345678)
173 me
.assertEqual(kf
.byid(0x12345678, fail
= False), None)
175 me
.assertRaises(C
.KeyError, kf
.mergeline
, "nowhere", 2, "")
177 ## The keyring should be readonly.
178 me
.assertRaises(C
.KeyError, kf
.newkey
, 0x12345678, "fail")
179 me
.assertRaises(C
.KeyError, setattr, k
, "tag", "foo")
180 me
.assertRaises(C
.KeyError, delattr, k
, "tag")
181 me
.assertRaises(C
.KeyError, kf
.mergeline
, "notexist", 1,
182 "22222222:test integer,public:32519164 forever forever -")
183 me
.assertRaises(C
.KeyError, setattr, k
, "data", C
.KeyDataString("foo"))
185 def test_keywrite(me
):
186 kf
= C
.KeyFile("test", C
.KOPEN_WRITE | C
.KOPEN_NOFILE
)
187 me
.assertEqual(kf
.modifiedp
, False)
191 k
= kf
.newkey(0x11111111, "first", exp
)
192 me
.assertEqual(kf
.modifiedp
, True)
194 me
.assertEqual(k
, kf
[0x11111111])
195 me
.assertEqual(k
.exptime
, exp
)
196 me
.assertEqual(k
.deltime
, exp
)
197 me
.assertRaises(ValueError, setattr, k
, "deltime", C
.KEXP_FOREVER
)
199 me
.assertEqual(k
.data
.str, "<unset>")
201 k
.data
= C
.KeyDataMP(n
)
202 me
.assertEqual(k
.data
.mp
, n
)
203 me
.assertEqual(k
.comment
, None)
206 me
.assertEqual(k
.comment
, c
)
208 me
.assertEqual(k
.comment
, None)
210 me
.assertEqual(k
.comment
, c
)
212 me
.assertEqual(k
.comment
, None)
214 kf
.mergeline("notexist", 1,
215 "22222222:test integer,public:32519164 forever forever -")
217 ###--------------------------------------------------------------------------
218 class TestKeyData (U
.TestCase
):
221 me
.assertEqual(C
.KeyData
.readflags("none"), (0, 0, ""))
222 me
.assertEqual(C
.KeyData
.readflags("ec,public:..."),
223 (C
.KENC_EC | C
.KCAT_PUB
,
224 C
.KF_ENCMASK | C
.KF_CATMASK
,
226 me
.assertEqual(C
.KeyData
.readflags("int,burn"),
227 (C
.KENC_MP | C
.KF_BURN
, C
.KF_ENCMASK | C
.KF_BURN
, ""))
228 me
.assertRaises(C
.KeyError, C
.KeyData
.readflags
, "int,burn?")
229 me
.assertRaises(C
.KeyError, C
.KeyData
.readflags
, "int,ec")
230 me
.assertRaises(C
.KeyError, C
.KeyData
.readflags
, "snork")
231 me
.assertEqual(C
.KeyData
.writeflags(0), "binary,symmetric")
232 me
.assertEqual(C
.KeyData
.writeflags(C
.KENC_EC | C
.KCAT_PUB
), "ec,public")
235 kd
= C
.KeyDataStructured({ "a": C
.KeyDataString("foo", "public"),
236 "b": C
.KeyDataMP(12345, "private"),
237 "c": C
.KeyDataString("bar", "public") })
240 me
.assertEqual(type(kd2
), C
.KeyDataStructured
)
241 me
.assertEqual(set(T
.iterkeys(kd2
)), set(["a", "b", "c"]))
243 kd2
= C
.KeyDataMP(12345, C
.KCAT_PRIV
).copy("private")
245 kd2
= kd
.copy("-secret")
246 me
.assertEqual(type(kd2
), C
.KeyDataStructured
)
247 me
.assertEqual(set(T
.iterkeys(kd2
)), set(["a", "c"]))
249 kd2
= kd
.copy((0, C
.KF_NONSECRET
))
250 me
.assertEqual(type(kd2
), C
.KeyDataStructured
)
251 me
.assertEqual(set(T
.iterkeys(kd2
)), set(["b"]))
253 def check_encode(me
, kd
):
254 me
.assertEqual(C
.KeyData
.decode(kd
.encode()), kd
)
255 me
.assertEqual(C
.KeyData
.read(kd
.write()), (kd
, ""))
258 rng
= T
.detrand("kd-bin")
260 kd
= C
.KeyDataBinary(by
, "symm,burn")
261 me
.assertEqual(kd
.bin
, by
)
265 rng
= T
.detrand("kd-mp")
267 kd
= C
.KeyDataMP(x
, "symm,burn")
268 me
.assertEqual(kd
.mp
, x
)
272 s
= "some random string"
273 kd
= C
.KeyDataString(s
, "symm,burn")
274 me
.assertEqual(kd
.str, s
)
278 rng
= T
.detrand("kd-enc")
280 kd
= C
.KeyDataEncrypted(ct
, "symm")
281 me
.assertEqual(kd
.ct
, ct
)
285 rng
= T
.detrand("kd-ec")
286 Q
= C
.ECPt(rng
.mp(128), rng
.mp(128))
287 kd
= C
.KeyDataECPt(Q
, "symm,burn")
288 me
.assertEqual(kd
.ecpt
, Q
)
292 rng
= T
.detrand("kd-struct")
293 kd
= C
.KeyDataStructured({ "a": C
.KeyDataString("a"),
294 "b": C
.KeyDataString("b") },
295 c
= C
.KeyDataString("c"),
296 d
= C
.KeyDataString("d"))
297 for i
in ["a", "b", "c", "d"]: me
.assertEqual(kd
[i
].str, i
)
298 me
.assertEqual(len(kd
), 4)
300 me
.assertRaises(TypeError, C
.KeyDataStructured
, { "a": "a" })
301 me
.assertRaises(ValueError, C
.KeyDataStructured
,
302 { "a": C
.KeyDataString("a") },
303 a
= C
.KeyDataString("b"))
305 ###--------------------------------------------------------------------------
308 class TestKeyFileMapping (T
.ImmutableMappingTextMixin
):
309 def _mkkey(me
, i
): return i
310 def _getkey(me
, k
): return k
311 def _getvalue(me
, v
): return v
.data
.mp
313 def test_keyfile(me
):
314 kf
= C
.KeyFile("test", C
.KOPEN_WRITE | C
.KOPEN_NOFILE
)
318 kf
.newkey(i
, "k#%d" % i
).data
= C
.KeyDataMP(100 + i
)
320 me
.assertEqual(kf
[1], kf
[1])
322 me
.check_immutable_mapping(kf
, model
)
324 class TestKeyStructMapping (T
.MutableMappingTestMixin
):
325 def _mkvalue(me
, i
): return C
.KeyDataMP(i
)
326 def _getvalue(me
, v
): return v
.mp
328 def test_keystructmap(me
):
329 me
.check_mapping(C
.KeyDataStructured
)
331 class TestKeyAttrMapping (T
.MutableMappingTestMixin
):
333 def test_attrmap(me
):
335 kf
= C
.KeyFile("test", C
.KOPEN_WRITE | C
.KOPEN_NOFILE
)
336 k
= kf
.newkey(0x12345678, "test-key")
338 me
.check_mapping(mkmap
)
341 me
.assertRaises(TypeError, a
.update
, { 3: 3, 4: 5 })
343 ###----- That's all, folks --------------------------------------------------
345 if __name__
== "__main__": U
.main()