Skip to content

Signers

blake2signer.signers.Blake2Signer

Bases: Blake2SignerBase

BLAKE in keyed hashing mode for signing data.

Examples:

>>> data = b'facundo castro presente'
>>> secret_key = b'a very secret string'
>>> signer = Blake2Signer(
>>>     secret_key,
>>>     personalisation=b'the-data-signer',  # Make it unique per instance
>>> )
>>> # Sign, and i.e. store the data in a cookie
>>> signed: bytes = signer.sign(data)
>>> cookie = {'data': signed}
>>> # To verify and recover data simply use unsign: you will either get the
>>> # data, or a `SignerError` subclass exception (it is recommended to use
>>> # `SignedDataError` given it is more specific and prevents masking other
>>> # unrelated errors).
>>> try:
>>>     unsigned = signer.unsign(cookie.get('data', ''))
>>> except errors.SignedDataError:
>>>     # Can't trust given data so set a default, break current process, etc.
>>>     unsigned = b''
Source code in blake2signer/signers.py
 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
class Blake2Signer(Blake2SignerBase):
    """BLAKE in keyed hashing mode for signing data.

    Examples:
        >>> data = b'facundo castro presente'
        >>> secret_key = b'a very secret string'
        >>> signer = Blake2Signer(
        >>>     secret_key,
        >>>     personalisation=b'the-data-signer',  # Make it unique per instance
        >>> )
        >>> # Sign, and i.e. store the data in a cookie
        >>> signed: bytes = signer.sign(data)
        >>> cookie = {'data': signed}
        >>> # To verify and recover data simply use unsign: you will either get the
        >>> # data, or a `SignerError` subclass exception (it is recommended to use
        >>> # `SignedDataError` given it is more specific and prevents masking other
        >>> # unrelated errors).
        >>> try:
        >>>     unsigned = signer.unsign(cookie.get('data', ''))
        >>> except errors.SignedDataError:
        >>>     # Can't trust given data so set a default, break current process, etc.
        >>>     unsigned = b''

    """

    def sign(self, data: typing.Union[str, bytes]) -> bytes:
        """Sign given data and produce a stream composed of it, salt and signature.

        Note that given data is _not_ encrypted, only signed. To recover data from
        it, while validating the signature, use `unsign`.

        The signature and salt are encoded using the chosen encoder.
        Data is left as-is.

        For deterministic signatures, no salt is used. For non-deterministic ones,
        the salt is a cryptographically secure pseudorandom string generated for
        this signature only (meaning that the signature always changes even when
        the payload stays the same).

        If given data is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        bytes to this function.

        Args:
            data: Data to sign.

        Returns:
            A signed stream composed of salt, signature and data.

        Raises:
            ConversionError: Data can't be converted to bytes.
        """
        data_b = self._force_bytes(data)

        return self._compose(data_b, signature=self._sign(data_b))

    def sign_parts(self, data: typing.Union[str, bytes]) -> Blake2Signature:
        """Sign given data and produce a container with it and salted signature.

        This method is identical to `sign`, but it produces a container instead
        of a stream, in case of needing to handle data and signature separately.

        Note that given data is _not_ encrypted, only signed. To recover data from
        it, while validating the signature, use `unsign_parts`.

        The signature and salt are encoded using the chosen encoder.
        Data is left as-is.

        For deterministic signatures, no salt is used. For non-deterministic ones,
        the salt is a cryptographically secure pseudorandom string generated for
        this signature only (meaning that the signature always changes even when
        the payload stays the same).

        If given data is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        bytes to this function.

        Args:
            data: Data to sign.

        Returns:
            A container with data and its salted signature.

        Raises:
            ConversionError: Data can't be converted to bytes.
        """
        data_b = self._force_bytes(data)

        return Blake2Signature(data=data_b, signature=self._sign(data_b))

    def unsign(self, signed_data: typing.Union[str, bytes]) -> bytes:
        """Verify a stream signed by `sign` and recover original data.

        If given data is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        bytes to this function.

        Args:
            signed_data: Signed data to unsign.

        Returns:
            Original data.

        Raises:
            ConversionError: Signed data can't be converted to bytes.
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
        """
        return self._unsign(self._decompose(self._force_bytes(signed_data)))

    def unsign_parts(
        self,
        signature: typing.Union[Blake2Signature, Blake2SignatureDump],
    ) -> bytes:
        """Verify a container signed by `sign_parts` and recover original data.

        This method is identical to `unsign`, but it reads a container instead of
        a stream.

        If given container is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        a bytes container to this function.

        Args:
            signature: Signed data container to unsign.

        Returns:
            Original data.

        Raises:
            ConversionError: Signed data can't be converted to bytes.
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
        """
        signature = self._force_bytes_parts(signature)
        # It's easier to just join the parts together and unsign the stream.
        signed_data = self._compose(signature.data, signature=signature.signature)

        return self.unsign(signed_data)

sign(data)

Sign given data and produce a stream composed of it, salt and signature.

Note that given data is not encrypted, only signed. To recover data from it, while validating the signature, use unsign.

The signature and salt are encoded using the chosen encoder. Data is left as-is.

For deterministic signatures, no salt is used. For non-deterministic ones, the salt is a cryptographically secure pseudorandom string generated for this signature only (meaning that the signature always changes even when the payload stays the same).

If given data is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing bytes to this function.

Parameters:

Name Type Description Default
data typing.Union[str, bytes]

Data to sign.

required

Returns:

Type Description
bytes

A signed stream composed of salt, signature and data.

Raises:

Type Description
ConversionError

Data can't be converted to bytes.

Source code in blake2signer/signers.py
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
def sign(self, data: typing.Union[str, bytes]) -> bytes:
    """Sign given data and produce a stream composed of it, salt and signature.

    Note that given data is _not_ encrypted, only signed. To recover data from
    it, while validating the signature, use `unsign`.

    The signature and salt are encoded using the chosen encoder.
    Data is left as-is.

    For deterministic signatures, no salt is used. For non-deterministic ones,
    the salt is a cryptographically secure pseudorandom string generated for
    this signature only (meaning that the signature always changes even when
    the payload stays the same).

    If given data is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    bytes to this function.

    Args:
        data: Data to sign.

    Returns:
        A signed stream composed of salt, signature and data.

    Raises:
        ConversionError: Data can't be converted to bytes.
    """
    data_b = self._force_bytes(data)

    return self._compose(data_b, signature=self._sign(data_b))

sign_parts(data)

Sign given data and produce a container with it and salted signature.

This method is identical to sign, but it produces a container instead of a stream, in case of needing to handle data and signature separately.

Note that given data is not encrypted, only signed. To recover data from it, while validating the signature, use unsign_parts.

The signature and salt are encoded using the chosen encoder. Data is left as-is.

For deterministic signatures, no salt is used. For non-deterministic ones, the salt is a cryptographically secure pseudorandom string generated for this signature only (meaning that the signature always changes even when the payload stays the same).

If given data is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing bytes to this function.

Parameters:

Name Type Description Default
data typing.Union[str, bytes]

Data to sign.

required

Returns:

Type Description
Blake2Signature

A container with data and its salted signature.

Raises:

Type Description
ConversionError

Data can't be converted to bytes.

Source code in blake2signer/signers.py
 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
def sign_parts(self, data: typing.Union[str, bytes]) -> Blake2Signature:
    """Sign given data and produce a container with it and salted signature.

    This method is identical to `sign`, but it produces a container instead
    of a stream, in case of needing to handle data and signature separately.

    Note that given data is _not_ encrypted, only signed. To recover data from
    it, while validating the signature, use `unsign_parts`.

    The signature and salt are encoded using the chosen encoder.
    Data is left as-is.

    For deterministic signatures, no salt is used. For non-deterministic ones,
    the salt is a cryptographically secure pseudorandom string generated for
    this signature only (meaning that the signature always changes even when
    the payload stays the same).

    If given data is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    bytes to this function.

    Args:
        data: Data to sign.

    Returns:
        A container with data and its salted signature.

    Raises:
        ConversionError: Data can't be converted to bytes.
    """
    data_b = self._force_bytes(data)

    return Blake2Signature(data=data_b, signature=self._sign(data_b))

unsign(signed_data)

Verify a stream signed by sign and recover original data.

If given data is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing bytes to this function.

Parameters:

Name Type Description Default
signed_data typing.Union[str, bytes]

Signed data to unsign.

required

Returns:

Type Description
bytes

Original data.

Raises:

Type Description
ConversionError

Signed data can't be converted to bytes.

SignatureError

Signed data structure is not valid.

InvalidSignatureError

Signed data signature is invalid.

Source code in blake2signer/signers.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def unsign(self, signed_data: typing.Union[str, bytes]) -> bytes:
    """Verify a stream signed by `sign` and recover original data.

    If given data is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    bytes to this function.

    Args:
        signed_data: Signed data to unsign.

    Returns:
        Original data.

    Raises:
        ConversionError: Signed data can't be converted to bytes.
        SignatureError: Signed data structure is not valid.
        InvalidSignatureError: Signed data signature is invalid.
    """
    return self._unsign(self._decompose(self._force_bytes(signed_data)))

