diff --git a/src/bindings/crypto_core_ristretto255.h b/src/bindings/crypto_core_ristretto255.h index 5e6f20ad6..fbd69011e 100644 --- a/src/bindings/crypto_core_ristretto255.h +++ b/src/bindings/crypto_core_ristretto255.h @@ -1,4 +1,4 @@ -/* Copyright 2020 Donald Stufft and individual contributors +/* Copyright 2021 Donald Stufft and individual contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,8 @@ * limitations under the License. */ +static const int PYNACL_HAS_CRYPTO_CORE_RISTRETTO25519; + size_t crypto_core_ristretto255_scalarbytes(void); size_t crypto_core_ristretto255_nonreducedscalarbytes(void); diff --git a/src/bindings/crypto_scalarmult_ristretto255.h b/src/bindings/crypto_scalarmult_ristretto255.h index 354592427..befe787a0 100644 --- a/src/bindings/crypto_scalarmult_ristretto255.h +++ b/src/bindings/crypto_scalarmult_ristretto255.h @@ -1,4 +1,4 @@ -/* Copyright 2020 Donald Stufft and individual contributors +/* Copyright 2021 Donald Stufft and individual contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,8 @@ * limitations under the License. */ +static const int PYNACL_HAS_CRYPTO_SCALARMULT_RISTRETTO25519; + size_t crypto_scalarmult_ristretto255_bytes(void); size_t crypto_scalarmult_ristretto255_scalarbytes(void); diff --git a/src/bindings/minimal/crypto_core_ristretto255.h b/src/bindings/minimal/crypto_core_ristretto255.h new file mode 100644 index 000000000..ef1d1ddbc --- /dev/null +++ b/src/bindings/minimal/crypto_core_ristretto255.h @@ -0,0 +1,41 @@ +/* Copyright 2021 Donald Stufft and individual contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef SODIUM_LIBRARY_MINIMAL +static const int PYNACL_HAS_CRYPTO_CORE_RISTRETTO25519 = 0; + +size_t (*crypto_core_ristretto255_scalarbytes)(void) = NULL; +size_t (*crypto_core_ristretto255_nonreducedscalarbytes)(void) = NULL; + +void (*crypto_core_ristretto255_scalar_add)(unsigned char *, const unsigned char *, const unsigned char *) = NULL; +void (*crypto_core_ristretto255_scalar_complement)(unsigned char *, const unsigned char *) = NULL; +int (*crypto_core_ristretto255_scalar_invert)(unsigned char *, const unsigned char *) = NULL; +void (*crypto_core_ristretto255_scalar_mul)(unsigned char *, const unsigned char *, const unsigned char *) = NULL; +void (*crypto_core_ristretto255_scalar_negate)(unsigned char *, const unsigned char *) = NULL; +void (*crypto_core_ristretto255_scalar_random)(unsigned char *) = NULL; +void (*crypto_core_ristretto255_scalar_reduce)(unsigned char *, const unsigned char *) = NULL; +void (*crypto_core_ristretto255_scalar_sub)(unsigned char *, const unsigned char *, const unsigned char *) = NULL; + +size_t (*crypto_core_ristretto255_bytes)(void) = NULL; +size_t (*crypto_core_ristretto255_hashbytes)(void) = NULL; + +int (*crypto_core_ristretto255_add)(unsigned char *, const unsigned char *, const unsigned char *) = NULL; +int (*crypto_core_ristretto255_from_hash)(unsigned char *, const unsigned char *) = NULL; +int (*crypto_core_ristretto255_is_valid_point)(const unsigned char *) = NULL; +int (*crypto_core_ristretto255_sub)(unsigned char *, const unsigned char *, const unsigned char *) = NULL; +void (*crypto_core_ristretto255_random)(unsigned char *) = NULL; +#else +static const int PYNACL_HAS_CRYPTO_CORE_RISTRETTO25519 = 1; +#endif diff --git a/src/bindings/minimal/crypto_scalarmult_ristretto255.h b/src/bindings/minimal/crypto_scalarmult_ristretto255.h new file mode 100644 index 000000000..f02a001fd --- /dev/null +++ b/src/bindings/minimal/crypto_scalarmult_ristretto255.h @@ -0,0 +1,26 @@ +/* Copyright 2021 Donald Stufft and individual contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef SODIUM_LIBRARY_MINIMAL +static const int PYNACL_HAS_CRYPTO_SCALARMULT_RISTRETTO25519 = 0; + +size_t (*crypto_scalarmult_ristretto255_bytes)(void) = NULL; +size_t (*crypto_scalarmult_ristretto255_scalarbytes)(void) = NULL; + +int (*crypto_scalarmult_ristretto255_base)(unsigned char *q, const unsigned char *n) = NULL; +int (*crypto_scalarmult_ristretto255)(unsigned char *q, const unsigned char *n, const unsigned char *p) = NULL; +#else +static const int PYNACL_HAS_CRYPTO_SCALARMULT_RISTRETTO25519 = 1; +#endif diff --git a/src/nacl/bindings/__init__.py b/src/nacl/bindings/__init__.py index 20f9a9609..08897ecbf 100644 --- a/src/nacl/bindings/__init__.py +++ b/src/nacl/bindings/__init__.py @@ -91,6 +91,7 @@ crypto_core_ristretto255_scalar_reduce, crypto_core_ristretto255_scalar_sub, crypto_core_ristretto255_sub, + has_crypto_core_ristretto25519, ) from nacl.bindings.crypto_generichash import ( crypto_generichash_BYTES, @@ -199,6 +200,7 @@ crypto_scalarmult_ristretto255_BYTES, crypto_scalarmult_ristretto255_SCALAR_BYTES, crypto_scalarmult_ristretto255_base, + has_crypto_scalarmult_ristretto25519, ) from nacl.bindings.crypto_secretbox import ( crypto_secretbox, @@ -323,6 +325,7 @@ "crypto_core_ed25519_scalar_sub", "crypto_core_ed25519_scalar_mul", "crypto_core_ed25519_scalar_reduce", + "has_crypto_core_ristretto25519", "crypto_core_ristretto255_SCALAR_BYTES", "crypto_core_ristretto255_NONREDUCED_SCALAR_BYTES", "crypto_core_ristretto255_GROUP_ORDER", @@ -379,6 +382,7 @@ "crypto_scalarmult_ed25519_base", "crypto_scalarmult_ed25519_noclamp", "crypto_scalarmult_ed25519_base_noclamp", + "has_crypto_scalarmult_ristretto25519", "crypto_scalarmult_ristretto255_BYTES", "crypto_scalarmult_ristretto255_SCALAR_BYTES", "crypto_scalarmult_ristretto255_base", diff --git a/src/nacl/bindings/crypto_core_ristretto255.py b/src/nacl/bindings/crypto_core_ristretto255.py index 5a63e9c99..39323fa50 100644 --- a/src/nacl/bindings/crypto_core_ristretto255.py +++ b/src/nacl/bindings/crypto_core_ristretto255.py @@ -1,4 +1,4 @@ -# Copyright 2020 Donald Stufft and individual contributors +# Copyright 2021 Donald Stufft and individual contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,21 +18,28 @@ from nacl._sodium import ffi, lib from nacl.exceptions import ensure +has_crypto_core_ristretto25519 = bool( + lib.PYNACL_HAS_CRYPTO_CORE_RISTRETTO25519 +) # Group order L of both the scalar group and group of points. crypto_core_ristretto255_GROUP_ORDER = ( 2 ** 252 + 27742317777372353535851937790883648493 ) -# Size of a Ristretto255 scalar. -crypto_core_ristretto255_SCALAR_BYTES = ( - lib.crypto_core_ristretto255_scalarbytes() -) +if has_crypto_core_ristretto25519: + # Size of a Ristretto255 scalar. + crypto_core_ristretto255_SCALAR_BYTES = ( + lib.crypto_core_ristretto255_scalarbytes() + ) -# Size of values that are reduced modulo the order to a Ristretto255 scalar. -crypto_core_ristretto255_NONREDUCED_SCALAR_BYTES = ( - lib.crypto_core_ristretto255_nonreducedscalarbytes() -) + # Size of values that are reduced modulo the order to a Ristretto255 scalar. + crypto_core_ristretto255_NONREDUCED_SCALAR_BYTES = ( + lib.crypto_core_ristretto255_nonreducedscalarbytes() + ) +else: # pragma: no cover + crypto_core_ristretto255_SCALAR_BYTES = 0 + crypto_core_ristretto255_NONREDUCED_SCALAR_BYTES = 0 def crypto_core_ristretto255_scalar_add(x, y): @@ -46,7 +53,15 @@ def crypto_core_ristretto255_scalar_add(x, y): bytes in little endian order representing the second scalar :type y: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(x, bytes) and len(x) == crypto_core_ristretto255_SCALAR_BYTES, @@ -79,7 +94,15 @@ def crypto_core_ristretto255_scalar_complement(s): bytes in little endian order representing the scalar :type s: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(s, bytes) and len(s) == crypto_core_ristretto255_SCALAR_BYTES, @@ -105,7 +128,15 @@ def crypto_core_ristretto255_scalar_invert(s): :type s: bytes :rtype: bytes :raises ValueError: if the value is not invertible + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(s, bytes) and len(s) == crypto_core_ristretto255_SCALAR_BYTES, @@ -134,7 +165,15 @@ def crypto_core_ristretto255_scalar_mul(x, y): bytes in little endian order representing the second scalar :type y: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(x, bytes) and len(x) == crypto_core_ristretto255_SCALAR_BYTES, @@ -168,7 +207,14 @@ def crypto_core_ristretto255_scalar_negate(s): bytes in little endian order representing the scalar :type s: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) ensure( isinstance(s, bytes) and len(s) == crypto_core_ristretto255_SCALAR_BYTES, @@ -189,7 +235,15 @@ def crypto_core_ristretto255_scalar_random(): Generate a random non-zero scalar modulo ``L``. :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + r = ffi.new("unsigned char[]", crypto_core_ristretto255_SCALAR_BYTES) lib.crypto_core_ristretto255_scalar_random(r) @@ -207,7 +261,15 @@ def crypto_core_ristretto255_scalar_reduce(s): to a Ristretto255 scalar :type s: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(s, bytes) and len(s) == crypto_core_ristretto255_NONREDUCED_SCALAR_BYTES, @@ -234,7 +296,15 @@ def crypto_core_ristretto255_scalar_sub(x, y): bytes in little endian order representing the second scalar :type y: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(x, bytes) and len(x) == crypto_core_ristretto255_SCALAR_BYTES, @@ -259,11 +329,17 @@ def crypto_core_ristretto255_scalar_sub(x, y): return ffi.buffer(z, crypto_core_ristretto255_SCALAR_BYTES)[:] -# Size of a Ristretto255 point. -crypto_core_ristretto255_BYTES = lib.crypto_core_ristretto255_bytes() +if has_crypto_core_ristretto25519: + # Size of a Ristretto255 point. + crypto_core_ristretto255_BYTES = lib.crypto_core_ristretto255_bytes() -# Size of the input to crypto_core_ristretto255_from_hash -crypto_core_ristretto255_HASH_BYTES = lib.crypto_core_ristretto255_hashbytes() + # Size of the input to crypto_core_ristretto255_from_hash + crypto_core_ristretto255_HASH_BYTES = ( + lib.crypto_core_ristretto255_hashbytes() + ) +else: # pragma: no cover + crypto_core_ristretto255_BYTES = 0 + crypto_core_ristretto255_HASH_BYTES = 0 def crypto_core_ristretto255_add(p, q): @@ -277,7 +353,15 @@ def crypto_core_ristretto255_add(p, q): bytes representing the second point :type q: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(p, bytes) and len(p) == crypto_core_ristretto255_BYTES, "First point must be a sequence of {} bytes".format( @@ -311,7 +395,15 @@ def crypto_core_ristretto255_from_hash(r): bytes representing the value to convert :type r: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(r, bytes) and len(r) == crypto_core_ristretto255_HASH_BYTES, "Input must be a sequence of {} bytes".format( @@ -337,7 +429,15 @@ def crypto_core_ristretto255_is_valid_point(p): :type p: bytes :return: False if invalid, True if valid :rtype: bool + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(p, bytes) and len(p) == crypto_core_ristretto255_BYTES, "Input must be a sequence of {} bytes".format( @@ -357,7 +457,15 @@ def crypto_core_ristretto255_random(): although astronomically unlikely, the zero point. :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + p = ffi.new("unsigned char[]", crypto_core_ristretto255_BYTES) lib.crypto_core_ristretto255_random(p) @@ -375,7 +483,15 @@ def crypto_core_ristretto255_sub(p, q): bytes representing the second point :type q: bytes :rtype: bytes + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_core_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(p, bytes) and len(p) == crypto_core_ristretto255_BYTES, "First point must be a sequence of {} bytes".format( diff --git a/src/nacl/bindings/crypto_scalarmult_ristretto255.py b/src/nacl/bindings/crypto_scalarmult_ristretto255.py index 2d89f61b6..66b31c75f 100644 --- a/src/nacl/bindings/crypto_scalarmult_ristretto255.py +++ b/src/nacl/bindings/crypto_scalarmult_ristretto255.py @@ -1,4 +1,4 @@ -# Copyright 2020 Donald Stufft and individual contributors +# Copyright 2021 Donald Stufft and individual contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,16 +18,24 @@ from nacl._sodium import ffi, lib from nacl.exceptions import ensure -# Size of a Ristretto255 point. -# Should equal crypto_core_ristretto255_BYTES -crypto_scalarmult_ristretto255_BYTES = ( - lib.crypto_scalarmult_ristretto255_bytes() +has_crypto_scalarmult_ristretto25519 = bool( + lib.PYNACL_HAS_CRYPTO_SCALARMULT_RISTRETTO25519 ) -# Size of scalars for the two functions. -crypto_scalarmult_ristretto255_SCALAR_BYTES = ( - lib.crypto_scalarmult_ristretto255_scalarbytes() -) +if has_crypto_scalarmult_ristretto25519: + # Size of a Ristretto255 point. + # Should equal crypto_core_ristretto255_BYTES + crypto_scalarmult_ristretto255_BYTES = ( + lib.crypto_scalarmult_ristretto255_bytes() + ) + + # Size of scalars for the two functions. + crypto_scalarmult_ristretto255_SCALAR_BYTES = ( + lib.crypto_scalarmult_ristretto255_scalarbytes() + ) +else: # pragma: no cover + crypto_scalarmult_ristretto255_BYTES = 0 + crypto_scalarmult_ristretto255_SCALAR_BYTES = 0 def crypto_scalarmult_ristretto255_base(n): @@ -40,7 +48,15 @@ def crypto_scalarmult_ristretto255_base(n): :type n: bytes :rtype: bytes :raises exc.RuntimeError: on error or if result is zero + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_scalarmult_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(n, bytes) and len(n) == crypto_scalarmult_ristretto255_SCALAR_BYTES, @@ -74,7 +90,15 @@ def crypto_scalarmult_ristretto255(n, p): :type p: bytes :rtype: bytes :raises exc.RuntimeError: on error or if result is zero + :raises nacl.exceptions.UnavailableError: If called when using a + minimal build of libsodium. """ + ensure( + has_crypto_scalarmult_ristretto25519, + "Not available in minimal build", + raising=exc.UnavailableError, + ) + ensure( isinstance(n, bytes) and len(n) == crypto_scalarmult_ristretto255_SCALAR_BYTES, diff --git a/src/nacl/ristretto.py b/src/nacl/ristretto.py index 47c417c89..aba1ffc97 100644 --- a/src/nacl/ristretto.py +++ b/src/nacl/ristretto.py @@ -1,4 +1,4 @@ -# Copyright 2013 Donald Stufft and individual contributors +# Copyright 2021 Donald Stufft and individual contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,19 +14,11 @@ from __future__ import absolute_import, division, print_function -from binascii import hexlify from fractions import Fraction -import six - import nacl.bindings from nacl import exceptions as exc -from nacl.utils import ( - bytes_as_string, - int_to_little_endian, - little_endian_to_int, - random, -) +from nacl.utils import random class Ristretto255Scalar(object): @@ -43,9 +35,9 @@ def __init__(self, value): if len(value) != Ristretto255Scalar.SIZE: raise exc.ValueError self._value = value - elif isinstance(value, six.integer_types): - self._value = int_to_little_endian( - value % Ristretto255Scalar.ORDER, Ristretto255Scalar.SIZE + elif isinstance(value, int): + self._value = (value % Ristretto255Scalar.ORDER).to_bytes( + Ristretto255Scalar.SIZE, "little" ) elif isinstance(value, Fraction): self._value = ( @@ -167,35 +159,36 @@ def __bytes__(self): return self._value def __int__(self): - return little_endian_to_int(self._value) + return int.from_bytes(self._value, "little") def __bool__(self): return not nacl.bindings.sodium_is_zero(self._value) - def __nonzero__(self): - return self.__bool__() - def __repr__(self): return "Ristretto255Scalar({})".format(int(self)) def __str__(self): - if six.PY2: - return self.__bytes__() - else: - return repr(self) + return repr(self) - def __unicode__(self): - return repr(self).decode() +if nacl.bindings.has_crypto_core_ristretto25519: + # Neutral additive element + Ristretto255Scalar.ZERO = Ristretto255Scalar(0) -# Neutral additive element -Ristretto255Scalar.ZERO = Ristretto255Scalar(0) + # Neutral multiplicative element + Ristretto255Scalar.ONE = Ristretto255Scalar(1) -# Neutral multiplicative element -Ristretto255Scalar.ONE = Ristretto255Scalar(1) + # Constant needed for inverting points + Ristretto255Scalar.MINUS_ONE = Ristretto255Scalar(-1) +else: # pragma: no cover -# Constant needed for inverting points -Ristretto255Scalar.MINUS_ONE = Ristretto255Scalar(-1) + Ristretto255Scalar.ZERO = Ristretto255Scalar( + bytes(Ristretto255Scalar.SIZE) + ) + Ristretto255Scalar.ONE = Ristretto255Scalar(bytes(Ristretto255Scalar.SIZE)) + Ristretto255Scalar.MINUS_ONE = Ristretto255Scalar( + bytes(Ristretto255Scalar.SIZE) + ) class Ristretto255Point(object): @@ -301,9 +294,6 @@ def __bool__(self): """ return not nacl.bindings.sodium_is_zero(self._value) - def __nonzero__(self): - return self.__bool__() - def __eq__(self, other): if not isinstance(other, self.__class__): return False @@ -320,21 +310,13 @@ def __hash__(self): return hash(self._value) def __repr__(self): - return "Ristretto255Point('{}')".format( - bytes_as_string(hexlify(bytes(self))) - ) + return "Ristretto255Point({!r})".format(bytes(self)) def __str__(self): - if six.PY2: - return self.__bytes__() - else: - return repr(self) - - def __unicode__(self): - return repr(self).decode() + return "Ristretto255Point({})".format(bytes(self).hex()) # Neutral element Ristretto255Point.ZERO = Ristretto255Point( - b"\x00" * Ristretto255Point.SIZE, _assume_valid=True + bytes(Ristretto255Point.SIZE), _assume_valid=True ) diff --git a/src/nacl/utils.py b/src/nacl/utils.py index c9a669ae8..6f1b782a4 100644 --- a/src/nacl/utils.py +++ b/src/nacl/utils.py @@ -74,26 +74,3 @@ def randombytes_deterministic(size, seed, encoder=encoding.RawEncoder): raw_data = nacl.bindings.randombytes_buf_deterministic(size, seed) return encoder.encode(raw_data) - - -def int_to_little_endian(value, size): - if six.PY3: - return value.to_bytes(size, "little") - else: - result = b"" - for i in range(size): - result += chr(value & 0xFF) - value >>= 8 - if value: - raise OverflowError - return result - - -def little_endian_to_int(value): - if six.PY3: - return int.from_bytes(value, "little") - else: - result = 0 - for byte in reversed(value): - result = (result << 8) | ord(byte) - return result diff --git a/tests/test_bindings.py b/tests/test_bindings.py index c125f667d..5f4cba21b 100644 --- a/tests/test_bindings.py +++ b/tests/test_bindings.py @@ -328,7 +328,7 @@ def test_sodium_is_zero(): assert c.sodium_is_zero(b"\x00" * 37) assert not c.sodium_is_zero(b"\x00" * 13 + b"\xe1" + b"\x00" * 22) with pytest.raises(TypeError): - c.sodium_is_zero(u"zero") + c.sodium_is_zero("zero") @given(integers(min_value=-2, max_value=0)) @@ -886,3 +886,51 @@ def test_scalarmult_ed25519_unavailable(): c.crypto_scalarmult_ed25519(zero, zero) with pytest.raises(UnavailableError): c.crypto_scalarmult_ed25519_noclamp(zero, zero) + + +@pytest.mark.skipif( + c.has_crypto_core_ristretto25519, + reason="Requires minimal build of libsodium", +) +def test_core_ristretto25519_unavailable(): + zero = 32 * b"\x00" + + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_add(zero, zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_complement(zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_invert(zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_mul(zero, zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_negate(zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_random() + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_reduce(zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_scalar_sub(zero, zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_add(zero, zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_from_hash(zero + zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_is_valid_point(zero) + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_random() + with pytest.raises(UnavailableError): + c.crypto_core_ristretto255_sub(zero, zero) + + +@pytest.mark.skipif( + c.has_crypto_scalarmult_ristretto25519, + reason="Requires minimal build of libsodium", +) +def test_scalarmult_ristretto25519_unavailable(): + zero = 32 * b"\x00" + + with pytest.raises(UnavailableError): + c.crypto_scalarmult_ristretto255_base(zero) + with pytest.raises(UnavailableError): + c.crypto_scalarmult_ristretto255(zero, zero) diff --git a/tests/test_ristretto.py b/tests/test_ristretto.py index d13a2bb4f..b42300b61 100644 --- a/tests/test_ristretto.py +++ b/tests/test_ristretto.py @@ -1,4 +1,4 @@ -# Copyright 2020 Donald Stufft and individual contributors +# Copyright 2021 Donald Stufft and individual contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ import json import os -from binascii import unhexlify from fractions import Fraction from functools import reduce from hashlib import sha256, sha512 @@ -25,9 +24,11 @@ import pytest -from six import int2byte - import nacl.exceptions as exc +from nacl.bindings import ( + has_crypto_core_ristretto25519, + has_crypto_scalarmult_ristretto25519, +) from nacl.ristretto import Ristretto255Point, Ristretto255Scalar @@ -41,12 +42,14 @@ def _ristretto255_vectors(): return { "encodings_of_small_multiples": [ - (idx, unhexlify(enc)) + (idx, bytes.fromhex(enc)) for idx, enc in enumerate(vectors["encodings_of_small_multiples"]) ], - "bad_encodings": [unhexlify(enc) for enc in vectors["bad_encodings"]], + "bad_encodings": [ + bytes.fromhex(enc) for enc in vectors["bad_encodings"] + ], "label_hash_to_points": [ - (label, unhexlify(enc)) + (label, bytes.fromhex(enc)) for label, enc in zip( vectors["labels"], vectors["encoded_hash_to_points"] ) @@ -56,10 +59,14 @@ def _ristretto255_vectors(): class TestRistretto255Scalar(object): order = 7237005577332262213973186563042994240857116359379907606001950938285454250989 - order_bytes = unhexlify( + order_bytes = bytes.fromhex( "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010" ) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_init(self): dgst = sha256(b"hello").digest() @@ -77,7 +84,7 @@ def test_init(self): bytes(Ristretto255Scalar(Fraction(5, 1))) == b"\x05" + b"\x00" * 31 ) # (pow(3, -1, order) * 5 % order).to_bytes(32, "little").hex() - five_thirds = unhexlify( + five_thirds = bytes.fromhex( "a646a7c9082106c89c8952364a534a5c55555555555555555555555555555505" ) assert bytes(Ristretto255Scalar(Fraction(5, 3))) == five_thirds @@ -88,6 +95,10 @@ def test_init(self): with pytest.raises(exc.TypeError): Ristretto255Scalar(3.14) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_random(self): s = Ristretto255Scalar.random() t = Ristretto255Scalar.random() @@ -96,6 +107,10 @@ def test_random(self): # it can only be a serious bug because of the huge group size. assert s != t + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_random_zero(self): s = Ristretto255Scalar.random_zero() t = Ristretto255Scalar.random_zero() @@ -104,6 +119,10 @@ def test_random_zero(self): # it can only be a serious bug because of the huge group size. assert s != t + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_reduce(self): assert ( bytes(Ristretto255Scalar.reduce(b"\xcd\xab" + b"\x00" * 62)) @@ -112,26 +131,34 @@ def test_reduce(self): dgst = sha512(b"hello").digest() # (int.from_bytes(sha512(b"hello").digest(), "little") % order).to_bytes(32, "little").hex() - reduced_dgst = unhexlify( + reduced_dgst = bytes.fromhex( "b586c3423482ab97d876ce24cab8bd8ab84e22ac3a52a8dfbb330bbe92a3260f" ) assert bytes(Ristretto255Scalar.reduce(dgst)) == reduced_dgst + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_inverse(self): assert Ristretto255Scalar(1).inverse == Ristretto255Scalar.ONE s = Ristretto255Scalar.random() assert s.inverse * s == Ristretto255Scalar.ONE - t = Ristretto255Scalar(b"".join(int2byte(i) for i in range(32))) + t = Ristretto255Scalar(bytes(range(32))) # pow(int.from_bytes(bytes(range(32)), "little"), -1, order).to_bytes(32, "little").hex() - inv = unhexlify( + inv = bytes.fromhex( "0cf17e6d77775ab76bd4f41cd2ef9ecc9ddd8242185bd685a60b49b5b3f16606" ) assert bytes(t.inverse) == inv + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_complement(self): assert Ristretto255Scalar(1).complement == Ristretto255Scalar.ZERO assert Ristretto255Scalar(0).complement == Ristretto255Scalar.ONE @@ -139,14 +166,18 @@ def test_complement(self): s = Ristretto255Scalar.random() assert s.complement + s == Ristretto255Scalar.ONE - t = Ristretto255Scalar(b"".join(int2byte(i) for i in range(32))) + t = Ristretto255Scalar(bytes(range(32))) # ((1 - int.from_bytes(bytes(range(32)), "little")) % order).to_bytes(32, "little").hex() - compl = unhexlify( + compl = bytes.fromhex( "dba6e9b630c11ea9a430e53ab1e6af1af0eeedecebeae9e8e7e6e5e4e3e2e100" ) assert bytes(t.complement) == compl + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_add(self): s = Ristretto255Scalar(123) t = Ristretto255Scalar(456) @@ -166,6 +197,10 @@ def test_add(self): assert (a + b) + c == (c + a) + b + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_sub(self): s = Ristretto255Scalar(123) t = Ristretto255Scalar(456) @@ -183,6 +218,10 @@ def test_sub(self): assert (a - b) - c == a - (c + b) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_mul(self): s = Ristretto255Scalar(123) t = Ristretto255Scalar(456) @@ -198,7 +237,7 @@ def test_mul(self): v = Ristretto255Scalar(b"\x01" * 32) w = Ristretto255Scalar(b"\x02" * 32) # (int.from_bytes(b"\x01" * 32, "little") * int.from_bytes(b"\x02" * 32, "little") % order).to_bytes(32, "little").hex() - x = unhexlify( + x = bytes.fromhex( "7d808bf1fafea25f3ee660ef3c1793985190ba1413f9b714edf967ce6b8bdd06" ) assert bytes(v * w) == x @@ -211,6 +250,10 @@ def test_mul(self): assert a * Ristretto255Scalar.ZERO == Ristretto255Scalar.ZERO assert a * Ristretto255Scalar.ONE == a + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_neg(self): s = Ristretto255Scalar(123) t = Ristretto255Scalar(-123) @@ -225,6 +268,10 @@ def test_neg(self): assert -Ristretto255Scalar.ZERO == Ristretto255Scalar.ZERO assert -Ristretto255Scalar.ONE == Ristretto255Scalar.MINUS_ONE + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_eq(self): s = Ristretto255Scalar(123) t = Ristretto255Scalar(123) @@ -248,6 +295,10 @@ def test_eq(self): assert a != c assert b != c + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_hash(self): p = Ristretto255Scalar.random() q = Ristretto255Scalar.random() @@ -257,11 +308,19 @@ def test_hash(self): assert h0 == h1 + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_bytes(self): s = Ristretto255Scalar(123) assert type(bytes(s)) is bytes assert len(bytes(s)) == 32 + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_int(self): s = Ristretto255Scalar(123) t = -s @@ -272,6 +331,10 @@ def test_int(self): assert int(Ristretto255Scalar.ONE) == 1 assert int(Ristretto255Scalar.MINUS_ONE) == self.order - 1 + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_bool(self): assert not Ristretto255Scalar.ZERO assert Ristretto255Scalar.ONE @@ -286,23 +349,40 @@ def test_bool(self): assert u - t assert not (u - t - s) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_repr(self): s = Ristretto255Scalar(123) assert repr(s) == "Ristretto255Scalar(123)" + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) + def test_str(self): + s = Ristretto255Scalar(123) + text = str(s) + assert text == "Ristretto255Scalar(123)" + class TestRistretto255Point(object): _vectors = _ristretto255_vectors() + _base = bytes.fromhex( + "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" + ) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) @pytest.mark.parametrize( ("idx", "encoding"), _vectors["encodings_of_small_multiples"] ) def test_small_multiples(self, idx, encoding): - base = Ristretto255Point( - unhexlify( - "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" - ) - ) + base = Ristretto255Point(self._base) point = Ristretto255Point.ZERO for i in range(idx): @@ -315,11 +395,19 @@ def test_small_multiples(self, idx, encoding): point = Ristretto255Point.base_mul(idx) assert bytes(point) == encoding + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) @pytest.mark.parametrize(("encoding"), _vectors["bad_encodings"]) def test_bad_encodings(self, encoding): with pytest.raises(exc.ValueError): Ristretto255Point(encoding) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) @pytest.mark.parametrize( ("label", "encoding"), _vectors["label_hash_to_points"] ) @@ -329,6 +417,10 @@ def test_hash_to_point(self, label, encoding): ) assert bytes(point) == encoding + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_init(self): with pytest.raises(exc.TypeError): Ristretto255Point(b"too short") @@ -338,6 +430,10 @@ def test_init(self): # good code paths are tested elsewhere. + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_random(self): p = Ristretto255Point.random() q = Ristretto255Point.random() @@ -346,6 +442,10 @@ def test_random(self): # it can only be a serious bug because of the huge group size. assert p != q + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_neg(self): p = Ristretto255Point.random() q = -p @@ -353,6 +453,10 @@ def test_neg(self): assert p + q == Ristretto255Point.ZERO assert p - q == p + p + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_add(self): p = Ristretto255Point.random() q = Ristretto255Point.random() @@ -367,6 +471,10 @@ def test_add(self): assert (p + q) + r == p + (q + r) assert (p + q) + r == (r + p) + q + @pytest.mark.skipif( + not has_crypto_core_ristretto25519, + reason="Requires full build of libsodium", + ) def test_sub(self): p = Ristretto255Point.random() q = Ristretto255Point.random() @@ -381,6 +489,11 @@ def test_sub(self): assert p - q != q - p assert (p - q) - r == p - (q + r) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_mul(self): p = Ristretto255Point.random() q = Ristretto255Point.random() @@ -389,7 +502,7 @@ def test_mul(self): p * q with pytest.raises(exc.TypeError): - p * u"test" + p * "test" assert p * 3 == 3 * p assert p + p + p == p * 3 @@ -399,6 +512,11 @@ def test_mul(self): assert p * Fraction(8, 1) == p * 8 assert 27 * p * Fraction(-11, 27) == p * -11 + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_bool(self): p = Ristretto255Point.random() @@ -406,10 +524,15 @@ def test_bool(self): assert bool(Ristretto255Point.base_mul(1)) assert not (p - p) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_eq(self): p = Ristretto255Point.random() q = Ristretto255Point.random() - assert p != u"foobar" + assert p != "foobar" assert p == p a = p * 17 + q @@ -420,18 +543,25 @@ def test_eq(self): assert a != c assert b != c + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_bytes(self): base = Ristretto255Point.base_mul(1) - enc0 = bytes(base) - enc1 = unhexlify( - "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76" - ) - assert enc0 == enc1 + enc = bytes(base) + assert enc == self._base p = Ristretto255Point.random() assert type(bytes(p)) is bytes assert len(bytes(p)) == 32 + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_hash(self): p = Ristretto255Point.random() q = Ristretto255Point.random() @@ -441,13 +571,31 @@ def test_hash(self): assert h0 == h1 + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_repr(self): base = Ristretto255Point.base_mul(1) - assert ( - repr(base) - == "Ristretto255Point('e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76')" - ) + assert repr(base) == "Ristretto255Point({})".format(repr(self._base)) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) + def test_str(self): + base = Ristretto255Point.base_mul(1) + base_hex = self._base.hex() + text = str(base) + assert text == "Ristretto255Point({})".format(base_hex) + + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_library_error(self): p = Ristretto255Point( self._vectors["bad_encodings"][6], _assume_valid=True @@ -489,6 +637,11 @@ def decrypt(self, c0, c1, x): return m + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_el_gamal(self): x, h = self.gen_key() orig_msg = b"The quick brown fox jumps over the lazy dog.".ljust(64) @@ -561,6 +714,11 @@ def reconstruct(self, shares): Ristretto255Point.ZERO, ) + @pytest.mark.skipif( + not has_crypto_core_ristretto25519 + or not has_crypto_scalarmult_ristretto25519, + reason="Requires full build of libsodium", + ) def test_shamir(self): secret0, shares = self.share_secret(5, 3)