Add stream_xor(); deprecate encrypt/decrypt_unauthenticated
Following upstream libaegis 0.10.5 (874f884): the absorbing-mode unauthenticated pair is deprecated in favor of the pure keystream XOR. - stream_xor(key, nonce, data, into=None): same function encrypts and decrypts; output equals data XOR stream(key, nonce); supports in-place use via into (same buffer semantics as elsewhere). - encrypt_unauthenticated/decrypt_unauthenticated kept for the old format but now emit DeprecationWarning; removed from README (stream_xor documented under Keystream generation instead).
This commit is contained in:
1 parent
9d36d3b81b
commit
1eb0b6e8c0
9 files changed
+492
-7
No files matched your search
@@ -79,10 +79,6 @@ The MAC tag is handled separately of ciphertext:
|
||||
- encrypt_detached(key, nonce, message, ad=None, maclen=16, ct_into=None, mac_into=None) -> (ct, mac)
|
||||
- decrypt_detached(key, nonce, ct, mac, ad=None, into=None) -> plaintext
|
||||
|
||||
No MAC tag, vulnerable to alterations:
|
||||
- encrypt_unauthenticated(key, nonce, message, into=None) -> ciphertext (testing only)
|
||||
- decrypt_unauthenticated(key, nonce, ct, into=None) -> plaintext (testing only)
|
||||
|
||||
### Incremental AEAD
|
||||
|
||||
Stateful classes that can be used for processing the data in separate chunks:
|
||||
@@ -116,6 +112,7 @@ The `Mac` class follows the Python hashlib API for compatibility with code expec
|
||||
Useful for creating pseudo random bytes as rapidly as possible. Reuse of the same (key, nonce) creates identical output.
|
||||
|
||||
- stream(key, nonce=None, length=None, into=None) -> randombytes
|
||||
- stream_xor(key, nonce, data, into=None) -> data XOR keystream (unauthenticated; encrypts and decrypts)
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
@@ -311,7 +308,7 @@ pt = ciph.decrypt(key, nonce, ct, into=buf)
|
||||
print(bytes(pt))
|
||||
```
|
||||
|
||||
Detached and unauthenticated modes can use same size input and output (no MAC added to ciphertext). Detached encryption instead of `into` takes `ct_into` and `mac_into` separately and returns memoryviews to both.
|
||||
Detached mode can use same size input and output (no MAC added to ciphertext). Detached encryption instead of `into` takes `ct_into` and `mac_into` separately and returns memoryviews to both.
|
||||
|
||||
## Performance
|
||||
|
||||
|
||||
+10
-2
@@ -100,7 +100,15 @@ class Cipher(Protocol):
|
||||
into: "Buffer | None" = None,
|
||||
) -> "bytearray | Buffer": ...
|
||||
@staticmethod
|
||||
def encrypt_unauthenticated(
|
||||
def stream_xor(
|
||||
key: "Buffer",
|
||||
nonce: "Buffer",
|
||||
data: "Buffer",
|
||||
*,
|
||||
into: "Buffer | None" = None,
|
||||
) -> bytearray | memoryview: ...
|
||||
@staticmethod
|
||||
def encrypt_unauthenticated( # deprecated: use stream_xor
|
||||
key: "Buffer",
|
||||
nonce: "Buffer",
|
||||
message: "Buffer",
|
||||
@@ -108,7 +116,7 @@ class Cipher(Protocol):
|
||||
into: "Buffer | None" = None,
|
||||
) -> bytearray | memoryview: ...
|
||||
@staticmethod
|
||||
def decrypt_unauthenticated(
|
||||
def decrypt_unauthenticated( # deprecated: use stream_xor
|
||||
key: "Buffer",
|
||||
nonce: "Buffer",
|
||||
ct: "Buffer",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import errno
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from typing import Literal
|
||||
|
||||
from ._loader import ffi
|
||||
@@ -378,6 +379,55 @@ def stream(
|
||||
return out if into is None else memoryview(out)[: length or memoryview(out).nbytes] # type: ignore
|
||||
|
||||
|
||||
def stream_xor(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
data: Buffer,
|
||||
*,
|
||||
into: Buffer | None = None,
|
||||
) -> bytearray | memoryview:
|
||||
"""XOR data with the keystream from stream(); encrypts and decrypts.
|
||||
|
||||
Unauthenticated: only use when the data is authenticated some other
|
||||
way. Never reuse a nonce with the same key.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
data: Input bytes to XOR.
|
||||
into: Buffer to write the output into (default: bytearray created).
|
||||
|
||||
Returns:
|
||||
Output as bytearray if into not provided, memoryview of into otherwise.
|
||||
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
data = memoryview(data)
|
||||
if into is not None:
|
||||
into = memoryview(into)
|
||||
if key.nbytes != KEYBYTES:
|
||||
raise TypeError(f"key length must be {KEYBYTES}")
|
||||
if nonce.nbytes != NONCEBYTES:
|
||||
raise TypeError(f"nonce length must be {NONCEBYTES}")
|
||||
if into is None:
|
||||
out = bytearray(data.nbytes)
|
||||
else:
|
||||
if into.nbytes < data.nbytes:
|
||||
raise TypeError("into length must be at least data.nbytes")
|
||||
out = into
|
||||
_lib.aegis128l_stream_xor(
|
||||
ffi.from_buffer(out),
|
||||
_ptr(data),
|
||||
data.nbytes,
|
||||
_ptr(nonce),
|
||||
_ptr(key),
|
||||
)
|
||||
return out if into is None else memoryview(out)[: data.nbytes] # type: ignore
|
||||
|
||||
|
||||
def encrypt_unauthenticated(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
@@ -387,6 +437,10 @@ def encrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Encrypt message without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
@@ -399,6 +453,12 @@ def encrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"encrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
message = memoryview(message)
|
||||
@@ -433,6 +493,10 @@ def decrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Decrypt ciphertext without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (same key used during encryption).
|
||||
nonce: Public nonce (same nonce used during encryption).
|
||||
@@ -445,6 +509,12 @@ def decrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"decrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
ct = memoryview(ct)
|
||||
@@ -1122,6 +1192,7 @@ __all__ = [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"stream",
|
||||
"stream_xor",
|
||||
"encrypt_unauthenticated",
|
||||
"decrypt_unauthenticated",
|
||||
"mac",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import errno
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from typing import Literal
|
||||
|
||||
from ._loader import ffi
|
||||
@@ -378,6 +379,55 @@ def stream(
|
||||
return out if into is None else memoryview(out)[: length or memoryview(out).nbytes] # type: ignore
|
||||
|
||||
|
||||
def stream_xor(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
data: Buffer,
|
||||
*,
|
||||
into: Buffer | None = None,
|
||||
) -> bytearray | memoryview:
|
||||
"""XOR data with the keystream from stream(); encrypts and decrypts.
|
||||
|
||||
Unauthenticated: only use when the data is authenticated some other
|
||||
way. Never reuse a nonce with the same key.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
data: Input bytes to XOR.
|
||||
into: Buffer to write the output into (default: bytearray created).
|
||||
|
||||
Returns:
|
||||
Output as bytearray if into not provided, memoryview of into otherwise.
|
||||
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
data = memoryview(data)
|
||||
if into is not None:
|
||||
into = memoryview(into)
|
||||
if key.nbytes != KEYBYTES:
|
||||
raise TypeError(f"key length must be {KEYBYTES}")
|
||||
if nonce.nbytes != NONCEBYTES:
|
||||
raise TypeError(f"nonce length must be {NONCEBYTES}")
|
||||
if into is None:
|
||||
out = bytearray(data.nbytes)
|
||||
else:
|
||||
if into.nbytes < data.nbytes:
|
||||
raise TypeError("into length must be at least data.nbytes")
|
||||
out = into
|
||||
_lib.aegis128x2_stream_xor(
|
||||
ffi.from_buffer(out),
|
||||
_ptr(data),
|
||||
data.nbytes,
|
||||
_ptr(nonce),
|
||||
_ptr(key),
|
||||
)
|
||||
return out if into is None else memoryview(out)[: data.nbytes] # type: ignore
|
||||
|
||||
|
||||
def encrypt_unauthenticated(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
@@ -387,6 +437,10 @@ def encrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Encrypt message without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
@@ -399,6 +453,12 @@ def encrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"encrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
message = memoryview(message)
|
||||
@@ -433,6 +493,10 @@ def decrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Decrypt ciphertext without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (same key used during encryption).
|
||||
nonce: Public nonce (same nonce used during encryption).
|
||||
@@ -445,6 +509,12 @@ def decrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"decrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
ct = memoryview(ct)
|
||||
@@ -1122,6 +1192,7 @@ __all__ = [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"stream",
|
||||
"stream_xor",
|
||||
"encrypt_unauthenticated",
|
||||
"decrypt_unauthenticated",
|
||||
"mac",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import errno
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from typing import Literal
|
||||
|
||||
from ._loader import ffi
|
||||
@@ -378,6 +379,55 @@ def stream(
|
||||
return out if into is None else memoryview(out)[: length or memoryview(out).nbytes] # type: ignore
|
||||
|
||||
|
||||
def stream_xor(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
data: Buffer,
|
||||
*,
|
||||
into: Buffer | None = None,
|
||||
) -> bytearray | memoryview:
|
||||
"""XOR data with the keystream from stream(); encrypts and decrypts.
|
||||
|
||||
Unauthenticated: only use when the data is authenticated some other
|
||||
way. Never reuse a nonce with the same key.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
data: Input bytes to XOR.
|
||||
into: Buffer to write the output into (default: bytearray created).
|
||||
|
||||
Returns:
|
||||
Output as bytearray if into not provided, memoryview of into otherwise.
|
||||
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
data = memoryview(data)
|
||||
if into is not None:
|
||||
into = memoryview(into)
|
||||
if key.nbytes != KEYBYTES:
|
||||
raise TypeError(f"key length must be {KEYBYTES}")
|
||||
if nonce.nbytes != NONCEBYTES:
|
||||
raise TypeError(f"nonce length must be {NONCEBYTES}")
|
||||
if into is None:
|
||||
out = bytearray(data.nbytes)
|
||||
else:
|
||||
if into.nbytes < data.nbytes:
|
||||
raise TypeError("into length must be at least data.nbytes")
|
||||
out = into
|
||||
_lib.aegis128x4_stream_xor(
|
||||
ffi.from_buffer(out),
|
||||
_ptr(data),
|
||||
data.nbytes,
|
||||
_ptr(nonce),
|
||||
_ptr(key),
|
||||
)
|
||||
return out if into is None else memoryview(out)[: data.nbytes] # type: ignore
|
||||
|
||||
|
||||
def encrypt_unauthenticated(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
@@ -387,6 +437,10 @@ def encrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Encrypt message without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
@@ -399,6 +453,12 @@ def encrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"encrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
message = memoryview(message)
|
||||
@@ -433,6 +493,10 @@ def decrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Decrypt ciphertext without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (same key used during encryption).
|
||||
nonce: Public nonce (same nonce used during encryption).
|
||||
@@ -445,6 +509,12 @@ def decrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"decrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
ct = memoryview(ct)
|
||||
@@ -1122,6 +1192,7 @@ __all__ = [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"stream",
|
||||
"stream_xor",
|
||||
"encrypt_unauthenticated",
|
||||
"decrypt_unauthenticated",
|
||||
"mac",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import errno
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from typing import Literal
|
||||
|
||||
from ._loader import ffi
|
||||
@@ -378,6 +379,55 @@ def stream(
|
||||
return out if into is None else memoryview(out)[: length or memoryview(out).nbytes] # type: ignore
|
||||
|
||||
|
||||
def stream_xor(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
data: Buffer,
|
||||
*,
|
||||
into: Buffer | None = None,
|
||||
) -> bytearray | memoryview:
|
||||
"""XOR data with the keystream from stream(); encrypts and decrypts.
|
||||
|
||||
Unauthenticated: only use when the data is authenticated some other
|
||||
way. Never reuse a nonce with the same key.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
data: Input bytes to XOR.
|
||||
into: Buffer to write the output into (default: bytearray created).
|
||||
|
||||
Returns:
|
||||
Output as bytearray if into not provided, memoryview of into otherwise.
|
||||
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
data = memoryview(data)
|
||||
if into is not None:
|
||||
into = memoryview(into)
|
||||
if key.nbytes != KEYBYTES:
|
||||
raise TypeError(f"key length must be {KEYBYTES}")
|
||||
if nonce.nbytes != NONCEBYTES:
|
||||
raise TypeError(f"nonce length must be {NONCEBYTES}")
|
||||
if into is None:
|
||||
out = bytearray(data.nbytes)
|
||||
else:
|
||||
if into.nbytes < data.nbytes:
|
||||
raise TypeError("into length must be at least data.nbytes")
|
||||
out = into
|
||||
_lib.aegis256_stream_xor(
|
||||
ffi.from_buffer(out),
|
||||
_ptr(data),
|
||||
data.nbytes,
|
||||
_ptr(nonce),
|
||||
_ptr(key),
|
||||
)
|
||||
return out if into is None else memoryview(out)[: data.nbytes] # type: ignore
|
||||
|
||||
|
||||
def encrypt_unauthenticated(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
@@ -387,6 +437,10 @@ def encrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Encrypt message without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
@@ -399,6 +453,12 @@ def encrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"encrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
message = memoryview(message)
|
||||
@@ -433,6 +493,10 @@ def decrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Decrypt ciphertext without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (same key used during encryption).
|
||||
nonce: Public nonce (same nonce used during encryption).
|
||||
@@ -445,6 +509,12 @@ def decrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"decrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
ct = memoryview(ct)
|
||||
@@ -1122,6 +1192,7 @@ __all__ = [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"stream",
|
||||
"stream_xor",
|
||||
"encrypt_unauthenticated",
|
||||
"decrypt_unauthenticated",
|
||||
"mac",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import errno
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from typing import Literal
|
||||
|
||||
from ._loader import ffi
|
||||
@@ -378,6 +379,55 @@ def stream(
|
||||
return out if into is None else memoryview(out)[: length or memoryview(out).nbytes] # type: ignore
|
||||
|
||||
|
||||
def stream_xor(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
data: Buffer,
|
||||
*,
|
||||
into: Buffer | None = None,
|
||||
) -> bytearray | memoryview:
|
||||
"""XOR data with the keystream from stream(); encrypts and decrypts.
|
||||
|
||||
Unauthenticated: only use when the data is authenticated some other
|
||||
way. Never reuse a nonce with the same key.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
data: Input bytes to XOR.
|
||||
into: Buffer to write the output into (default: bytearray created).
|
||||
|
||||
Returns:
|
||||
Output as bytearray if into not provided, memoryview of into otherwise.
|
||||
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
data = memoryview(data)
|
||||
if into is not None:
|
||||
into = memoryview(into)
|
||||
if key.nbytes != KEYBYTES:
|
||||
raise TypeError(f"key length must be {KEYBYTES}")
|
||||
if nonce.nbytes != NONCEBYTES:
|
||||
raise TypeError(f"nonce length must be {NONCEBYTES}")
|
||||
if into is None:
|
||||
out = bytearray(data.nbytes)
|
||||
else:
|
||||
if into.nbytes < data.nbytes:
|
||||
raise TypeError("into length must be at least data.nbytes")
|
||||
out = into
|
||||
_lib.aegis256x2_stream_xor(
|
||||
ffi.from_buffer(out),
|
||||
_ptr(data),
|
||||
data.nbytes,
|
||||
_ptr(nonce),
|
||||
_ptr(key),
|
||||
)
|
||||
return out if into is None else memoryview(out)[: data.nbytes] # type: ignore
|
||||
|
||||
|
||||
def encrypt_unauthenticated(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
@@ -387,6 +437,10 @@ def encrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Encrypt message without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
@@ -399,6 +453,12 @@ def encrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"encrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
message = memoryview(message)
|
||||
@@ -433,6 +493,10 @@ def decrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Decrypt ciphertext without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (same key used during encryption).
|
||||
nonce: Public nonce (same nonce used during encryption).
|
||||
@@ -445,6 +509,12 @@ def decrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"decrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
ct = memoryview(ct)
|
||||
@@ -1122,6 +1192,7 @@ __all__ = [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"stream",
|
||||
"stream_xor",
|
||||
"encrypt_unauthenticated",
|
||||
"decrypt_unauthenticated",
|
||||
"mac",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import errno
|
||||
import sys
|
||||
import threading
|
||||
import warnings
|
||||
from typing import Literal
|
||||
|
||||
from ._loader import ffi
|
||||
@@ -378,6 +379,55 @@ def stream(
|
||||
return out if into is None else memoryview(out)[: length or memoryview(out).nbytes] # type: ignore
|
||||
|
||||
|
||||
def stream_xor(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
data: Buffer,
|
||||
*,
|
||||
into: Buffer | None = None,
|
||||
) -> bytearray | memoryview:
|
||||
"""XOR data with the keystream from stream(); encrypts and decrypts.
|
||||
|
||||
Unauthenticated: only use when the data is authenticated some other
|
||||
way. Never reuse a nonce with the same key.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
data: Input bytes to XOR.
|
||||
into: Buffer to write the output into (default: bytearray created).
|
||||
|
||||
Returns:
|
||||
Output as bytearray if into not provided, memoryview of into otherwise.
|
||||
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
data = memoryview(data)
|
||||
if into is not None:
|
||||
into = memoryview(into)
|
||||
if key.nbytes != KEYBYTES:
|
||||
raise TypeError(f"key length must be {KEYBYTES}")
|
||||
if nonce.nbytes != NONCEBYTES:
|
||||
raise TypeError(f"nonce length must be {NONCEBYTES}")
|
||||
if into is None:
|
||||
out = bytearray(data.nbytes)
|
||||
else:
|
||||
if into.nbytes < data.nbytes:
|
||||
raise TypeError("into length must be at least data.nbytes")
|
||||
out = into
|
||||
_lib.aegis256x4_stream_xor(
|
||||
ffi.from_buffer(out),
|
||||
_ptr(data),
|
||||
data.nbytes,
|
||||
_ptr(nonce),
|
||||
_ptr(key),
|
||||
)
|
||||
return out if into is None else memoryview(out)[: data.nbytes] # type: ignore
|
||||
|
||||
|
||||
def encrypt_unauthenticated(
|
||||
key: Buffer,
|
||||
nonce: Buffer,
|
||||
@@ -387,6 +437,10 @@ def encrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Encrypt message without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (generate with random_key()).
|
||||
nonce: Public nonce (generate with random_nonce()).
|
||||
@@ -399,6 +453,12 @@ def encrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"encrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
message = memoryview(message)
|
||||
@@ -433,6 +493,10 @@ def decrypt_unauthenticated(
|
||||
) -> bytearray | memoryview:
|
||||
"""Decrypt ciphertext without authentication (for testing/debugging).
|
||||
|
||||
.. deprecated::
|
||||
Use stream_xor() in new code. Its output is different, so keep
|
||||
this only for data in the old format.
|
||||
|
||||
Args:
|
||||
key: Secret key (same key used during encryption).
|
||||
nonce: Public nonce (same nonce used during encryption).
|
||||
@@ -445,6 +509,12 @@ def decrypt_unauthenticated(
|
||||
Raises:
|
||||
TypeError: If lengths are invalid.
|
||||
"""
|
||||
warnings.warn(
|
||||
"decrypt_unauthenticated is deprecated; use stream_xor "
|
||||
"(its output differs, so keep this only for existing data)",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
key = memoryview(key)
|
||||
nonce = memoryview(nonce)
|
||||
ct = memoryview(ct)
|
||||
@@ -1122,6 +1192,7 @@ __all__ = [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"stream",
|
||||
"stream_xor",
|
||||
"encrypt_unauthenticated",
|
||||
"decrypt_unauthenticated",
|
||||
"mac",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import pytest
|
||||
|
||||
from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4
|
||||
|
||||
CIPHERS = [aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4]
|
||||
|
||||
|
||||
def cipher_id(c):
|
||||
return c.NAME
|
||||
|
||||
|
||||
@pytest.mark.parametrize("c", CIPHERS, ids=cipher_id)
|
||||
def test_stream_xor_roundtrip(c):
|
||||
key, nonce = c.random_key(), c.random_nonce()
|
||||
msg = b"stream cipher test" * 100 # spans multiple blocks, unaligned tail
|
||||
ct = c.stream_xor(key, nonce, msg)
|
||||
assert isinstance(ct, bytearray)
|
||||
assert len(ct) == len(msg)
|
||||
assert ct != msg
|
||||
# the same function decrypts
|
||||
assert c.stream_xor(key, nonce, ct) == msg
|
||||
|
||||
|
||||
@pytest.mark.parametrize("c", CIPHERS, ids=cipher_id)
|
||||
def test_stream_xor_is_stream_xor(c):
|
||||
"""Output equals data XOR stream(key, nonce) keystream."""
|
||||
key, nonce = c.random_key(), c.random_nonce()
|
||||
msg = b"\x5a" * 500
|
||||
ks = c.stream(key, nonce, len(msg))
|
||||
ct = c.stream_xor(key, nonce, msg)
|
||||
assert bytes(ct) == bytes(m ^ k for m, k in zip(msg, ks))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("c", CIPHERS, ids=cipher_id)
|
||||
def test_stream_xor_into(c):
|
||||
key, nonce = c.random_key(), c.random_nonce()
|
||||
msg = b"into target" * 10
|
||||
buf = bytearray(len(msg) + 10)
|
||||
out = c.stream_xor(key, nonce, msg, into=buf)
|
||||
assert len(out) == len(msg)
|
||||
assert bytes(out) == bytes(c.stream_xor(key, nonce, msg))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("c", CIPHERS, ids=cipher_id)
|
||||
def test_unauthenticated_deprecated(c):
|
||||
key, nonce = c.random_key(), c.random_nonce()
|
||||
msg = b"legacy format" * 20 # longer than one block: absorbing mode diverges
|
||||
with pytest.warns(DeprecationWarning):
|
||||
ct = c.encrypt_unauthenticated(key, nonce, msg)
|
||||
with pytest.warns(DeprecationWarning):
|
||||
pt = c.decrypt_unauthenticated(key, nonce, ct)
|
||||
assert pt == msg
|
||||
# deprecated pair is NOT compatible with stream_xor beyond the first block
|
||||
assert bytes(ct) != bytes(c.stream_xor(key, nonce, msg))
|
||||
Reference in new issue
Block a user