# Copyright 2017 Canonical Ltd.
# Licensed under the LGPLv3, see LICENCE file for details.
import base64
from unittest import TestCase
import macaroonbakery.bakery as bakery
import macaroonbakery.checkers as checkers
import nacl.public
import six
from macaroonbakery.bakery import _codec as codec
class TestCodec(TestCase):
def setUp(self):
self.fp_key = bakery.generate_key()
self.tp_key = bakery.generate_key()
def test_v1_round_trip(self):
tp_info = bakery.ThirdPartyInfo(
version=bakery.VERSION_1,
public_key=self.tp_key.public_key)
cid = bakery.encode_caveat(
'is-authenticated-user',
b'a random string',
tp_info,
self.fp_key,
None)
res = bakery.decode_caveat(self.tp_key, cid)
self.assertEquals(res, bakery.ThirdPartyCaveatInfo(
first_party_public_key=self.fp_key.public_key,
root_key=b'a random string',
condition='is-authenticated-user',
caveat=cid,
third_party_key_pair=self.tp_key,
version=bakery.VERSION_1,
id=None,
namespace=bakery.legacy_namespace()
))
def test_v2_round_trip(self):
tp_info = bakery.ThirdPartyInfo(
version=bakery.VERSION_2,
public_key=self.tp_key.public_key)
cid = bakery.encode_caveat(
'is-authenticated-user',
b'a random string',
tp_info,
self.fp_key,
None)
res = bakery.decode_caveat(self.tp_key, cid)
self.assertEquals(res, bakery.ThirdPartyCaveatInfo(
first_party_public_key=self.fp_key.public_key,
root_key=b'a random string',
condition='is-authenticated-user',
caveat=cid,
third_party_key_pair=self.tp_key,
version=bakery.VERSION_2,
id=None,
namespace=bakery.legacy_namespace()
))
def test_v3_round_trip(self):
tp_info = bakery.ThirdPartyInfo(
version=bakery.VERSION_3,
public_key=self.tp_key.public_key)
ns = checkers.Namespace()
ns.register('testns', 'x')
cid = bakery.encode_caveat(
'is-authenticated-user',
b'a random string',
tp_info,
self.fp_key,
ns)
res = bakery.decode_caveat(self.tp_key, cid)
self.assertEquals(res, bakery.ThirdPartyCaveatInfo(
first_party_public_key=self.fp_key.public_key,
root_key=b'a random string',
condition='is-authenticated-user',
caveat=cid,
third_party_key_pair=self.tp_key,
version=bakery.VERSION_3,
id=None,
namespace=ns
))
def test_empty_caveat_id(self):
with self.assertRaises(bakery.VerificationError) as context:
bakery.decode_caveat(self.tp_key, b'')
self.assertTrue('empty third party caveat' in str(context.exception))
def test_decode_caveat_v1_from_go(self):
tp_key = bakery.PrivateKey(
nacl.public.PrivateKey(base64.b64decode(
'TSpvLpQkRj+T3JXnsW2n43n5zP/0X4zn0RvDiWC3IJ0=')))
fp_key = bakery.PrivateKey(
nacl.public.PrivateKey(base64.b64decode(
'KXpsoJ9ujZYi/O2Cca6kaWh65MSawzy79LWkrjOfzcs=')))
root_key = base64.b64decode('vDxEmWZEkgiNEFlJ+8ruXe3qDSLf1H+o')
# This caveat has been generated from the go code
# to check the compatibilty
encrypted_cav = six.b(
'eyJUaGlyZFBhcnR5UHVibGljS2V5IjoiOFA3R1ZZc3BlWlN4c'
'3hFdmJsSVFFSTFqdTBTSWl0WlIrRFdhWE40cmxocz0iLCJGaX'
'JzdFBhcnR5UHVibGljS2V5IjoiSDlqSFJqSUxidXppa1VKd2o'
'5VGtDWk9qeW5oVmtTdHVsaUFRT2d6Y0NoZz0iLCJOb25jZSI6'
'Ii9lWTRTTWR6TGFxbDlsRFc3bHUyZTZuSzJnVG9veVl0IiwiS'
'WQiOiJra0ZuOGJEaEt4RUxtUjd0NkJxTU0vdHhMMFVqaEZjR1'
'BORldUUExGdjVla1dWUjA4Uk1sbGJhc3c4VGdFbkhzM0laeVo'
'0V2lEOHhRUWdjU3ljOHY4eUt4dEhxejVEczJOYmh1ZDJhUFdt'
'UTVMcVlNWitmZ2FNaTAxdE9DIn0=')
cav = bakery.decode_caveat(tp_key, encrypted_cav)
self.assertEquals(cav, bakery.ThirdPartyCaveatInfo(
condition='caveat condition',
first_party_public_key=fp_key.public_key,
third_party_key_pair=tp_key,
root_key=root_key,
caveat=encrypted_cav,
version=bakery.VERSION_1,
id=None,
namespace=bakery.legacy_namespace()
))
def test_decode_caveat_v2_from_go(self):
tp_key = bakery.PrivateKey(nacl.public.PrivateKey(
base64.b64decode(
'TSpvLpQkRj+T3JXnsW2n43n5zP/0X4zn0RvDiWC3IJ0=')))
fp_key = bakery.PrivateKey(
nacl.public.PrivateKey(base64.b64decode(
'KXpsoJ9ujZYi/O2Cca6kaWh65MSawzy79LWkrjOfzcs=')))
root_key = base64.b64decode('wh0HSM65wWHOIxoGjgJJOFvQKn2jJFhC')
# This caveat has been generated from the go code
# to check the compatibilty
encrypted_cav = bakery.b64decode(
'AvD-xlUf2MdGMgtu7OKRQnCP1OQJk6PKeFWRK26WIBA6DNwKGIHq9xGcHS9IZ'
'Lh0cL6D9qpeKI0mXmCPfnwRQDuVYC8y5gVWd-oCGZaj5TGtk3byp2Vnw6ojmt'
'sULDhY59YA_J_Y0ATkERO5T9ajoRWBxU2OXBoX6bImXA',
)
cav = bakery.decode_caveat(tp_key, encrypted_cav)
self.assertEqual(cav, bakery.ThirdPartyCaveatInfo(
condition='third party condition',
first_party_public_key=fp_key.public_key,
third_party_key_pair=tp_key,
root_key=root_key,
caveat=encrypted_cav,
version=bakery.VERSION_2,
id=None,
namespace=bakery.legacy_namespace()
))
def test_decode_caveat_v3_from_go(self):
tp_key = bakery.PrivateKey(
nacl.public.PrivateKey(base64.b64decode(
'TSpvLpQkRj+T3JXnsW2n43n5zP/0X4zn0RvDiWC3IJ0=')))
fp_key = bakery.PrivateKey(nacl.public.PrivateKey(
base64.b64decode(
'KXpsoJ9ujZYi/O2Cca6kaWh65MSawzy79LWkrjOfzcs=')))
root_key = base64.b64decode(b'oqOXI3/Mz/pKjCuFOt2eYxb7ndLq66GY')
# This caveat has been generated from the go code
# to check the compatibilty
encrypted_cav = bakery.b64decode(
'A_D-xlUf2MdGMgtu7OKRQnCP1OQJk6PKeFWRK26WIBA6DNwKGNLeFSkD2M-8A'
'EYvmgVH95GWu7T7caKxKhhOQFcEKgnXKJvYXxz1zin4cZc4Q6C7gVqA-J4_j3'
'1LX4VKxymqG62UGPo78wOv0_fKjr3OI6PPJOYOQgBMclemlRF2',
)
cav = bakery.decode_caveat(tp_key, encrypted_cav)
self.assertEquals(cav, bakery.ThirdPartyCaveatInfo(
condition='third party condition',
first_party_public_key=fp_key.public_key,
third_party_key_pair=tp_key,
root_key=root_key,
caveat=encrypted_cav,
version=bakery.VERSION_3,
id=None,
namespace=bakery.legacy_namespace()
))
def test_encode_decode_varint(self):
tests = [
(12, [12]),
(127, [127]),
(128, [128, 1]),
(129, [129, 1]),
(1234567, [135, 173, 75]),
(12131231231312, [208, 218, 233, 173, 136, 225, 2])
]
for test in tests:
data = bytearray()
expected = bytearray()
bakery.encode_uvarint(test[0], data)
for v in test[1]:
expected.append(v)
self.assertEquals(data, expected)
val = codec.decode_uvarint(bytes(data))
self.assertEquals(test[0], val[0])
self.assertEquals(len(test[1]), val[1])