unsign_parts(signature)

Verify a container signed by sign_parts and recover original data.

This method is identical to unsign, but it reads a container instead of a stream.

If given container is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing a bytes container to this function.

Parameters:

Name Type Description Default
signature typing.Union[Blake2Signature, Blake2SignatureDump]

Signed data container to unsign.

required

Returns:

Type Description
bytes

Original data.

Raises:

Type Description
ConversionError

Signed data can't be converted to bytes.

SignatureError

Signed data structure is not valid.

InvalidSignatureError

Signed data signature is invalid.

Source code in blake2signer/signers.py
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
def unsign_parts(
    self,
    signature: typing.Union[Blake2Signature, Blake2SignatureDump],
) -> bytes:
    """Verify a container signed by `sign_parts` and recover original data.

    This method is identical to `unsign`, but it reads a container instead of
    a stream.

    If given container is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    a bytes container to this function.

    Args:
        signature: Signed data container to unsign.

    Returns:
        Original data.

    Raises:
        ConversionError: Signed data can't be converted to bytes.
        SignatureError: Signed data structure is not valid.
        InvalidSignatureError: Signed data signature is invalid.
    """
    signature = self._force_bytes_parts(signature)
    # It's easier to just join the parts together and unsign the stream.
    signed_data = self._compose(signature.data, signature=signature.signature)

    return self.unsign(signed_data)

blake2signer.signers.Blake2TimestampSigner

Bases: Blake2TimestampSignerBase

BLAKE in keyed hashing mode for signing data with timestamp.

Examples:

>>> data = b'facundo castro presente'
>>> secret_key = b'a very secret string'
>>> signer = Blake2TimestampSigner(
>>>     secret_key,
>>>     personalisation=b'the-data-time-signer',  # Make it unique per instance
>>> )
>>> # Sign, and i.e. store the data in a cookie
>>> signed: bytes = signer.sign(data)
>>> cookie = {'data': signed}
>>> # To verify and recover data simply use unsign: you will either get the
>>> # data, or a `SignerError` subclass exception (it is recommended to use
>>> # `SignedDataError` given it is more specific and prevents masking other
>>> # unrelated errors). You need to specify the signature age in seconds
>>> # (or a timedelta instance). If more than said seconds since the signature
>>> # was made have passed then an `ExpiredSignatureError` is raised.
>>> try:
>>>     unsigned = signer.unsign(cookie.get('data', ''), max_age=10)
>>> except errors.SignedDataError:
>>>     # Can't trust given data so set a default, break current process, etc.
>>>     unsigned = b''
Source code in blake2signer/signers.py
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
class Blake2TimestampSigner(Blake2TimestampSignerBase):
    """BLAKE in keyed hashing mode for signing data with timestamp.

    Examples:
        >>> data = b'facundo castro presente'
        >>> secret_key = b'a very secret string'
        >>> signer = Blake2TimestampSigner(
        >>>     secret_key,
        >>>     personalisation=b'the-data-time-signer',  # Make it unique per instance
        >>> )
        >>> # Sign, and i.e. store the data in a cookie
        >>> signed: bytes = signer.sign(data)
        >>> cookie = {'data': signed}
        >>> # To verify and recover data simply use unsign: you will either get the
        >>> # data, or a `SignerError` subclass exception (it is recommended to use
        >>> # `SignedDataError` given it is more specific and prevents masking other
        >>> # unrelated errors). You need to specify the signature age in seconds
        >>> # (or a timedelta instance). If more than said seconds since the signature
        >>> # was made have passed then an `ExpiredSignatureError` is raised.
        >>> try:
        >>>     unsigned = signer.unsign(cookie.get('data', ''), max_age=10)
        >>> except errors.SignedDataError:
        >>>     # Can't trust given data so set a default, break current process, etc.
        >>>     unsigned = b''

    """

    def sign(self, data: typing.Union[str, bytes]) -> bytes:
        """Sign given data and produce a stream of it, timestamp, salt and signature.

        Note that given data is _not_ encrypted, only signed. To recover data from
        it, while validating the signature and timestamp, use `unsign`.

        The signature, salt and timestamp are encoded using chosen encoder.
        Data is left as-is.

        For deterministic signatures, no salt is used. For non-deterministic ones,
        the salt is a cryptographically secure pseudorandom string generated for
        this signature only (meaning that the signature always changes even when
        the payload stays the same).

        If given data is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        bytes to this function.

        Args:
            data: Data to sign.

        Returns:
            A signed stream composed of salt, signature, timestamp and data.

        Raises:
            ConversionError: Data can't be converted to bytes.
        """
        data_b = self._force_bytes(data)

        return self._compose(data_b, signature=self._sign_with_timestamp(data_b))

    def sign_parts(self, data: typing.Union[str, bytes]) -> Blake2Signature:
        """Sign given data and produce a container of it, timestamp, salt and signature.

        This method is identical to `sign`, but it produces a container instead
        of a stream, in case of needing to handle data and signature separately.

        Note that given data is _not_ encrypted, only signed. To recover data from
        it, while validating the signature and timestamp, use `unsign_parts`.

        The signature, salt and timestamp are encoded using chosen encoder.
        Data is left as-is.

        For deterministic signatures, no salt is used. For non-deterministic ones,
        the salt is a cryptographically secure pseudorandom string generated for
        this signature only (meaning that the signature always changes even when
        the payload stays the same).

        If given data is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        bytes to this function.

        Args:
            data: Data to sign.

        Returns:
            A container with data and its timestamped salted signature.

        Raises:
            ConversionError: Data can't be converted to bytes.
        """
        data_b = self._force_bytes(data)

        return Blake2Signature(data=data_b, signature=self._sign_with_timestamp(data_b))

    def unsign(
        self,
        signed_data: typing.Union[str, bytes],
        *,
        max_age: typing.Union[None, int, float, timedelta],
    ) -> bytes:
        """Verify a stream signed and timestamped by `sign` and recover data.

        If given data is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        bytes to this function.

        If `max_age` is not provided, then the timestamp is not checked (the
        signature is always checked).

        Args:
            signed_data: Signed data to unsign.

        Keyword Args:
            max_age (optional): Ensure the signature is not older than this time
                in seconds.

        Returns:
            Original data.

        Raises:
            ConversionError: Signed data can't be converted to bytes.
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
            ExpiredSignatureError: Signed data signature has expired.
            DecodeError: Timestamp can't be decoded.
        """
        return self._unsign_with_timestamp(
            self._decompose(self._force_bytes(signed_data)),
            max_age=max_age,
        )

    def unsign_parts(
        self,
        signature: typing.Union[Blake2Signature, Blake2SignatureDump],
        *,
        max_age: typing.Union[None, int, float, timedelta],
    ) -> bytes:
        """Verify a container signed by `sign_parts` and recover original data.

        This method is identical to `unsign`, but it reads a container instead of
        a stream.

        If given container is not bytes, a conversion will be applied assuming it's
        UTF-8 encoded. You should prefer to properly encode strings and passing
        a bytes container to this function.

        If `max_age` is not provided, then the timestamp is not checked (the
        signature is always checked).

        Args:
            signature: Signed data container to unsign.

        Keyword Args:
            max_age (optional): Ensure the signature is not older than this time
                in seconds.

        Returns:
            Original data.

        Raises:
            ConversionError: Signed data can't be converted to bytes.
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
            ExpiredSignatureError: Signed data signature has expired.
            DecodeError: Timestamp can't be decoded.
        """
        sig = self._force_bytes_parts(signature)
        # It's easier to just join the parts together and unsign the stream.
        signed_data = self._compose(sig.data, signature=sig.signature)

        return self.unsign(signed_data, max_age=max_age)

sign(data)

Sign given data and produce a stream of it, timestamp, salt and signature.

Note that given data is not encrypted, only signed. To recover data from it, while validating the signature and timestamp, use unsign.

The signature, salt and timestamp are encoded using chosen encoder. Data is left as-is.

For deterministic signatures, no salt is used. For non-deterministic ones, the salt is a cryptographically secure pseudorandom string generated for this signature only (meaning that the signature always changes even when the payload stays the same).

If given data is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing bytes to this function.

Parameters:

Name Type Description Default
data typing.Union[str, bytes]

Data to sign.

required

Returns:

Type Description
bytes

A signed stream composed of salt, signature, timestamp and data.

Raises:

Type Description
ConversionError

Data can't be converted to bytes.

Source code in blake2signer/signers.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def sign(self, data: typing.Union[str, bytes]) -> bytes:
    """Sign given data and produce a stream of it, timestamp, salt and signature.

    Note that given data is _not_ encrypted, only signed. To recover data from
    it, while validating the signature and timestamp, use `unsign`.

    The signature, salt and timestamp are encoded using chosen encoder.
    Data is left as-is.

    For deterministic signatures, no salt is used. For non-deterministic ones,
    the salt is a cryptographically secure pseudorandom string generated for
    this signature only (meaning that the signature always changes even when
    the payload stays the same).

    If given data is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    bytes to this function.

    Args:
        data: Data to sign.

    Returns:
        A signed stream composed of salt, signature, timestamp and data.

    Raises:
        ConversionError: Data can't be converted to bytes.
    """
    data_b = self._force_bytes(data)

    return self._compose(data_b, signature=self._sign_with_timestamp(data_b))

