Skip to content

Commit

Permalink
Add ignore_whitespace option to Base64.from_encoded
Browse files Browse the repository at this point in the history
This patch makes it easy to ignore whitespace in Base64 input, e. g.
generated by the base64 tool without the --wrap=0 option.

Fixes: #108
  • Loading branch information
robin-nitrokey committed May 3, 2024
1 parent 49e683e commit 7f7e9d8
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- Support key import from PEM files ([#99](https://github.com/Nitrokey/nethsm-sdk-py/issues/99))
- Add `ignore_whitespace` option to `Base64.from_encoded` ([#108](https://github.com/Nitrokey/nethsm-sdk-py/issues/108))

[All Changes](https://github.com/Nitrokey/nethsm-sdk-py/compare/v0.5.0...HEAD)

Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@ nethsm-client: nethsm-api.yaml

.PHONY: test
test:
$(PYTHON3_VENV) -m doctest nethsm/__init__.py
$(PYTHON3_VENV) -m pytest --cov nethsm --cov-report=xml $(PYTEST_FLAGS)
39 changes: 34 additions & 5 deletions nethsm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import contextlib
import enum
import json
import string
from base64 import b64decode, b64encode
from dataclasses import dataclass
from datetime import datetime, timezone
Expand Down Expand Up @@ -205,14 +206,42 @@ def decode(self) -> bytes:
return b64decode(self.data)

@classmethod
def from_encoded(cls, data: Union[bytes, str]) -> "Base64":
def from_encoded(cls, data: Union[bytes, str], ignore_whitespace: bool = False) -> "Base64":
"""
>>> Base64.from_encoded("dGVzdAo=")
Base64(data='dGVzdAo=')
>>> Base64.from_encoded(b"dGVzdAo=")
Base64(data='dGVzdAo=')
>>> Base64.from_encoded("dGV zdAo=")
Traceback (most recent call last):
...
ValueError: Invalid base64 data: Non-base64 digit found: dGV zdAo=
>>> Base64.from_encoded(b"dGV zdAo=")
Traceback (most recent call last):
...
ValueError: Invalid base64 data: Non-base64 digit found: dGV zdAo=
>>> Base64.from_encoded("dGV zdAo=", ignore_whitespace=True)
Base64(data='dGVzdAo=')
>>> Base64.from_encoded(b"dGV zdAo=", ignore_whitespace=True)
Base64(data='dGVzdAo=')
"""
if ignore_whitespace:
if isinstance(data, bytes):
data = data.translate(None, delete=string.whitespace.encode())
else:
data = data.translate(str.maketrans("", "", string.whitespace))

try:
b64decode(data, validate=True)
except binascii.Error as e:
if isinstance(data, bytes):
data = data.decode()
return cls(data=data)
except binascii.Error:
raise ValueError(f"Invalid base64 data: {data!r}")
data = data.decode(errors="replace")
raise ValueError(f"Invalid base64 data: {e}: {data}") from None

if isinstance(data, bytes):
data = data.decode()
return cls(data=data)


@classmethod
def encode(cls, data: bytes) -> "Base64":
Expand Down

0 comments on commit 7f7e9d8

Please sign in to comment.