summaryrefslogtreecommitdiff
path: root/macaroonbakery/codec.py
diff options
context:
space:
mode:
Diffstat (limited to 'macaroonbakery/codec.py')
-rw-r--r--macaroonbakery/codec.py148
1 files changed, 73 insertions, 75 deletions
diff --git a/macaroonbakery/codec.py b/macaroonbakery/codec.py
index 4015bbb..d9340b7 100644
--- a/macaroonbakery/codec.py
+++ b/macaroonbakery/codec.py
@@ -1,16 +1,13 @@
# Copyright 2017 Canonical Ltd.
# Licensed under the LGPLv3, see LICENCE file for details.
-
import base64
import json
-import namespace
-from nacl.public import Box, PublicKey
-from nacl.encoding import Base64Encoder
import six
+import nacl.public
-import bakery
-import macaroon
+import macaroonbakery
+import macaroonbakery.checkers as checkers
_PUBLIC_KEY_PREFIX_LEN = 4
_KEY_LEN = 32
@@ -36,44 +33,42 @@ def encode_caveat(condition, root_key, third_party_info, key, ns):
@param ns not used yet
@return bytes
'''
- if third_party_info.version == bakery.BAKERY_V1:
+ if third_party_info.version == macaroonbakery.BAKERY_V1:
return _encode_caveat_v1(condition, root_key,
third_party_info.public_key, key)
- if (third_party_info.version == bakery.BAKERY_V2 or
- third_party_info.version == bakery.BAKERY_V3):
+ if (third_party_info.version == macaroonbakery.BAKERY_V2 or
+ third_party_info.version == macaroonbakery.BAKERY_V3):
return _encode_caveat_v2_v3(third_party_info.version, condition,
- root_key, third_party_info.public_key, key,
- ns)
+ root_key, third_party_info.public_key,
+ key, ns)
raise NotImplementedError('only bakery v1, v2, v3 supported')
def _encode_caveat_v1(condition, root_key, third_party_pub_key, key):
'''Create a JSON-encoded third-party caveat.
- The third_party_pub_key key represents the public key of the third party
+ The third_party_pub_key key represents the PublicKey of the third party
we're encrypting the caveat for; the key is the public/private key pair of
the party that's adding the caveat.
@param condition string
@param root_key bytes
- @param third_party_pub_key nacl public key
- @param key nacl private key
+ @param third_party_pub_key (PublicKey)
+ @param key (PrivateKey)
@return a base64 encoded bytes
'''
plain_data = json.dumps({
'RootKey': base64.b64encode(root_key).decode('ascii'),
'Condition': condition
})
- box = Box(key, third_party_pub_key)
+ box = nacl.public.Box(key.key, third_party_pub_key.key)
encrypted = box.encrypt(six.b(plain_data))
- nonce = encrypted[0:Box.NONCE_SIZE]
- encrypted = encrypted[Box.NONCE_SIZE:]
+ nonce = encrypted[0:nacl.public.Box.NONCE_SIZE]
+ encrypted = encrypted[nacl.public.Box.NONCE_SIZE:]
return base64.b64encode(six.b(json.dumps({
- 'ThirdPartyPublicKey': third_party_pub_key.encode(
- Base64Encoder).decode('ascii'),
- 'FirstPartyPublicKey': key.public_key.encode(
- Base64Encoder).decode('ascii'),
+ 'ThirdPartyPublicKey': third_party_pub_key.encode().decode('ascii'),
+ 'FirstPartyPublicKey': key.public_key.encode().decode('ascii'),
'Nonce': base64.b64encode(nonce).decode('ascii'),
'Id': base64.b64encode(encrypted).decode('ascii')
})))
@@ -104,17 +99,17 @@ def _encode_caveat_v2_v3(version, condition, root_key, third_party_pub_key,
condition [rest of encrypted part]
'''
ns_data = bytearray()
- if version >= bakery.BAKERY_V3:
- ns_data = ns.serialize()
+ if version >= macaroonbakery.BAKERY_V3:
+ ns_data = ns.serialize_text()
data = bytearray()
data.append(version)
- data.extend(third_party_pub_key.encode()[:_PUBLIC_KEY_PREFIX_LEN])
- data.extend(key.public_key.encode()[:])
+ data.extend(third_party_pub_key.encode(raw=True)[:_PUBLIC_KEY_PREFIX_LEN])
+ data.extend(key.public_key.encode(raw=True)[:])
secret = _encode_secret_part_v2_v3(version, condition, root_key, ns_data)
- box = Box(key, third_party_pub_key)
+ box = nacl.public.Box(key.key, third_party_pub_key.key)
encrypted = box.encrypt(secret)
- nonce = encrypted[0:Box.NONCE_SIZE]
- encrypted = encrypted[Box.NONCE_SIZE:]
+ nonce = encrypted[0:nacl.public.Box.NONCE_SIZE]
+ encrypted = encrypted[nacl.public.Box.NONCE_SIZE:]
data.extend(nonce[:])
data.extend(encrypted)
return bytes(data)
@@ -134,10 +129,10 @@ def _encode_secret_part_v2_v3(version, condition, root_key, ns):
'''
data = bytearray()
data.append(version)
- _encode_uvarint(len(root_key), data)
+ encode_uvarint(len(root_key), data)
data.extend(root_key)
- if version >= bakery.BAKERY_V3:
- _encode_uvarint(len(ns), data)
+ if version >= macaroonbakery.BAKERY_V3:
+ encode_uvarint(len(ns), data)
data.extend(ns)
data.extend(condition.encode('utf-8'))
return bytes(data)
@@ -151,7 +146,7 @@ def decode_caveat(key, caveat):
@return ThirdPartyCaveatInfo
'''
if len(caveat) == 0:
- raise ValueError('empty third party caveat')
+ raise macaroonbakery.VerificationError('empty third party caveat')
first = caveat[:1]
if first == b'e':
@@ -159,16 +154,17 @@ def decode_caveat(key, caveat):
# encoded JSON object.
return _decode_caveat_v1(key, caveat)
first_as_int = six.byte2int(first)
- if first_as_int == bakery.BAKERY_V2 or first_as_int == bakery.BAKERY_V3:
+ if (first_as_int == macaroonbakery.BAKERY_V2 or
+ first_as_int == macaroonbakery.BAKERY_V3):
if (len(caveat) < _VERSION3_CAVEAT_MIN_LEN
- and first_as_int == bakery.BAKERY_V3):
+ and first_as_int == macaroonbakery.BAKERY_V3):
# If it has the version 3 caveat tag and it's too short, it's
# almost certainly an id, not an encrypted payload.
- raise ValueError(
+ raise macaroonbakery.VerificationError(
'caveat id payload not provided for caveat id {}'.format(
caveat))
return _decode_caveat_v2_v3(first_as_int, key, caveat)
- raise NotImplementedError('only bakery v1 supported')
+ raise macaroonbakery.VerificationError('unknown version for caveat')
def _decode_caveat_v1(key, caveat):
@@ -180,8 +176,9 @@ def _decode_caveat_v1(key, caveat):
data = base64.b64decode(caveat).decode('utf-8')
wrapper = json.loads(data)
- tp_public_key = PublicKey(base64.b64decode(wrapper['ThirdPartyPublicKey']))
- if key.public_key != tp_public_key:
+ tp_public_key = nacl.public.PublicKey(
+ base64.b64decode(wrapper['ThirdPartyPublicKey']))
+ if key.public_key.key != tp_public_key:
raise Exception('public key mismatch') # TODO
if wrapper.get('FirstPartyPublicKey', None) is None:
@@ -191,21 +188,22 @@ def _decode_caveat_v1(key, caveat):
secret = base64.b64decode(wrapper.get('Id'))
nonce = base64.b64decode(wrapper.get('Nonce'))
- fp_public_key = PublicKey(base64.b64decode(
+ fp_public_key = nacl.public.PublicKey(base64.b64decode(
wrapper.get('FirstPartyPublicKey')))
- box = Box(key, fp_public_key)
+ box = nacl.public.Box(key.key, fp_public_key)
c = box.decrypt(secret, nonce)
record = json.loads(c.decode('utf-8'))
- fp_key = PublicKey(base64.b64decode(wrapper.get('FirstPartyPublicKey')))
- return macaroon.ThirdPartyCaveatInfo(
- record.get('Condition'),
- fp_key,
- key,
- base64.b64decode(record.get('RootKey')),
- caveat,
- bakery.BAKERY_V1,
- macaroon.legacy_namespace()
+ fp_key = nacl.public.PublicKey(
+ base64.b64decode(wrapper.get('FirstPartyPublicKey')))
+ return macaroonbakery.ThirdPartyCaveatInfo(
+ condition=record.get('Condition'),
+ first_party_public_key=macaroonbakery.PublicKey(fp_key),
+ third_party_key_pair=key,
+ root_key=base64.b64decode(record.get('RootKey')),
+ caveat=caveat,
+ version=macaroonbakery.BAKERY_V1,
+ namespace=macaroonbakery.legacy_namespace()
)
@@ -213,60 +211,60 @@ def _decode_caveat_v2_v3(version, key, caveat):
'''Decodes a version 2 or version 3 caveat.
'''
if (len(caveat) < 1 + _PUBLIC_KEY_PREFIX_LEN +
- _KEY_LEN + Box.NONCE_SIZE + 16):
- raise ValueError('caveat id too short')
+ _KEY_LEN + nacl.public.Box.NONCE_SIZE + 16):
+ raise macaroonbakery.VerificationError('caveat id too short')
original_caveat = caveat
caveat = caveat[1:] # skip version (already checked)
pk_prefix = caveat[:_PUBLIC_KEY_PREFIX_LEN]
caveat = caveat[_PUBLIC_KEY_PREFIX_LEN:]
- if key.public_key.encode()[:_PUBLIC_KEY_PREFIX_LEN] != pk_prefix:
- raise ValueError('public key mismatch')
+ if key.public_key.encode(raw=True)[:_PUBLIC_KEY_PREFIX_LEN] != pk_prefix:
+ raise macaroonbakery.VerificationError('public key mismatch')
first_party_pub = caveat[:_KEY_LEN]
caveat = caveat[_KEY_LEN:]
- nonce = caveat[:Box.NONCE_SIZE]
- caveat = caveat[Box.NONCE_SIZE:]
- fp_public_key = PublicKey(first_party_pub)
- box = Box(key, fp_public_key)
+ nonce = caveat[:nacl.public.Box.NONCE_SIZE]
+ caveat = caveat[nacl.public.Box.NONCE_SIZE:]
+ fp_public_key = nacl.public.PublicKey(first_party_pub)
+ box = nacl.public.Box(key.key, fp_public_key)
data = box.decrypt(caveat, nonce)
root_key, condition, ns = _decode_secret_part_v2_v3(version, data)
- return macaroon.ThirdPartyCaveatInfo(
- condition.decode('utf-8'),
- fp_public_key,
- key,
- root_key,
- original_caveat,
- version,
- ns
+ return macaroonbakery.ThirdPartyCaveatInfo(
+ condition=condition.decode('utf-8'),
+ first_party_public_key=macaroonbakery.PublicKey(fp_public_key),
+ third_party_key_pair=key,
+ root_key=root_key,
+ caveat=original_caveat,
+ version=version,
+ namespace=ns
)
def _decode_secret_part_v2_v3(version, data):
if len(data) < 1:
- raise ValueError('secret part too short')
+ raise macaroonbakery.VerificationError('secret part too short')
got_version = six.byte2int(data[:1])
data = data[1:]
if version != got_version:
- raise ValueError(
+ raise macaroonbakery.VerificationError(
'unexpected secret part version, got {} want {}'.format(
got_version, version))
- root_key_length, read = _decode_uvarint(data)
+ root_key_length, read = decode_uvarint(data)
data = data[read:]
root_key = data[:root_key_length]
data = data[root_key_length:]
- if version >= bakery.BAKERY_V3:
- namespace_length, read = _decode_uvarint(data)
+ if version >= macaroonbakery.BAKERY_V3:
+ namespace_length, read = decode_uvarint(data)
data = data[read:]
ns_data = data[:namespace_length]
data = data[namespace_length:]
- ns = namespace.deserialize_namespace(ns_data)
+ ns = checkers.deserialize_namespace(ns_data)
else:
- ns = macaroon.legacy_namespace()
+ ns = macaroonbakery.legacy_namespace()
return root_key, data, ns
-def _encode_uvarint(n, data):
+def encode_uvarint(n, data):
'''encodes integer into variable-length format into data.'''
if n < 0:
raise ValueError('only support positive integer')
@@ -279,8 +277,8 @@ def _encode_uvarint(n, data):
data.append(this_byte | 128)
-def _decode_uvarint(data):
- '''Decode a variable -length integer.
+def decode_uvarint(data):
+ '''Decode a variable-length integer.
Reads a sequence of unsigned integer byte and decodes them into an integer
in variable-length format and returns it and the length read.