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.assertEqual(kf.bytag("notexist"), None)
167 me.assertEqual(kf.bytag(12345), None)
168 me.assertEqual(kf.bytype("notexist"), None)
169 me.assertRaises(TypeError, kf.bytype, 12345)
170 me.assertRaises(C.KeyError, kf.byid, 0x12345678)
172 ## The keyring should be readonly.
173 me.assertRaises(C.KeyError, kf.newkey, 0x12345678, "fail")
174 me.assertRaises(C.KeyError, setattr, k, "tag", "foo")
175 me.assertRaises(C.KeyError, delattr, k, "tag")
176 me.assertRaises(C.KeyError, setattr, k, "data", C.KeyDataString("foo"))
178 def test_keywrite(me):
179 kf = C.KeyFile("test", C.KOPEN_WRITE | C.KOPEN_NOFILE)
180 me.assertEqual(kf.modifiedp, False)
184 k = kf.newkey(0x11111111, "first", exp)
185 me.assertEqual(kf.modifiedp, True)
187 me.assertEqual(kf[0x11111111].id, 0x11111111)
188 me.assertEqual(k.exptime, exp)
189 me.assertEqual(k.deltime, exp)
190 me.assertRaises(ValueError, setattr, k, "deltime", C.KEXP_FOREVER)
192 me.assertEqual(k.data.str, "<unset>")
194 k.data = C.KeyDataMP(n)
195 me.assertEqual(k.data.mp, n)
196 me.assertEqual(k.comment, None)
199 me.assertEqual(k.comment, c)
201 me.assertEqual(k.comment, None)
203 me.assertEqual(k.comment, c)
205 me.assertEqual(k.comment, None)
207 ###--------------------------------------------------------------------------
209 def keydata_equalp(kd0, kd1):
210 if type(kd0) is not type(kd1): return False
211 elif type(kd0) is C.KeyDataBinary: return kd0.bin == kd1.bin
212 elif type(kd0) is C.KeyDataMP: return kd0.mp == kd1.mp
213 elif type(kd0) is C.KeyDataEncrypted: return kd0.ct == kd1.ct
214 elif type(kd0) is C.KeyDataECPt: return kd0.ecpt == kd1.ecpt
215 elif type(kd0) is C.KeyDataString: return kd0.str == kd1.str
216 elif type(kd0) is C.KeyDataStructured:
217 if len(kd0) != len(kd1): return False
218 for t, v0 in T.iteritems(kd0):
220 except KeyError: return False
221 if not keydata_equalp(v0, v1): return False
224 raise SystemError("unexpected keydata type")
226 class TestKeyData (U.TestCase):
229 me.assertEqual(C.KeyData.readflags("none"), (0, 0, ""))
230 me.assertEqual(C.KeyData.readflags("ec,public:..."),
231 (C.KENC_EC | C.KCAT_PUB,
232 C.KF_ENCMASK | C.KF_CATMASK,
234 me.assertEqual(C.KeyData.readflags("int,burn"),
235 (C.KENC_MP | C.KF_BURN, C.KF_ENCMASK | C.KF_BURN, ""))
236 me.assertRaises(C.KeyError, C.KeyData.readflags, "int,burn?")
237 me.assertRaises(C.KeyError, C.KeyData.readflags, "int,ec")
238 me.assertRaises(C.KeyError, C.KeyData.readflags, "snork")
239 me.assertEqual(C.KeyData.writeflags(0), "binary,symmetric")
240 me.assertEqual(C.KeyData.writeflags(C.KENC_EC | C.KCAT_PUB), "ec,public")
243 kd = C.KeyDataStructured({ "a": C.KeyDataString("foo", "public"),
244 "b": C.KeyDataMP(12345, "private"),
245 "c": C.KeyDataString("bar", "public") })
248 me.assertEqual(type(kd2), C.KeyDataStructured)
249 me.assertEqual(set(T.iterkeys(kd2)), set(["a", "b", "c"]))
251 kd2 = C.KeyDataMP(12345, C.KCAT_PRIV).copy("private")
253 kd2 = kd.copy("-secret")
254 me.assertEqual(type(kd2), C.KeyDataStructured)
255 me.assertEqual(set(T.iterkeys(kd2)), set(["a", "c"]))
257 kd2 = kd.copy((0, C.KF_NONSECRET))
258 me.assertEqual(type(kd2), C.KeyDataStructured)
259 me.assertEqual(set(T.iterkeys(kd2)), set(["b"]))
261 def check_encode(me, kd):
262 me.assertTrue(keydata_equalp(C.KeyData.decode(kd.encode()), kd))
263 kd1, tail = C.KeyData.read(kd.write())
264 me.assertEqual(tail, "")
265 me.assertTrue(keydata_equalp(kd, kd1))
268 rng = T.detrand("kd-bin")
270 kd = C.KeyDataBinary(by, "symm,burn")
271 me.assertEqual(kd.bin, by)
275 rng = T.detrand("kd-mp")
277 kd = C.KeyDataMP(x, "symm,burn")
278 me.assertEqual(kd.mp, x)
282 s = "some random string"
283 kd = C.KeyDataString(s, "symm,burn")
284 me.assertEqual(kd.str, s)
288 rng = T.detrand("kd-enc")
290 kd = C.KeyDataEncrypted(ct, "symm")
291 me.assertEqual(kd.ct, ct)
295 rng = T.detrand("kd-ec")
296 Q = C.ECPt(rng.mp(128), rng.mp(128))
297 kd = C.KeyDataECPt(Q, "symm,burn")
298 me.assertEqual(kd.ecpt, Q)
302 rng = T.detrand("kd-struct")
303 kd = C.KeyDataStructured({ "a": C.KeyDataString("a"),
304 "b": C.KeyDataString("b"),
305 "c": C.KeyDataString("c"),
306 "d": C.KeyDataString("d") })
307 for i in ["a", "b", "c", "d"]: me.assertEqual(kd[i].str, i)
308 me.assertEqual(len(kd), 4)
310 me.assertRaises(TypeError, C.KeyDataStructured, { "a": "a" })
312 ###--------------------------------------------------------------------------
315 class TestKeyFileMapping (T.ImmutableMappingTextMixin):
316 def _mkkey(me, i): return i
317 def _getkey(me, k): return k
318 def _getvalue(me, v): return v.data.mp
320 def test_keyfile(me):
321 kf = C.KeyFile("test", C.KOPEN_WRITE | C.KOPEN_NOFILE)
325 kf.newkey(i, "k#%d" % i).data = C.KeyDataMP(100 + i)
327 me.check_immutable_mapping(kf, model)
329 class TestKeyAttrMapping (T.MutableMappingTestMixin):
331 def test_attrmap(me):
333 kf = C.KeyFile("test", C.KOPEN_WRITE | C.KOPEN_NOFILE)
334 k = kf.newkey(0x12345678, "test-key")
336 me.check_mapping(mkmap)
339 me.assertRaises(TypeError, a.update, { 3: 3, 4: 5 })
341 ###----- That's all, folks --------------------------------------------------
343 if __name__ == "__main__": U.main()