sign_parts(data)

Sign given data and produce a container of it, timestamp, salt and signature.

This method is identical to sign, but it produces a container instead of a stream, in case of needing to handle data and signature separately.

Note that given data is not encrypted, only signed. To recover data from it, while validating the signature and timestamp, use unsign_parts.

The signature, salt and timestamp are encoded using chosen encoder. Data is left as-is.

For deterministic signatures, no salt is used. For non-deterministic ones, the salt is a cryptographically secure pseudorandom string generated for this signature only (meaning that the signature always changes even when the payload stays the same).

If given data is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing bytes to this function.

Parameters:

Name Type Description Default
data typing.Union[str, bytes]

Data to sign.

required

Returns:

Type Description
Blake2Signature

A container with data and its timestamped salted signature.

Raises:

Type Description
ConversionError

Data can't be converted to bytes.

Source code in blake2signer/signers.py
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def sign_parts(self, data: typing.Union[str, bytes]) -> Blake2Signature:
    """Sign given data and produce a container of it, timestamp, salt and signature.

    This method is identical to `sign`, but it produces a container instead
    of a stream, in case of needing to handle data and signature separately.

    Note that given data is _not_ encrypted, only signed. To recover data from
    it, while validating the signature and timestamp, use `unsign_parts`.

    The signature, salt and timestamp are encoded using chosen encoder.
    Data is left as-is.

    For deterministic signatures, no salt is used. For non-deterministic ones,
    the salt is a cryptographically secure pseudorandom string generated for
    this signature only (meaning that the signature always changes even when
    the payload stays the same).

    If given data is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    bytes to this function.

    Args:
        data: Data to sign.

    Returns:
        A container with data and its timestamped salted signature.

    Raises:
        ConversionError: Data can't be converted to bytes.
    """
    data_b = self._force_bytes(data)

    return Blake2Signature(data=data_b, signature=self._sign_with_timestamp(data_b))

unsign(signed_data, *, max_age)

Verify a stream signed and timestamped by sign and recover data.

If given data is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing bytes to this function.

If max_age is not provided, then the timestamp is not checked (the signature is always checked).

Parameters:

Name Type Description Default
signed_data typing.Union[str, bytes]

Signed data to unsign.

required

Other Parameters:

Name Type Description
max_age optional

Ensure the signature is not older than this time in seconds.

Returns:

Type Description
bytes

Original data.

Raises:

Type Description
ConversionError

Signed data can't be converted to bytes.

SignatureError

Signed data structure is not valid.

InvalidSignatureError

Signed data signature is invalid.

ExpiredSignatureError

Signed data signature has expired.

DecodeError

Timestamp can't be decoded.

Source code in blake2signer/signers.py
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
def unsign(
    self,
    signed_data: typing.Union[str, bytes],
    *,
    max_age: typing.Union[None, int, float, timedelta],
) -> bytes:
    """Verify a stream signed and timestamped by `sign` and recover data.

    If given data is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    bytes to this function.

    If `max_age` is not provided, then the timestamp is not checked (the
    signature is always checked).

    Args:
        signed_data: Signed data to unsign.

    Keyword Args:
        max_age (optional): Ensure the signature is not older than this time
            in seconds.

    Returns:
        Original data.

    Raises:
        ConversionError: Signed data can't be converted to bytes.
        SignatureError: Signed data structure is not valid.
        InvalidSignatureError: Signed data signature is invalid.
        ExpiredSignatureError: Signed data signature has expired.
        DecodeError: Timestamp can't be decoded.
    """
    return self._unsign_with_timestamp(
        self._decompose(self._force_bytes(signed_data)),
        max_age=max_age,
    )

unsign_parts(signature, *, max_age)

Verify a container signed by sign_parts and recover original data.

This method is identical to unsign, but it reads a container instead of a stream.

If given container is not bytes, a conversion will be applied assuming it's UTF-8 encoded. You should prefer to properly encode strings and passing a bytes container to this function.

If max_age is not provided, then the timestamp is not checked (the signature is always checked).

Parameters:

Name Type Description Default
signature typing.Union[Blake2Signature, Blake2SignatureDump]

Signed data container to unsign.

required

Other Parameters:

Name Type Description
max_age optional

Ensure the signature is not older than this time in seconds.

Returns:

Type Description
bytes

Original data.

Raises:

Type Description
ConversionError

Signed data can't be converted to bytes.

SignatureError

Signed data structure is not valid.

InvalidSignatureError

Signed data signature is invalid.

ExpiredSignatureError

Signed data signature has expired.

DecodeError

Timestamp can't be decoded.

Source code in blake2signer/signers.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
def unsign_parts(
    self,
    signature: typing.Union[Blake2Signature, Blake2SignatureDump],
    *,
    max_age: typing.Union[None, int, float, timedelta],
) -> bytes:
    """Verify a container signed by `sign_parts` and recover original data.

    This method is identical to `unsign`, but it reads a container instead of
    a stream.

    If given container is not bytes, a conversion will be applied assuming it's
    UTF-8 encoded. You should prefer to properly encode strings and passing
    a bytes container to this function.

    If `max_age` is not provided, then the timestamp is not checked (the
    signature is always checked).

    Args:
        signature: Signed data container to unsign.

    Keyword Args:
        max_age (optional): Ensure the signature is not older than this time
            in seconds.

    Returns:
        Original data.

    Raises:
        ConversionError: Signed data can't be converted to bytes.
        SignatureError: Signed data structure is not valid.
        InvalidSignatureError: Signed data signature is invalid.
        ExpiredSignatureError: Signed data signature has expired.
        DecodeError: Timestamp can't be decoded.
    """
    sig = self._force_bytes_parts(signature)
    # It's easier to just join the parts together and unsign the stream.
    signed_data = self._compose(sig.data, signature=sig.signature)

    return self.unsign(signed_data, max_age=max_age)

blake2signer.signers.Blake2SerializerSigner

Bases: SerializerMixin, CompressorMixin, Blake2SerializerSignerBase

BLAKE for signing and optionally timestamping serialized data.

It uses BLAKE in keyed hashing mode, and it can handle data serialization, compression and encoding.

Examples:

>>> data = {'message': 'attack at dawn', 'extra': [1, 2, 3, 4]}
>>> secret_key = b'a very secret string'
>>> signer = Blake2SerializerSigner(
>>>     secret_key,
>>>     max_age=timedelta(days=1),
>>>     personalisation=b'the-cookie-signer',  # Make it unique per instance
>>> )
>>> # Sign, and i.e. store the data in a cookie
>>> signed: str = signer.dumps(data)  # Compression is enabled by default
>>> cookie = {'data': signed}
>>> # To verify and recover data simply use loads: you will either get the
>>> # data, or a `SignerError` subclass exception (it is recommended to use
>>> # `SignedDataError` given it is more specific and prevents masking other
>>> # unrelated errors).
>>> try:
>>>     unsigned = signer.loads(cookie.get('data', ''))
>>> except errors.SignedDataError:
>>>     # Can't trust given data so set a default, break current process, etc.
>>>     unsigned = {'message': '', 'extra': []}
Note

If compressing data turns out to be detrimental then data won't be compressed. If you know that from beforehand and don't need compression, you can disable it: signed: str = signer.dumps(data, compress=False). Likewise, you can force compression using: signed: str = signer.dumps(data, force_compression=True).

