Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"build",
".",
"--wheel",
"--sdist",
"--sdist"
],
"problemMatcher": []
},
Expand Down Expand Up @@ -201,6 +201,7 @@
"dependsOrder": "sequence",
"dependsOn": [
"Clean",
//"InstallCryptography",
"Security",
"Sort Imports",
"Format",
Expand All @@ -223,5 +224,18 @@
],
"problemMatcher": []
},
{
"label": "InstallCryptography",
"type": "process",
"command": "${config:python.defaultInterpreterPath}",
"args": [
"-m",
"pip",
"install",
"--upgrade",
"cryptography"
],
"problemMatcher": []
},
]
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ source env/bin/activate # (or env\Scripts\activate on Windows)
python3 -m pip install --upgrade pyspartn
```

**FYI** From `pyspartn` version 1.0.7 onwards, SPARTN decryption functionality is optional. To install without decryption support, use the `--no-deps` argument e.g. ```python3 -m pip install --upgrade pyspartn --no-deps```.

*¹* On some 32-bit Linux platforms (e.g. Raspberry Pi OS 32), it may be necessary to [install Rust compiler support](https://www.rust-lang.org/tools/install) in order to install the `cryptography` package which `pyspartn` depends on to decrypt SPARTN message payloads. See [cryptography install README](https://github.com/semuconsulting/pyspartn/blob/main/cryptography_installation/README.md).

For [Conda](https://docs.conda.io/en/latest/) users, `pyspartn` is also available from [conda-forge](https://github.com/conda-forge/pyspartn-feedstock):
Expand Down
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# pyspartn Release Notes

### RELEASE 1.0.7

1. Make SPARTN decryption (and associated `cryptography` library dependencies) an optional feature, to avoid a hard dependency on the `cryptography` library (which is problematic on some platforms). To install without SPARTN decryption support, use `python3 -m pip install pyspartn --no-deps`.

### RELEASE 1.0.6

1. Update build configuration and minimum cryptography version - no functional changes.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ disable = """

[tool.pytest.ini_options]
minversion = "7.0"
addopts = "--cov --cov-report html --cov-fail-under 95"
addopts = "--cov --cov-report html --cov-fail-under 94"
pythonpath = ["src"]

[tool.coverage.run]
Expand Down
2 changes: 1 addition & 1 deletion src/pyspartn/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""

__version__ = "1.0.6"
__version__ = "1.0.7"
12 changes: 11 additions & 1 deletion src/pyspartn/spartnhelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@

from datetime import datetime, timedelta, timezone

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
try:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

HASCRYPTO = True
except (ImportError, ModuleNotFoundError):
HASCRYPTO = False
from pyspartn.exceptions import SPARTNMessageError
from pyspartn.spartntypes_core import FL, IN, SPARTN_DATA_FIELDS, TIMEBASE

Expand Down Expand Up @@ -176,6 +180,9 @@ def encrypt(pt: bytes, key: bytes, iv: bytes, mode: str = "CTR") -> tuple:
:rtype: tuple
"""

if not HASCRYPTO: # pragma: no-cover
return None, None

if mode == "CTR":
cipher = Cipher(algorithms.AES(key), modes.CTR(iv))
else:
Expand All @@ -201,6 +208,9 @@ def decrypt(ct: bytes, key: bytes, iv: bytes, mode: str = "CTR") -> bytes:
:rtype: bytes
"""

if not HASCRYPTO: # pragma: no-cover
return None

if mode == "CTR":
cipher = Cipher(algorithms.AES(key), modes.CTR(iv))
else:
Expand Down
15 changes: 11 additions & 4 deletions src/pyspartn/spartnmessage.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
SPARTNParseError,
)
from pyspartn.spartnhelpers import (
HASCRYPTO,
bitsval,
convert_timetag,
decrypt,
Expand Down Expand Up @@ -124,9 +125,6 @@ def __init__(
self._key = None if key is None else bytes.fromhex(key)
self._iv = None

if self._decode and self._key is None:
raise ParameterError("Key must be provided if decoding is enabled")

self._do_attributes()
self._immutable = True # once initialised, object is immutable

Expand All @@ -141,10 +139,19 @@ def _do_attributes(self):
# start of framestart
self.msgType = bitsval(self._transport, 8, 7)
self.nData = bitsval(self._transport, 15, 10)
self.eaf = bitsval(self._transport, 25, 1)
self.eaf = bitsval(self._transport, 25, 1) # 1 = encrypted
self.crcType = bitsval(self._transport, 26, 2)
self.frameCrc = bitsval(self._transport, 28, 4)

# check if decryption available
if self._decode and self.eaf:
if self._key is None:
raise ParameterError("Key must be provided if decryption is enabled")
if not HASCRYPTO:
raise ParameterError(
"Decryption not available - cryptography library is not installed"
)

# start of payDesc
self.msgSubtype = bitsval(self._transport, 32, 4)
self.timeTagtype = bitsval(self._transport, 36, 1)
Expand Down
10 changes: 10 additions & 0 deletions tests/test_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
import unittest
from datetime import datetime, timezone

try:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

HASCRYPTO = True
except (ImportError, ModuleNotFoundError):
HASCRYPTO = False

from pyspartn.exceptions import SPARTNMessageError
from pyspartn.spartnhelpers import (
att2idx,
Expand Down Expand Up @@ -114,6 +121,9 @@ def testescapeall(self):
self.assertEqual(res, EXPECTED_RESULT)

def testdecrypt(self):
if not HASCRYPTO:
return

msg = b"your secret message"
key = 0x395C12348D083E53AD0A5AA257C6A741.to_bytes(16, "big")
iv = os.urandom(16)
Expand Down
58 changes: 57 additions & 1 deletion tests/test_stream.py

Large diffs are not rendered by default.