File: test_random.py

package info (click to toggle)
python-mbedtls 2.10.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 960 kB
  • sloc: python: 4,595; sh: 170; makefile: 18
file content (124 lines) | stat: -rw-r--r-- 4,004 bytes parent folder | download
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
# SPDX-License-Identifier: MIT

"""Unit tests for mbedtls.random."""

from __future__ import annotations

import pickle
import random
from collections import defaultdict
from typing import Callable, MutableMapping, Sequence

import pytest

import mbedtls._random as _drbg
import mbedtls.mpi as _mpi
from mbedtls.exceptions import TLSError


def sample(start: int, end: int, k: int = 20) -> Sequence[int]:
    return random.sample(range(start, end), k)


class TestEntropy:
    # pylint: disable=protected-access, no-member
    def test_pickle(self) -> None:
        entropy = _drbg.Random()._entropy
        with pytest.raises(TypeError) as excinfo:
            pickle.dumps(entropy)

        assert str(excinfo.value).startswith("cannot pickle")

    def test_gather(self) -> None:
        # Only test that this does not raise.
        _drbg.Random()._entropy.gather()

    @pytest.mark.parametrize("length", range(64))
    def test_retrieve(self, length: int) -> None:
        entropy = _drbg.Random()._entropy
        assert len(entropy.retrieve(length)) == length

    @pytest.mark.parametrize("length", [100])
    def test_retrieve_long_block_raises_exception(self, length: int) -> None:
        entropy = _drbg.Random()._entropy
        with pytest.raises(TLSError):
            entropy.retrieve(length)

    def test_update(self, randbytes: Callable[[int], bytes]) -> None:
        # Only test that this does not raise.
        buf = randbytes(64)
        _drbg.Random()._entropy.update(buf)

    def test_not_reproducible(self) -> None:
        entropy = _drbg.Random()._entropy
        assert entropy.retrieve(8) != entropy.retrieve(8)

    def test_random_retrieve(self) -> None:
        size = 4
        entropy = _drbg.Random()._entropy
        number = entropy.retrieve(size)
        result: MutableMapping[bool, int] = defaultdict(int)
        for _ in range(250):
            result[entropy.retrieve(size) == number] += 1
        assert result[True] <= 1


class TestRandom:
    def test_pickle(self) -> None:
        random = _drbg.Random()
        with pytest.raises(TypeError) as excinfo:
            pickle.dumps(random)

        assert str(excinfo.value).startswith("cannot pickle")

    def test_reseed(self) -> None:
        _drbg.Random()._reseed()

    @pytest.mark.repeat(100)
    def test_random(self) -> None:
        value = _drbg.Random().random()
        assert isinstance(value, float)
        assert 0 <= value < 1

    @pytest.mark.repeat(100)
    def test_random_not_reproducible(self) -> None:
        random = _drbg.Random()
        assert random.random() != random.random()

    @pytest.mark.repeat(100)
    def test_getrandbits_not_reproducible(self) -> None:
        random = _drbg.Random()
        assert random.getrandbits(64) != random.getrandbits(64)

    @pytest.mark.repeat(100)
    def test_random_initial_values(self) -> None:
        random = _drbg.Random()
        other = _drbg.Random()
        assert random.random() != other.random()

    @pytest.mark.repeat(100)
    def test_getrandbits_initial_values(self) -> None:
        random = _drbg.Random()
        other = _drbg.Random()
        assert random.getrandbits(64) != other.getrandbits(64)

    @pytest.mark.parametrize("nbits", [1, 5, 10, 50, 100, 300, 500])
    def test_getrandbits_size(self, nbits: int) -> None:
        random = _drbg.Random()
        value = random.getrandbits(nbits)
        assert isinstance(value, _mpi.MPI)
        assert value.bit_length() == pytest.approx(nbits, abs=8)
        assert value.bit_length() <= nbits

    @pytest.mark.parametrize("nbits", [-1, 0])
    def test_getrandbits_negative_k_raises_value_error(
        self, nbits: int
    ) -> None:
        random = _drbg.Random()
        with pytest.raises(ValueError):
            random.getrandbits(nbits)

    def test_getrandbits_nonint_k_raises_type_error(self) -> None:
        random = _drbg.Random()
        with pytest.raises(TypeError):
            random.getrandbits("a")  # type: ignore[arg-type]