Source code in blake2signer/signers.py
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
class Blake2SerializerSigner(
        SerializerMixin,
        CompressorMixin,
        Blake2SerializerSignerBase,
):
    """BLAKE for signing and optionally timestamping serialized data.

    It uses BLAKE in keyed hashing mode, and it can handle data serialization,
    compression and encoding.

    Examples:
        >>> data = {'message': 'attack at dawn', 'extra': [1, 2, 3, 4]}
        >>> secret_key = b'a very secret string'
        >>> signer = Blake2SerializerSigner(
        >>>     secret_key,
        >>>     max_age=timedelta(days=1),
        >>>     personalisation=b'the-cookie-signer',  # Make it unique per instance
        >>> )
        >>> # Sign, and i.e. store the data in a cookie
        >>> signed: str = signer.dumps(data)  # Compression is enabled by default
        >>> cookie = {'data': signed}
        >>> # To verify and recover data simply use loads: you will either get the
        >>> # data, or a `SignerError` subclass exception (it is recommended to use
        >>> # `SignedDataError` given it is more specific and prevents masking other
        >>> # unrelated errors).
        >>> try:
        >>>     unsigned = signer.loads(cookie.get('data', ''))
        >>> except errors.SignedDataError:
        >>>     # Can't trust given data so set a default, break current process, etc.
        >>>     unsigned = {'message': '', 'extra': []}

    Note:
        If compressing data turns out to be detrimental then data won't be compressed.
        If you know that from beforehand and don't need compression, you can disable it:
        `signed: str = signer.dumps(data, compress=False)`.
        Likewise, you can force compression using:
        `signed: str = signer.dumps(data, force_compression=True)`.

    """

    def __init__(  # pylint: disable=R0801
        self,
        secret: typing.Union[Secret, typing.Sequence[Secret]],
        *,
        max_age: typing.Union[None, int, float, timedelta] = None,
        personalisation: typing.Union[str, bytes] = b'',
        digest_size: typing.Optional[int] = None,
        hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
        deterministic: bool = False,
        separator: typing.Union[str, bytes] = b'.',
        encoder: typing.Type[EncoderInterface] = B64URLEncoder,
        serializer: typing.Type[SerializerInterface] = JSONSerializer,
        compressor: typing.Type[CompressorInterface] = ZlibCompressor,
        compression_flag: typing.Union[str, bytes] = b'.',
        compression_ratio: typing.Union[int, float] = 5.0,
    ) -> None:
        """Serialize, sign and verify serialized signed data using BLAKE.

        It uses BLAKE in keyed hashing mode, and it can handle data serialization,
        compression and encoding.

        Setting `max_age` will produce a timestamped signed stream.

        Args:
            secret: Secret value which will be derived using BLAKE to produce the
                signing key. The minimum secret size is enforced to 16 bytes and there
                is no maximum. You can optionally provide a sequence of secrets, oldest
                to newest, that are used during signature check to allow for secret
                rotation. The last, newest, secret is used for signing.

        Keyword Args:
            max_age (optional): Use a timestamp signer instead of a regular one
                to ensure that the signature is not older than this time in seconds.
            personalisation (optional): Personalisation string to force the hash
                function to produce different digests for the same input. It is
                derived using BLAKE to ensure it fits the hasher limits, so it
                has no practical size limit. It defaults to the class name.
            digest_size (optional): Size of output signature (digest) in bytes
                (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
            hasher (optional): Hash function to use: blake2b (default), blake2s
                or blake3.
            deterministic (optional): Define if signatures are deterministic or
                non-deterministic (default). Non-deterministic sigs are preferred,
                and achieved through the use of a random salt. For deterministic
                sigs, no salt is used: this means that for the same payload, the
                same sig is obtained (the advantage is that the sig is shorter).
                Note that this assumes that the serializer and compressor are
                always deterministic.
            separator (optional): Character to separate the signature and the
                payload. It must not belong to the encoder alphabet and be ASCII
                (defaults to ".").
            encoder (optional): Encoder class to use (defaults to a Base64 URL
                safe encoder).
            serializer (optional): Serializer class to use (defaults to a JSON
                serializer).
            compressor (optional): Compressor class to use (defaults to a Zlib
                compressor).
            compression_flag (optional): Character to mark the payload as compressed.
                It must not belong to the encoder alphabet and be ASCII (defaults
                to ".").
            compression_ratio (optional): Desired minimal compression ratio, between
                0 and below 100 (defaults to 5). It is used to calculate when
                to consider a payload sufficiently compressed to detect detrimental
                compression. By default, if compression achieves less than 5% of
                size reduction, it is considered detrimental.

        Raises:
            ConversionError: A bytes parameter is not bytes and can't be converted
                to bytes.
            InvalidOptionError: A parameter is out of bounds.
        """
        super().__init__(
            secret,
            max_age=max_age,
            personalisation=personalisation,
            digest_size=digest_size,
            hasher=hasher,
            deterministic=deterministic,
            separator=separator,
            serializer=serializer,
            compressor=compressor,
            encoder=encoder,
            compression_flag=compression_flag,
            compression_ratio=compression_ratio,
        )

        if self._compression_flag in self._encoder.alphabet:
            raise errors.InvalidOptionError(
                'the compression flag character must not belong to the encoder alphabet',
            )

    def _dumps(self, data: typing.Any, **kwargs: typing.Any) -> bytes:
        """Dump data serializing it.

        This method serializes data, then compresses it and finally encodes it.

        Args:
            data: Data to serialize.

        Keyword Args:
            compress (bool): Compress data after serializing.
            compression_level (int, optional): Set the desired compression level
                when using compression, where 1 is the fastest and least compressed
                and 9 the slowest and most compressed.
            force_compression (bool): Force compression even if it would be detrimental
                for performance or size. This parameter overrides `compress`.
            serializer_kwargs (dict, optional): Provide keyword arguments for the
                serializer.

        Returns:
            Serialized data.
        """
        compress: bool = kwargs['compress']
        compression_level: typing.Optional[int] = kwargs.get('compression_level')
        force_compression: bool = kwargs['force_compression']
        serializer_kwargs: typing.Dict[str, typing.Any]
        serializer_kwargs = kwargs.get('serializer_kwargs') or {}

        serialized = self._serialize(data, **serializer_kwargs)

        if compress or force_compression:
            compressed, is_compressed = self._compress(
                serialized,
                level=compression_level,
                force=force_compression,
            )
        else:
            compressed, is_compressed = serialized, False

        encoded = self._encode(compressed)
        if is_compressed:
            encoded = self._add_compression_flag(encoded)

        return encoded

    def _loads(self, dumped_data: bytes, **kwargs: typing.Any) -> typing.Any:
        """Load serialized data to recover it.

        This method decodes data, then decompresses it and finally unserializes it.

        Args:
            dumped_data: Data to unserialize.

        Keyword Args:
            **kwargs: Ignored.

        Returns:
            Original data.

        Raises:
            DecodeError: Data can't be decoded.
            DecompressionError: Data can't be decompressed.
            UnserializationError: Data can't be unserialized.
        """
        data, is_compressed = self._remove_compression_flag_if_compressed(dumped_data)

        decoded = self._decode(data)

        decompressed = self._decompress(decoded) if is_compressed else decoded

        unserizalized = self._unserialize(decompressed)

        return unserizalized

    def dumps(
        self,
        data: typing.Any,
        *,
        compress: bool = True,
        compression_level: typing.Optional[int] = None,
        force_compression: bool = False,
        serializer_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
    ) -> str:
        """Serialize and sign data, optionally compressing and/or timestamping it.

        Note that given data is _not_ encrypted, only signed. To recover data from
        the produced string, while validating the signature (and timestamp if any),
        use `loads`.

        Data will be serialized, optionally compressed, and encoded before being
        signed. This means that it must be of any type serializable by the chosen
        serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool,
        None or dict, or a composition of those (tuples are unserialized as lists).

        If `max_age` was specified then the stream will be timestamped.

        For deterministic signatures, no salt is used. For non-deterministic ones,
        the salt is a cryptographically secure pseudorandom string generated for
        this signature only (meaning that the signature always changes even when
        the payload stays the same).

        The full flow is as follows, where optional actions are marked between brackets:
        data -> serialize -> [compress] -> [timestamp] -> encode -> sign

        Args:
            data: Any serializable object.

        Keyword Args:
            compress (optional): Compress data (default) after serializing it and
                decompress it before unserializing. For low entropy payloads such
                as human-readable text, it's beneficial from around ~30bytes, and
                detrimental if smaller. For high entropy payloads like pseudorandom
                text, it's beneficial from around ~300bytes and detrimental if lower
                than ~100bytes. You can safely enable it since a size check is done
                so if compression turns detrimental then it won't be used. If you
                know from beforehand that data can't be compressed and don't want
                to waste resources trying, set it to False.
            compression_level (optional): Set the desired compression level when
                using compression, where 1 is the fastest and least compressed
                and 9 the slowest and most compressed. The default value depends
                on the compressor being used. Note that the performance impact is
                for both compression and decompression.
            force_compression (optional): Force compression even if it would be
                detrimental for performance or size. This parameter overrides
                `compress`.
            serializer_kwargs (optional): Provide keyword arguments for the serializer.

        Returns:
            An encoded, signed and optionally timestamped string of serialized
            and optionally compressed data. This value is safe for printing or
            transmitting as it only contains the characters supported by the
            encoder and the separator, which are ASCII.

        Raises:
            SerializationError: Data can't be serialized.
            CompressionError: Data can't be compressed or compression level is invalid.
            EncodeError: Data can't be encoded.
        """
        dump = self._dumps(
            data,
            compress=compress,
            compression_level=compression_level,
            force_compression=force_compression,
            serializer_kwargs=serializer_kwargs,
        )

        # since everything is ASCII, decoding is safe
        return self._compose(dump, signature=self._proper_sign(dump)).decode()

    def dumps_parts(
        self,
        data: typing.Any,
        *,
        compress: bool = True,
        compression_level: typing.Optional[int] = None,
        force_compression: bool = False,
        serializer_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
    ) -> Blake2SignatureDump:
        """Serialize and sign data, optionally compressing and/or timestamping it.

        This method is identical to `dumps`, but it dumps into a container instead
        of a stream, in case of needing to handle data and signature separately.

        Note that given data is _not_ encrypted, only signed. To recover data from
        the produced string, while validating the signature (and timestamp if any),
        use `loads_parts`.

        Data will be serialized, optionally compressed, and encoded before being
        signed. This means that it must be of any type serializable by the chosen
        serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool,
        None or dict, or a composition of those (tuples are unserialized as lists).

        If `max_age` was specified then the stream will be timestamped.

        For deterministic signatures, no salt is used. For non-deterministic ones,
        the salt is a cryptographically secure pseudorandom string generated for
        this signature only (meaning that the signature always changes even when
        the payload stays the same).

        The full flow is as follows, where optional actions are marked between brackets:
        data -> serialize -> [compress] -> [timestamp] -> encode -> sign

        Args:
            data: Any serializable object.

        Keyword Args:
            compress (optional): Compress data (default) after serializing it and
                decompress it before unserializing. For low entropy payloads such
                as human-readable text, it's beneficial from around ~30bytes, and
                detrimental if smaller. For high entropy payloads like pseudorandom
                text, it's beneficial from around ~300bytes and detrimental if lower
                than ~100bytes. You can safely enable it since a size check is done
                so if compression turns detrimental then it won't be used. If you
                know from beforehand that data can't be compressed and don't want
                to waste resources trying, set it to False.
            compression_level (optional): Set the desired compression level when
                using compression, where 1 is the fastest and least compressed
                and 9 the slowest and most compressed. The default value depends
                on the compressor being used. Note that the performance impact is
                for both compression and decompression.
            force_compression (optional): Force compression even if it would be
                detrimental for performance or size. This parameter overrides
                `compress`.
            serializer_kwargs (optional): Provide keyword arguments for the serializer.

        Returns:
            A container with an encoded, signed and optionally timestamped string
            of serialized and optionally compressed data. This value is safe for
            printing or transmitting as it only contains the characters supported
            by the encoder and the separator, which are ASCII.

        Raises:
            SerializationError: Data can't be serialized.
            CompressionError: Data can't be compressed or compression level is invalid.
            EncodeError: Data can't be encoded.
        """
        dump = self._dumps(
            data,
            compress=compress,
            compression_level=compression_level,
            force_compression=force_compression,
            serializer_kwargs=serializer_kwargs,
        )

        # since everything is ASCII, decoding is safe
        return Blake2SignatureDump(
            data=dump.decode(),
            signature=self._proper_sign(dump).decode(),
        )

    def dump(
        self,
        data: typing.Any,
        file: typing.IO,
        *,
        compress: bool = True,
        compression_level: typing.Optional[int] = None,
        force_compression: bool = False,
        serializer_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
    ) -> str:
        """Serialize and sign data to file optionally compressing and/or timestamping it.

        This method is identical to :meth:`dumps`, but it dumps into a file, which
        must be a `.write()`-supporting file-like object, and returns the written
        value for convenience.

        Note that given data is _not_ encrypted, only signed. To recover data from
        the produced string, while validating the signature (and timestamp if any),
        use `loads`.

        Data will be serialized, optionally compressed, and encoded before being
        signed. This means that it must be of any type serializable by the chosen
        serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool,
        None or dict, or a composition of those (tuples are unserialized as lists).

        If `max_age` was specified then the stream will be timestamped.

        For deterministic signatures, no salt is used. For non-deterministic ones,
        the salt is a cryptographically secure pseudorandom string generated for
        this signature only (meaning that the signature always changes even when
        the payload stays the same).

        The full flow is as follows, where optional actions are marked between brackets:
        data -> serialize -> [compress] -> [timestamp] -> encode -> sign

        Args:
            data: Any serializable object.
            file: A `.write()`-supporting file-like object.

        Keyword Args:
            compress (optional): Compress data (default) after serializing it and
                decompress it before unserializing. For low entropy payloads such
                as human-readable text, it's beneficial from around ~30bytes, and
                detrimental if smaller. For high entropy payloads like pseudorandom
                text, it's beneficial from around ~300bytes and detrimental if lower
                than ~100bytes. You can safely enable it since a size check is done
                so if compression turns detrimental then it won't be used. If you
                know from beforehand that data can't be compressed and don't want
                to waste resources trying, set it to False.
            compression_level (optional): Set the desired compression level when
                using compression, where 1 is the fastest and least compressed
                and 9 the slowest and most compressed. The default value depends
                on the compressor being used. Note that the performance impact is
                for both compression and decompression.
            force_compression (optional): Force compression even if it would be
                detrimental for performance or size. This parameter overrides
                `compress`.
            serializer_kwargs (optional): Provide keyword arguments for the serializer.

        Returns:
            An encoded, signed and optionally timestamped string of serialized
            and optionally compressed data. This value is safe for printing or
            transmitting as it only contains the characters supported by the
            encoder and the separator, which are ASCII.

        Raises:
            SerializationError: Data can't be serialized.
            CompressionError: Data can't be compressed or compression level is invalid.
            EncodeError: Data can't be encoded.
            FileError: File can't be written.
        """
        signed = self.dumps(
            data,
            compress=compress,
            compression_level=compression_level,
            force_compression=force_compression,
            serializer_kwargs=serializer_kwargs,
        )

        # Signed value is ASCII so ConversionError can't happen.
        self._write(file, signed)

        return signed

    def loads(self, signed_data: typing.Union[str, bytes]) -> typing.Any:
        """Recover original data from a signed serialized string from `dumps`.

        If `max_age` was specified then it will be ensured that the signature is
        not older than that time in seconds.

        If the data was compressed, it will be decompressed before unserializing it.

        The full flow is as follows, where optional actions are marked between brackets:
        data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

        Args:
            signed_data: Signed data to unsign.

        Returns:
            Original data.

        Raises:
            ConversionError: Signed data can't be converted to bytes.
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
            ExpiredSignatureError: Signed data signature has expired.
            DecodeError: Signed data can't be decoded.
            DecompressionError: Signed data can't be decompressed.
            UnserializationError: Signed data can't be unserialized.
        """
        parts = self._decompose(self._force_bytes(signed_data))

        return self._loads(self._proper_unsign(parts))

    def loads_parts(
        self,
        signature: typing.Union[Blake2Signature, Blake2SignatureDump],
    ) -> typing.Any:
        """Recover original data from a signed serialized container from `dumps_parts`.

        This method is identical to `loads`, but it reads a container instead of
        a stream.

        If `max_age` was specified then it will be ensured that the signature is
        not older than that time in seconds.

        If the data was compressed, it will be decompressed before unserializing it.

        The full flow is as follows, where optional actions are marked between brackets:
        data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

        Args:
            signature: Signed data container to unsign.

        Returns:
            Original data.

        Raises:
            ConversionError: Signed data can't be converted to bytes.
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
            ExpiredSignatureError: Signed data signature has expired.
            DecodeError: Signed data can't be decoded.
            DecompressionError: Signed data can't be decompressed.
            UnserializationError: Signed data can't be unserialized.
        """
        sig = self._force_bytes_parts(signature)
        # It's easier to just join the parts together and unsign the stream.
        signed_data = self._compose(sig.data, signature=sig.signature)

        return self.loads(signed_data)

    def load(self, file: typing.IO) -> typing.Any:
        """Recover original data from a signed serialized file from `dump`.

        This method is identical to `loads`, but it reads a file, which
        must be a `.read()`-supporting file-like object.

        If `max_age` was specified then it will be ensured that the signature is
        not older than that time in seconds.

        If the data was compressed, it will be decompressed before unserializing it.

        The full flow is as follows, where optional actions are marked between brackets:
        data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

        Args:
            file: A `.read()`-supporting file-like object containing data to unsign.

        Returns:
            Original data.

        Raises:
            ConversionError: Signed data can't be converted to bytes.
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
            ExpiredSignatureError: Signed data signature has expired.
            DecodeError: Signed data can't be decoded.
            DecompressionError: Signed data can't be decompressed.
            UnserializationError: Signed data can't be unserialized.
            FileError: File can't be read.
        """
        return self.loads(self._read(file))

    def data_from_exc(self, exc: errors.ExpiredSignatureError) -> typing.Any:
        """Recover original data from an ExpiredSignatureError exception.

        If the data was compressed, it will be decompressed before unserializing it.

        The full flow is as follows, where optional actions are marked between brackets:
        data -> decode -> [decompress] -> unserialize

        Args:
            exc: An ExpiredSignatureError exception to extract the data from it.

        Returns:
            Original data.

        Raises:
            DecodeError: Data can't be decoded.
            DecompressionError: Data can't be decompressed.
            UnserializationError: Data can't be unserialized.
        """
        return self._loads(exc.data)

