File: test_signedjson.py

package info (click to toggle)
laniakea 0.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 15,464 kB
  • sloc: javascript: 38,493; python: 21,153; sh: 196; makefile: 129; ansic: 3
file content (198 lines) | stat: -rw-r--r-- 6,326 bytes parent folder | download | duplicates (2)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# -*- coding: utf-8 -*-
#
# Copyright (C) 2019-2022 Matthias Klumpp <matthias@tenstral.net>
# Copyright (C) 2014-2015 OpenMarket Ltd
#
# SPDX-License-Identifier: LGPL-3.0+ AND Apache-2.0

import pytest
import nacl.signing

from laniakea.utils import decode_base64, encode_base64
from laniakea.msgstream.signing import (
    get_verify_key,
    read_signing_keys,
    write_signing_keys,
    generate_signing_key,
    read_old_signing_keys,
    decode_verify_key_bytes,
    encode_verify_key_base64,
    decode_signing_key_base64,
    encode_signing_key_base64,
    is_signing_algorithm_supported,
)
from laniakea.msgstream.signedjson import (
    SignatureVerifyException,
    sign_json,
    signature_ids,
    verify_signed_json,
)


class TestGenerate:
    def test_generate_key(self):
        my_version = "my_version"
        my_key = generate_signing_key(my_version)
        assert my_key.alg == "ed25519"
        assert my_key.version == my_version


class TestDecode:
    def setup_method(self, test_method):
        self.version = "my_version"
        self.key = generate_signing_key(self.version)
        self.key_base64 = encode_signing_key_base64(self.key)
        self.verify_key = get_verify_key(self.key)
        self.verify_key_base64 = encode_verify_key_base64(self.verify_key)

    def test_decode(self):
        decoded_key = decode_signing_key_base64("ed25519", self.key_base64, self.version)
        assert decoded_key.alg == "ed25519"
        assert decoded_key.version == self.version

    def test_decode_invalid_base64(self):
        with pytest.raises(Exception):
            decode_signing_key_base64("ed25519", "not base 64", self.version)

    def test_decode_signing_invalid_algorithm(self):
        with pytest.raises(Exception):
            decode_signing_key_base64("not a valid alg", "", self.version)

    def test_decode_invalid_key(self):
        with pytest.raises(Exception):
            decode_signing_key_base64("ed25519", "", self.version)

    def test_read_keys(self):
        stream = ["ed25519 %s %s" % (self.version, self.key_base64)]
        keys = read_signing_keys(stream)
        assert len(keys) == 1

    def test_read_old_keys(self):
        stream = ["ed25519 %s 0 %s" % (self.version, self.verify_key_base64)]
        keys = read_old_signing_keys(stream)
        assert len(keys) == 1

    def test_decode_verify_invalid_algorithm(self):
        with pytest.raises(Exception):
            decode_verify_key_bytes("not a valid alg", self.verify_key)

    def test_write_signing_keys(self):
        class MockStream:
            def write(self, data):
                pass

        write_signing_keys(MockStream(), [self.key])


class TestAlgorithmSupported:
    def test_ed25519(self):
        assert is_signing_algorithm_supported("ed25519:an_id")

    def test_unsupported(self):
        assert not is_signing_algorithm_supported("unsupported:")


SIGNING_KEY_SEED = decode_base64("YJDBA9Xnr2sVqXD9Vj7XVUnmFZcZrlw8Md7kMW+3XA1")

KEY_ALG = "ed25519"
KEY_VER = 1
KEY_NAME = "%s:%d" % (KEY_ALG, KEY_VER)


class TestKnownKey:
    '''
    An entirely deterministic test using a given signing key seed, so that
    other implementations can compare that they get the same result.
    '''

    def setup_method(self, test_method):
        self.signing_key = nacl.signing.SigningKey(SIGNING_KEY_SEED)
        self.signing_key.alg = KEY_ALG
        self.signing_key.version = KEY_VER

    def test_sign_minimal(self):
        res = {
            'signatures': {
                'domain': {
                    KEY_NAME: "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMt"
                    "TdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"
                },
            }
        }
        assert sign_json({}, "domain", self.signing_key) == res

    def test_sign_with_data(self):
        res = {
            'one': 1,
            'two': "Two",
            'signatures': {
                'domain': {
                    KEY_NAME: "KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4s"
                    "L53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"
                },
            },
        }
        assert sign_json({'one': 1, 'two': "Two"}, "domain", self.signing_key) == res


class MockSigningKey:
    alg = "mock"
    version = "test"

    def sign(self, signed_bytes):
        self.signed_bytes = signed_bytes
        return MockSignature()


class MockVerifyKey:
    alg = "mock"
    version = "test"

    def verify(self, message, sig):
        if not sig == b"x_______":
            raise Exception()


class MockSignature:
    def __init__(self):
        self.signature = b"x_______"


class TestJsonSign:
    def setup_method(self, test_method):
        self.message = {'foo': 'bar', 'unsigned': {}}
        self.sigkey = MockSigningKey()
        assert self.sigkey.alg == 'mock'
        self.signed = sign_json(self.message, 'Alice', self.sigkey)
        self.verkey = MockVerifyKey()

    def test_sign_and_verify(self):
        assert 'signatures' in self.signed
        assert 'Alice' in self.signed['signatures']
        assert 'mock:test' in self.signed['signatures']['Alice']
        assert self.signed['signatures']['Alice']['mock:test'] == encode_base64(b'x_______')

        assert self.sigkey.signed_bytes == b'{"foo":"bar"}'
        verify_signed_json(self.signed, 'Alice', self.verkey)

    def test_signature_ids(self):
        key_ids = signature_ids(self.signed, 'Alice', supported_algorithms=['mock'])
        assert key_ids == ['mock:test']

    def test_verify_fail(self):
        self.signed['signatures']['Alice']['mock:test'] = encode_base64(b'not a signature')
        with pytest.raises(SignatureVerifyException):
            verify_signed_json(self.signed, 'Alice', self.verkey)

    def test_verify_fail_no_signatures(self):
        with pytest.raises(SignatureVerifyException):
            verify_signed_json({}, 'Alice', self.verkey)

    def test_verify_fail_no_signature_for_alice(self):
        with pytest.raises(SignatureVerifyException):
            verify_signed_json({'signatures': {}}, 'Alice', self.verkey)

    def test_verify_fail_not_base64(self):
        invalid = {'signatures': {'Alice': {'mock:test': 'not base64'}}}
        with pytest.raises(SignatureVerifyException):
            verify_signed_json(invalid, 'Alice', self.verkey)