__init__(secret, *, max_age=None, personalisation=b'', digest_size=None, hasher=HasherChoice.blake2b, deterministic=False, separator=b'.', encoder=B64URLEncoder, serializer=JSONSerializer, compressor=ZlibCompressor, compression_flag=b'.', compression_ratio=5.0)

Serialize, sign and verify serialized signed data using BLAKE.

It uses BLAKE in keyed hashing mode, and it can handle data serialization, compression and encoding.

Setting max_age will produce a timestamped signed stream.

Parameters:

Name Type Description Default
secret typing.Union[Secret, typing.Sequence[Secret]]

Secret value which will be derived using BLAKE to produce the signing key. The minimum secret size is enforced to 16 bytes and there is no maximum. You can optionally provide a sequence of secrets, oldest to newest, that are used during signature check to allow for secret rotation. The last, newest, secret is used for signing.

required

Other Parameters:

Name Type Description
max_age optional

Use a timestamp signer instead of a regular one to ensure that the signature is not older than this time in seconds.

personalisation optional

Personalisation string to force the hash function to produce different digests for the same input. It is derived using BLAKE to ensure it fits the hasher limits, so it has no practical size limit. It defaults to the class name.

digest_size optional

Size of output signature (digest) in bytes (defaults to 16 bytes). The minimum size is enforced to 16 bytes.

hasher optional

Hash function to use: blake2b (default), blake2s or blake3.

deterministic optional

Define if signatures are deterministic or non-deterministic (default). Non-deterministic sigs are preferred, and achieved through the use of a random salt. For deterministic sigs, no salt is used: this means that for the same payload, the same sig is obtained (the advantage is that the sig is shorter). Note that this assumes that the serializer and compressor are always deterministic.

separator optional

Character to separate the signature and the payload. It must not belong to the encoder alphabet and be ASCII (defaults to ".").

encoder optional

Encoder class to use (defaults to a Base64 URL safe encoder).

serializer optional

Serializer class to use (defaults to a JSON serializer).

compressor optional

Compressor class to use (defaults to a Zlib compressor).

compression_flag optional

Character to mark the payload as compressed. It must not belong to the encoder alphabet and be ASCII (defaults to ".").

compression_ratio optional

Desired minimal compression ratio, between 0 and below 100 (defaults to 5). It is used to calculate when to consider a payload sufficiently compressed to detect detrimental compression. By default, if compression achieves less than 5% of size reduction, it is considered detrimental.

Raises:

Type Description
ConversionError

A bytes parameter is not bytes and can't be converted to bytes.

InvalidOptionError

A parameter is out of bounds.

Source code in blake2signer/signers.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
def __init__(  # pylint: disable=R0801
    self,
    secret: typing.Union[Secret, typing.Sequence[Secret]],
    *,
    max_age: typing.Union[None, int, float, timedelta] = None,
    personalisation: typing.Union[str, bytes] = b'',
    digest_size: typing.Optional[int] = None,
    hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
    deterministic: bool = False,
    separator: typing.Union[str, bytes] = b'.',
    encoder: typing.Type[EncoderInterface] = B64URLEncoder,
    serializer: typing.Type[SerializerInterface] = JSONSerializer,
    compressor: typing.Type[CompressorInterface] = ZlibCompressor,
    compression_flag: typing.Union[str, bytes] = b'.',
    compression_ratio: typing.Union[int, float] = 5.0,
) -> None:
    """Serialize, sign and verify serialized signed data using BLAKE.

    It uses BLAKE in keyed hashing mode, and it can handle data serialization,
    compression and encoding.

    Setting `max_age` will produce a timestamped signed stream.

    Args:
        secret: Secret value which will be derived using BLAKE to produce the
            signing key. The minimum secret size is enforced to 16 bytes and there
            is no maximum. You can optionally provide a sequence of secrets, oldest
            to newest, that are used during signature check to allow for secret
            rotation. The last, newest, secret is used for signing.

    Keyword Args:
        max_age (optional): Use a timestamp signer instead of a regular one
            to ensure that the signature is not older than this time in seconds.
        personalisation (optional): Personalisation string to force the hash
            function to produce different digests for the same input. It is
            derived using BLAKE to ensure it fits the hasher limits, so it
            has no practical size limit. It defaults to the class name.
        digest_size (optional): Size of output signature (digest) in bytes
            (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
        hasher (optional): Hash function to use: blake2b (default), blake2s
            or blake3.
        deterministic (optional): Define if signatures are deterministic or
            non-deterministic (default). Non-deterministic sigs are preferred,
            and achieved through the use of a random salt. For deterministic
            sigs, no salt is used: this means that for the same payload, the
            same sig is obtained (the advantage is that the sig is shorter).
            Note that this assumes that the serializer and compressor are
            always deterministic.
        separator (optional): Character to separate the signature and the
            payload. It must not belong to the encoder alphabet and be ASCII
            (defaults to ".").
        encoder (optional): Encoder class to use (defaults to a Base64 URL
            safe encoder).
        serializer (optional): Serializer class to use (defaults to a JSON
            serializer).
        compressor (optional): Compressor class to use (defaults to a Zlib
            compressor).
        compression_flag (optional): Character to mark the payload as compressed.
            It must not belong to the encoder alphabet and be ASCII (defaults
            to ".").
        compression_ratio (optional): Desired minimal compression ratio, between
            0 and below 100 (defaults to 5). It is used to calculate when
            to consider a payload sufficiently compressed to detect detrimental
            compression. By default, if compression achieves less than 5% of
            size reduction, it is considered detrimental.

    Raises:
        ConversionError: A bytes parameter is not bytes and can't be converted
            to bytes.
        InvalidOptionError: A parameter is out of bounds.
    """
    super().__init__(
        secret,
        max_age=max_age,
        personalisation=personalisation,
        digest_size=digest_size,
        hasher=hasher,
        deterministic=deterministic,
        separator=separator,
        serializer=serializer,
        compressor=compressor,
        encoder=encoder,
        compression_flag=compression_flag,
        compression_ratio=compression_ratio,
    )

    if self._compression_flag in self._encoder.alphabet:
        raise errors.InvalidOptionError(
            'the compression flag character must not belong to the encoder alphabet',
        )

data_from_exc(exc)

Recover original data from an ExpiredSignatureError exception.

If the data was compressed, it will be decompressed before unserializing it.

The full flow is as follows, where optional actions are marked between brackets: data -> decode -> [decompress] -> unserialize

Parameters:

Name Type Description Default
exc errors.ExpiredSignatureError

An ExpiredSignatureError exception to extract the data from it.

required

Returns:

Type Description
typing.Any

Original data.

Raises:

Type Description
DecodeError

Data can't be decoded.

DecompressionError

Data can't be decompressed.

UnserializationError

Data can't be unserialized.

Source code in blake2signer/signers.py
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
def data_from_exc(self, exc: errors.ExpiredSignatureError) -> typing.Any:
    """Recover original data from an ExpiredSignatureError exception.

    If the data was compressed, it will be decompressed before unserializing it.

    The full flow is as follows, where optional actions are marked between brackets:
    data -> decode -> [decompress] -> unserialize

    Args:
        exc: An ExpiredSignatureError exception to extract the data from it.

    Returns:
        Original data.

    Raises:
        DecodeError: Data can't be decoded.
        DecompressionError: Data can't be decompressed.
        UnserializationError: Data can't be unserialized.
    """
    return self._loads(exc.data)

dump(data, file, *, compress=True, compression_level=None, force_compression=False, serializer_kwargs=None)

Serialize and sign data to file optionally compressing and/or timestamping it.

This method is identical to :meth:dumps, but it dumps into a file, which must be a .write()-supporting file-like object, and returns the written value for convenience.

Note that given data is not encrypted, only signed. To recover data from the produced string, while validating the signature (and timestamp if any), use loads.

Data will be serialized, optionally compressed, and encoded before being signed. This means that it must be of any type serializable by the chosen serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool, None or dict, or a composition of those (tuples are unserialized as lists).

If max_age was specified then the stream will be timestamped.

For deterministic signatures, no salt is used. For non-deterministic ones, the salt is a cryptographically secure pseudorandom string generated for this signature only (meaning that the signature always changes even when the payload stays the same).

The full flow is as follows, where optional actions are marked between brackets: data -> serialize -> [compress] -> [timestamp] -> encode -> sign

Parameters:

Name Type Description Default
data typing.Any

Any serializable object.

required
file typing.IO

A .write()-supporting file-like object.

required

Other Parameters:

Name Type Description
compress optional

Compress data (default) after serializing it and decompress it before unserializing. For low entropy payloads such as human-readable text, it's beneficial from around ~30bytes, and detrimental if smaller. For high entropy payloads like pseudorandom text, it's beneficial from around ~300bytes and detrimental if lower than ~100bytes. You can safely enable it since a size check is done so if compression turns detrimental then it won't be used. If you know from beforehand that data can't be compressed and don't want to waste resources trying, set it to False.

compression_level optional

Set the desired compression level when using compression, where 1 is the fastest and least compressed and 9 the slowest and most compressed. The default value depends on the compressor being used. Note that the performance impact is for both compression and decompression.

force_compression optional

Force compression even if it would be detrimental for performance or size. This parameter overrides compress.

serializer_kwargs optional

Provide keyword arguments for the serializer.

Returns:

Type Description
str

An encoded, signed and optionally timestamped string of serialized

str

and optionally compressed data. This value is safe for printing or

str

transmitting as it only contains the characters supported by the

str

encoder and the separator, which are ASCII.

Raises:

Type Description
SerializationError

Data can't be serialized.

CompressionError

Data can't be compressed or compression level is invalid.

EncodeError

Data can't be encoded.

FileError

File can't be written.

Source code in blake2signer/signers.py
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
def dump(
    self,
    data: typing.Any,
    file: typing.IO,
    *,
    compress: bool = True,
    compression_level: typing.Optional[int] = None,
    force_compression: bool = False,
    serializer_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> str:
    """Serialize and sign data to file optionally compressing and/or timestamping it.

    This method is identical to :meth:`dumps`, but it dumps into a file, which
    must be a `.write()`-supporting file-like object, and returns the written
    value for convenience.

    Note that given data is _not_ encrypted, only signed. To recover data from
    the produced string, while validating the signature (and timestamp if any),
    use `loads`.

    Data will be serialized, optionally compressed, and encoded before being
    signed. This means that it must be of any type serializable by the chosen
    serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool,
    None or dict, or a composition of those (tuples are unserialized as lists).

    If `max_age` was specified then the stream will be timestamped.

    For deterministic signatures, no salt is used. For non-deterministic ones,
    the salt is a cryptographically secure pseudorandom string generated for
    this signature only (meaning that the signature always changes even when
    the payload stays the same).

    The full flow is as follows, where optional actions are marked between brackets:
    data -> serialize -> [compress] -> [timestamp] -> encode -> sign

    Args:
        data: Any serializable object.
        file: A `.write()`-supporting file-like object.

    Keyword Args:
        compress (optional): Compress data (default) after serializing it and
            decompress it before unserializing. For low entropy payloads such
            as human-readable text, it's beneficial from around ~30bytes, and
            detrimental if smaller. For high entropy payloads like pseudorandom
            text, it's beneficial from around ~300bytes and detrimental if lower
            than ~100bytes. You can safely enable it since a size check is done
            so if compression turns detrimental then it won't be used. If you
            know from beforehand that data can't be compressed and don't want
            to waste resources trying, set it to False.
        compression_level (optional): Set the desired compression level when
            using compression, where 1 is the fastest and least compressed
            and 9 the slowest and most compressed. The default value depends
            on the compressor being used. Note that the performance impact is
            for both compression and decompression.
        force_compression (optional): Force compression even if it would be
            detrimental for performance or size. This parameter overrides
            `compress`.
        serializer_kwargs (optional): Provide keyword arguments for the serializer.

    Returns:
        An encoded, signed and optionally timestamped string of serialized
        and optionally compressed data. This value is safe for printing or
        transmitting as it only contains the characters supported by the
        encoder and the separator, which are ASCII.

    Raises:
        SerializationError: Data can't be serialized.
        CompressionError: Data can't be compressed or compression level is invalid.
        EncodeError: Data can't be encoded.
        FileError: File can't be written.
    """
    signed = self.dumps(
        data,
        compress=compress,
        compression_level=compression_level,
        force_compression=force_compression,
        serializer_kwargs=serializer_kwargs,
    )

    # Signed value is ASCII so ConversionError can't happen.
    self._write(file, signed)

    return signed

dumps(data, *, compress=True, compression_level=None, force_compression=False, serializer_kwargs=None)

Serialize and sign data, optionally compressing and/or timestamping it.

Note that given data is not encrypted, only signed. To recover data from the produced string, while validating the signature (and timestamp if any), use loads.

Data will be serialized, optionally compressed, and encoded before being signed. This means that it must be of any type serializable by the chosen serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool, None or dict, or a composition of those (tuples are unserialized as lists).

If max_age was specified then the stream will be timestamped.

For deterministic signatures, no salt is used. For non-deterministic ones, the salt is a cryptographically secure pseudorandom string generated for this signature only (meaning that the signature always changes even when the payload stays the same).

The full flow is as follows, where optional actions are marked between brackets: data -> serialize -> [compress] -> [timestamp] -> encode -> sign

Parameters:

Name Type Description Default
data typing.Any

Any serializable object.

required

Other Parameters:

Name Type Description
compress optional

Compress data (default) after serializing it and decompress it before unserializing. For low entropy payloads such as human-readable text, it's beneficial from around ~30bytes, and detrimental if smaller. For high entropy payloads like pseudorandom text, it's beneficial from around ~300bytes and detrimental if lower than ~100bytes. You can safely enable it since a size check is done so if compression turns detrimental then it won't be used. If you know from beforehand that data can't be compressed and don't want to waste resources trying, set it to False.

compression_level optional

Set the desired compression level when using compression, where 1 is the fastest and least compressed and 9 the slowest and most compressed. The default value depends on the compressor being used. Note that the performance impact is for both compression and decompression.

force_compression optional

Force compression even if it would be detrimental for performance or size. This parameter overrides compress.

serializer_kwargs optional

Provide keyword arguments for the serializer.

Returns:

Type Description
str

An encoded, signed and optionally timestamped string of serialized

str

and optionally compressed data. This value is safe for printing or

str

transmitting as it only contains the characters supported by the

str

encoder and the separator, which are ASCII.

Raises:

Type Description
SerializationError

Data can't be serialized.

CompressionError

Data can't be compressed or compression level is invalid.

EncodeError

Data can't be encoded.

Source code in blake2signer/signers.py
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
def dumps(
    self,
    data: typing.Any,
    *,
    compress: bool = True,
    compression_level: typing.Optional[int] = None,
    force_compression: bool = False,
    serializer_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> str:
    """Serialize and sign data, optionally compressing and/or timestamping it.

    Note that given data is _not_ encrypted, only signed. To recover data from
    the produced string, while validating the signature (and timestamp if any),
    use `loads`.

    Data will be serialized, optionally compressed, and encoded before being
    signed. This means that it must be of any type serializable by the chosen
    serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool,
    None or dict, or a composition of those (tuples are unserialized as lists).

    If `max_age` was specified then the stream will be timestamped.

    For deterministic signatures, no salt is used. For non-deterministic ones,
    the salt is a cryptographically secure pseudorandom string generated for
    this signature only (meaning that the signature always changes even when
    the payload stays the same).

    The full flow is as follows, where optional actions are marked between brackets:
    data -> serialize -> [compress] -> [timestamp] -> encode -> sign

    Args:
        data: Any serializable object.

    Keyword Args:
        compress (optional): Compress data (default) after serializing it and
            decompress it before unserializing. For low entropy payloads such
            as human-readable text, it's beneficial from around ~30bytes, and
            detrimental if smaller. For high entropy payloads like pseudorandom
            text, it's beneficial from around ~300bytes and detrimental if lower
            than ~100bytes. You can safely enable it since a size check is done
            so if compression turns detrimental then it won't be used. If you
            know from beforehand that data can't be compressed and don't want
            to waste resources trying, set it to False.
        compression_level (optional): Set the desired compression level when
            using compression, where 1 is the fastest and least compressed
            and 9 the slowest and most compressed. The default value depends
            on the compressor being used. Note that the performance impact is
            for both compression and decompression.
        force_compression (optional): Force compression even if it would be
            detrimental for performance or size. This parameter overrides
            `compress`.
        serializer_kwargs (optional): Provide keyword arguments for the serializer.

    Returns:
        An encoded, signed and optionally timestamped string of serialized
        and optionally compressed data. This value is safe for printing or
        transmitting as it only contains the characters supported by the
        encoder and the separator, which are ASCII.

    Raises:
        SerializationError: Data can't be serialized.
        CompressionError: Data can't be compressed or compression level is invalid.
        EncodeError: Data can't be encoded.
    """
    dump = self._dumps(
        data,
        compress=compress,
        compression_level=compression_level,
        force_compression=force_compression,
        serializer_kwargs=serializer_kwargs,
    )

    # since everything is ASCII, decoding is safe
    return self._compose(dump, signature=self._proper_sign(dump)).decode()

dumps_parts(data, *, compress=True, compression_level=None, force_compression=False, serializer_kwargs=None)

Serialize and sign data, optionally compressing and/or timestamping it.

This method is identical to dumps, but it dumps into a container instead of a stream, in case of needing to handle data and signature separately.

Note that given data is not encrypted, only signed. To recover data from the produced string, while validating the signature (and timestamp if any), use loads_parts.

Data will be serialized, optionally compressed, and encoded before being signed. This means that it must be of any type serializable by the chosen serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool, None or dict, or a composition of those (tuples are unserialized as lists).

If max_age was specified then the stream will be timestamped.

For deterministic signatures, no salt is used. For non-deterministic ones, the salt is a cryptographically secure pseudorandom string generated for this signature only (meaning that the signature always changes even when the payload stays the same).

The full flow is as follows, where optional actions are marked between brackets: data -> serialize -> [compress] -> [timestamp] -> encode -> sign

Parameters:

Name Type Description Default
data typing.Any

Any serializable object.

required

Other Parameters:

Name Type Description
compress optional

Compress data (default) after serializing it and decompress it before unserializing. For low entropy payloads such as human-readable text, it's beneficial from around ~30bytes, and detrimental if smaller. For high entropy payloads like pseudorandom text, it's beneficial from around ~300bytes and detrimental if lower than ~100bytes. You can safely enable it since a size check is done so if compression turns detrimental then it won't be used. If you know from beforehand that data can't be compressed and don't want to waste resources trying, set it to False.

compression_level optional

Set the desired compression level when using compression, where 1 is the fastest and least compressed and 9 the slowest and most compressed. The default value depends on the compressor being used. Note that the performance impact is for both compression and decompression.

force_compression optional

Force compression even if it would be detrimental for performance or size. This parameter overrides compress.

serializer_kwargs optional

Provide keyword arguments for the serializer.

Returns:

Type Description
Blake2SignatureDump

A container with an encoded, signed and optionally timestamped string

Blake2SignatureDump

of serialized and optionally compressed data. This value is safe for

Blake2SignatureDump

printing or transmitting as it only contains the characters supported

Blake2SignatureDump

by the encoder and the separator, which are ASCII.

Raises:

Type Description
SerializationError

Data can't be serialized.

CompressionError

Data can't be compressed or compression level is invalid.

EncodeError

Data can't be encoded.

Source code in blake2signer/signers.py
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
def dumps_parts(
    self,
    data: typing.Any,
    *,
    compress: bool = True,
    compression_level: typing.Optional[int] = None,
    force_compression: bool = False,
    serializer_kwargs: typing.Optional[typing.Dict[str, typing.Any]] = None,
) -> Blake2SignatureDump:
    """Serialize and sign data, optionally compressing and/or timestamping it.

    This method is identical to `dumps`, but it dumps into a container instead
    of a stream, in case of needing to handle data and signature separately.

    Note that given data is _not_ encrypted, only signed. To recover data from
    the produced string, while validating the signature (and timestamp if any),
    use `loads_parts`.

    Data will be serialized, optionally compressed, and encoded before being
    signed. This means that it must be of any type serializable by the chosen
    serializer, i.e. for a JSON serializer: str, int, float, list, tuple, bool,
    None or dict, or a composition of those (tuples are unserialized as lists).

    If `max_age` was specified then the stream will be timestamped.

    For deterministic signatures, no salt is used. For non-deterministic ones,
    the salt is a cryptographically secure pseudorandom string generated for
    this signature only (meaning that the signature always changes even when
    the payload stays the same).

    The full flow is as follows, where optional actions are marked between brackets:
    data -> serialize -> [compress] -> [timestamp] -> encode -> sign

    Args:
        data: Any serializable object.

    Keyword Args:
        compress (optional): Compress data (default) after serializing it and
            decompress it before unserializing. For low entropy payloads such
            as human-readable text, it's beneficial from around ~30bytes, and
            detrimental if smaller. For high entropy payloads like pseudorandom
            text, it's beneficial from around ~300bytes and detrimental if lower
            than ~100bytes. You can safely enable it since a size check is done
            so if compression turns detrimental then it won't be used. If you
            know from beforehand that data can't be compressed and don't want
            to waste resources trying, set it to False.
        compression_level (optional): Set the desired compression level when
            using compression, where 1 is the fastest and least compressed
            and 9 the slowest and most compressed. The default value depends
            on the compressor being used. Note that the performance impact is
            for both compression and decompression.
        force_compression (optional): Force compression even if it would be
            detrimental for performance or size. This parameter overrides
            `compress`.
        serializer_kwargs (optional): Provide keyword arguments for the serializer.

    Returns:
        A container with an encoded, signed and optionally timestamped string
        of serialized and optionally compressed data. This value is safe for
        printing or transmitting as it only contains the characters supported
        by the encoder and the separator, which are ASCII.

    Raises:
        SerializationError: Data can't be serialized.
        CompressionError: Data can't be compressed or compression level is invalid.
        EncodeError: Data can't be encoded.
    """
    dump = self._dumps(
        data,
        compress=compress,
        compression_level=compression_level,
        force_compression=force_compression,
        serializer_kwargs=serializer_kwargs,
    )

    # since everything is ASCII, decoding is safe
    return Blake2SignatureDump(
        data=dump.decode(),
        signature=self._proper_sign(dump).decode(),
    )

load(file)

Recover original data from a signed serialized file from dump.

This method is identical to loads, but it reads a file, which must be a .read()-supporting file-like object.

If max_age was specified then it will be ensured that the signature is not older than that time in seconds.

If the data was compressed, it will be decompressed before unserializing it.

The full flow is as follows, where optional actions are marked between brackets: data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

Parameters:

Name Type Description Default
file typing.IO

A .read()-supporting file-like object containing data to unsign.

required

Returns:

Type Description
typing.Any

Original data.

Raises:

Type Description
ConversionError

Signed data can't be converted to bytes.

SignatureError

Signed data structure is not valid.

InvalidSignatureError

Signed data signature is invalid.

ExpiredSignatureError

Signed data signature has expired.

DecodeError

Signed data can't be decoded.

DecompressionError

Signed data can't be decompressed.

UnserializationError

Signed data can't be unserialized.

FileError

File can't be read.

Source code in blake2signer/signers.py
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
def load(self, file: typing.IO) -> typing.Any:
    """Recover original data from a signed serialized file from `dump`.

    This method is identical to `loads`, but it reads a file, which
    must be a `.read()`-supporting file-like object.

    If `max_age` was specified then it will be ensured that the signature is
    not older than that time in seconds.

    If the data was compressed, it will be decompressed before unserializing it.

    The full flow is as follows, where optional actions are marked between brackets:
    data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

    Args:
        file: A `.read()`-supporting file-like object containing data to unsign.

    Returns:
        Original data.

    Raises:
        ConversionError: Signed data can't be converted to bytes.
        SignatureError: Signed data structure is not valid.
        InvalidSignatureError: Signed data signature is invalid.
        ExpiredSignatureError: Signed data signature has expired.
        DecodeError: Signed data can't be decoded.
        DecompressionError: Signed data can't be decompressed.
        UnserializationError: Signed data can't be unserialized.
        FileError: File can't be read.
    """
    return self.loads(self._read(file))

loads(signed_data)

Recover original data from a signed serialized string from dumps.

If max_age was specified then it will be ensured that the signature is not older than that time in seconds.

If the data was compressed, it will be decompressed before unserializing it.

The full flow is as follows, where optional actions are marked between brackets: data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

Parameters:

Name Type Description Default
signed_data typing.Union[str, bytes]

Signed data to unsign.

required

Returns:

Type Description
typing.Any

Original data.

Raises:

Type Description
ConversionError

Signed data can't be converted to bytes.

SignatureError

Signed data structure is not valid.

InvalidSignatureError

Signed data signature is invalid.

ExpiredSignatureError

Signed data signature has expired.

DecodeError

Signed data can't be decoded.

DecompressionError

Signed data can't be decompressed.

UnserializationError

Signed data can't be unserialized.

Source code in blake2signer/signers.py
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
def loads(self, signed_data: typing.Union[str, bytes]) -> typing.Any:
    """Recover original data from a signed serialized string from `dumps`.

    If `max_age` was specified then it will be ensured that the signature is
    not older than that time in seconds.

    If the data was compressed, it will be decompressed before unserializing it.

    The full flow is as follows, where optional actions are marked between brackets:
    data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

    Args:
        signed_data: Signed data to unsign.

    Returns:
        Original data.

    Raises:
        ConversionError: Signed data can't be converted to bytes.
        SignatureError: Signed data structure is not valid.
        InvalidSignatureError: Signed data signature is invalid.
        ExpiredSignatureError: Signed data signature has expired.
        DecodeError: Signed data can't be decoded.
        DecompressionError: Signed data can't be decompressed.
        UnserializationError: Signed data can't be unserialized.
    """
    parts = self._decompose(self._force_bytes(signed_data))

    return self._loads(self._proper_unsign(parts))

loads_parts(signature)

Recover original data from a signed serialized container from dumps_parts.

This method is identical to loads, but it reads a container instead of a stream.

If max_age was specified then it will be ensured that the signature is not older than that time in seconds.

If the data was compressed, it will be decompressed before unserializing it.

The full flow is as follows, where optional actions are marked between brackets: data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

Parameters:

Name Type Description Default
signature typing.Union[Blake2Signature, Blake2SignatureDump]

Signed data container to unsign.

required

Returns:

Type Description
typing.Any

Original data.

Raises:

Type Description
ConversionError

Signed data can't be converted to bytes.

SignatureError

Signed data structure is not valid.

InvalidSignatureError

Signed data signature is invalid.

ExpiredSignatureError

Signed data signature has expired.

DecodeError

Signed data can't be decoded.

DecompressionError

Signed data can't be decompressed.

UnserializationError

Signed data can't be unserialized.

Source code in blake2signer/signers.py
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
def loads_parts(
    self,
    signature: typing.Union[Blake2Signature, Blake2SignatureDump],
) -> typing.Any:
    """Recover original data from a signed serialized container from `dumps_parts`.

    This method is identical to `loads`, but it reads a container instead of
    a stream.

    If `max_age` was specified then it will be ensured that the signature is
    not older than that time in seconds.

    If the data was compressed, it will be decompressed before unserializing it.

    The full flow is as follows, where optional actions are marked between brackets:
    data -> check sig -> [check timestamp] -> decode -> [decompress] -> unserialize

    Args:
        signature: Signed data container to unsign.

    Returns:
        Original data.

    Raises:
        ConversionError: Signed data can't be converted to bytes.
        SignatureError: Signed data structure is not valid.
        InvalidSignatureError: Signed data signature is invalid.
        ExpiredSignatureError: Signed data signature has expired.
        DecodeError: Signed data can't be decoded.
        DecompressionError: Signed data can't be decompressed.
        UnserializationError: Signed data can't be unserialized.
    """
    sig = self._force_bytes_parts(signature)
    # It's easier to just join the parts together and unsign the stream.
    signed_data = self._compose(sig.data, signature=sig.signature)

    return self.loads(signed_data)