Skip to content

Commit

Permalink
Add helper to load key file as sslib CryptoSigner
Browse files Browse the repository at this point in the history
Upstream CryptoSigner may or may not provide API for this at some point
(see secure-systems-lab/securesystemslib#664). If not, it should at
least allow us to implement it ourselves without access to protected
names (see TODO comments).

Commit includes basic tests to load from un/encrypted pems. Test pems
come from secure-systems-lab/securesystemslib#604. Encryption pw is
hunter2.

Signed-off-by: Lukas Puehringer <lukas.puehringer@nyu.edu>
  • Loading branch information
lukpueh committed Nov 20, 2023
1 parent 1128f21 commit ab29b64
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 5 deletions.
60 changes: 56 additions & 4 deletions in_toto/models/_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,69 @@
See LICENSE for licensing information.
<Purpose>
Provides in-toto flavored GPGSigner, GPGSignature and GPGKey.
Provides in-toto flavored Signer implementations
"""

from dataclasses import dataclass
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, cast

import securesystemslib.gpg.exceptions as gpg_exceptions
import securesystemslib.gpg.functions as gpg
from securesystemslib import exceptions
from securesystemslib.signer import Key, SecretsHandler, Signature, Signer
from cryptography.hazmat.primitives.asymmetric import ec, ed25519, rsa
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from securesystemslib import (
KEY_TYPE_ECDSA,
KEY_TYPE_ED25519,
KEY_TYPE_RSA,
exceptions,
)
from securesystemslib.signer import (
CryptoSigner,
Key,
SecretsHandler,
Signature,
Signer,
SSlibKey,
)
from securesystemslib.signer._crypto_signer import (
_ECDSASigner,
_Ed25519Signer,
_RSASigner,
)


def load_crypto_signer_from_pkcs8_file(
path: str, password: Optional[bytes] = None
) -> CryptoSigner:
"""Internal helper to load CryptoSigner from PKCS8/PEM file."""
# TODO: coordinate with sslib to not require protected access
# pylint: disable=protected-access
with open(path, "rb") as f:
data = f.read()

private_key = load_pem_private_key(data, password)

# TODO: Fix upstream, we don't want to use protected methods
public_key = SSlibKey._from_crypto_public_key(
private_key.public_key(), None, None
)

# TODO: Fix upstream, we don't want to use protected classes
if public_key.keytype == KEY_TYPE_RSA:
signer = _RSASigner(public_key, cast(rsa.RSAPrivateKey, private_key))
elif public_key.keytype == KEY_TYPE_ECDSA:
signer = _ECDSASigner(
public_key, cast(ec.EllipticCurvePrivateKey, private_key)
)
elif public_key.keytype == KEY_TYPE_ED25519:
signer = _Ed25519Signer(
public_key, cast(ed25519.Ed25519PrivateKey, private_key)
)
else: # can't be reached
raise ValueError(f"unsupported keytype: {public_key.keytype}")

return signer


class GPGSignature(Signature):
Expand Down
30 changes: 29 additions & 1 deletion tests/models/test_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"""

import unittest
from pathlib import Path

from securesystemslib.exceptions import (
UnverifiedSignatureError,
Expand All @@ -29,7 +30,12 @@
from securesystemslib.gpg.constants import have_gpg
from securesystemslib.gpg.functions import export_pubkey

from in_toto.models._signer import GPGKey, GPGSignature, GPGSigner
from in_toto.models._signer import (
GPGKey,
GPGSignature,
GPGSigner,
load_crypto_signer_from_pkcs8_file,
)
from tests.common import GPGKeysMixin, TmpDirMixin


Expand Down Expand Up @@ -161,3 +167,25 @@ def test_gpg_key_equality(self):
key2.keyval = key1.keyval

self.assertEqual(key2, key1)


class TestCryptoSigner(unittest.TestCase):
"""Test helper to load CryptoSigner"""

def test_load_keys(self):
pems_dir = Path(__file__).parent.parent / "pems"

for algo in ["rsa", "ecdsa", "ed25519"]:
path = pems_dir / f"{algo}_private_unencrypted.pem"
signer = load_crypto_signer_from_pkcs8_file(path)
self.assertEqual(signer.public_key.keytype, algo)

path = pems_dir / f"{algo}_private_encrypted.pem"
signer2 = load_crypto_signer_from_pkcs8_file(path, b"hunter2")
self.assertEqual(
signer.public_key.to_dict(), signer2.public_key.to_dict()
)


if __name__ == "__main__":
unittest.main()
6 changes: 6 additions & 0 deletions tests/pems/ecdsa_private_encrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGwMBsGCSqGSIb3DQEFAzAOBAi7xKwdryb3lAICCAAEgZBz9ttxivvOc6XJKG5j
Ev55zbGqCRSoUn+deGgy/osENhbn4xTOYKRKXGMbfF16t7qvUtX9hHozrGeVIdYg
4R7hFYxgMFlYTTVcN30fPwAV2ePtmFu4vo1/TSLhLxRhv1F3GPLoOSzZxT8FP9oh
Rd9BeAgPPC5RPBJJVThTCXesCV4JWUpY2Wf0DjpFvo3OV4w=
-----END ENCRYPTED PRIVATE KEY-----
5 changes: 5 additions & 0 deletions tests/pems/ecdsa_private_unencrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIP90wnfOXb44C0fH
xubol3Vc2jsIYgBTQq0Oh84d3RWhRANCAARwthJnIUZ4p1Y23l1YVue/o303IcKh
Q0twboZkjEvA3xDwxR0d047EaQOfIFFImkhn+v+gMQJJPB8JiF2iDB4s
-----END PRIVATE KEY-----
6 changes: 6 additions & 0 deletions tests/pems/ed25519_private_encrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAi1r8RB+89SSQICCAAw
DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEC+4jGFz/I4eGd/sfUuFVXgEQD6d
idJtTe06bGSHcI66yxwUHolWyiVnnup79tGvv1y6R40P3vvxdA5EThp33HCLEE29
RAa02JqNkOK8DwzVZw8=
-----END ENCRYPTED PRIVATE KEY-----
3 changes: 3 additions & 0 deletions tests/pems/ed25519_private_unencrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIGiI3w9x2HZ9UKGi51USN5JN2wtppaYVCRIBTp8ESaj3
-----END PRIVATE KEY-----
29 changes: 29 additions & 0 deletions tests/pems/rsa_private_encrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIE4TAbBgkqhkiG9w0BBQMwDgQI/DHsRq4QSygCAggABIIEwI3XLWQ170mJ3qYl
miu7ULDvtIFfkp0Yjyvoo5PrwOQQYB5kD4Q4XkqPM3GOidmU0c0AiFtcLuxqD0/X
ivxPy/rpE9IW0uEtzw/8bF/eqyTTs+JZryYDYoNEqrLN8SxA5oMI+SCiz8Q43/y4
s6XOlaMKH9N+YcIZMSSVG0In/aQ7b5B2jehaB/eKTZmsvjk/7XMEHQ4vtjclswHd
yFbNk/Qh4MAIDJ9SCghwWyQp382ledKzqYPJbngkUu70Inbx0T2trh4wHE9c2/0Q
pymbSPLdPEPeDybt7VYenikQZBI9myyE7PfZN+Mj6HB01ziI1BTwegCtOL0XMYQD
J8aOCf5+qIcbouqvroMKgTYhpivacgzPwiuX+zDZv6FxDwn2Y16nIsrCYssSMCK0
COLynbXxTtijeHYF2GTymTKQD5caZ4eRV68JLpaPSees4UFGbgxUUmhrRtcO7vt4
FS4axe7AFOIEnBYEAsrmKb0hfq45jaBp7FQQ2abG+xO2wG6pNvM0omaP7fAtUdfm
agIxvRM4srboP7NK1TbmVufTTkOhGUw0gdW7Rom+VB6m/q3jcHdejQe4a4I9HyxP
rtCiBksZ9+J0X+7PZcbuR5AYtNldDg799p9UTjBttvLmKKhyIVbbwXoGXsyp+Bzm
W/1XWpMRT019jLb87adGR+HPPjAWF+kSe9TJA2XICekHvIxCfsZbi0TTq+5t31gH
Jv71Dn52NCTa6JMLtmFHH3h5JrujN9vvhr2lvQChkK1bb8xzAG4yzjAiOr09gRC3
hFK8in/6ePLnoEdfnTezzg5mK4NJnM65aY9IDCPdLzN+M0DgvZdM3NEzdOnvA8/w
clnWT1N3r09aNPac5VrnlUC9eNS8pzgQwpshkae3vecT+HLe79uoI0KyYD5XSy5j
HbQ6Fvl9AvMcWuZT7zx/V990Q/iYTRhuOTvyJhIe3iWikcO3zVzUl1UE6afmYmev
Iz8WQpNoPdI82aVk+t1DqYQWtY1a/gAYGT1TfjCnQebWeqAYFhCPklv2pttmAVYj
ThHiYLNR80v3KrrH+fFWynnVAmot23RuBIsM8rhTUIOd2Bxyq4YrE0SGTDKvFJ7H
mn5RRrIbICTsqpx/dLNL1mgiazHdZAFSsnZApXGYiixsgc4QQfApsLlGg3LGZD4a
STJW1+Ds9CQTzKZzMWnYDGCFhLjobH1LfnfzhvOpKA399uuv0L1P3ixgfUt30W+p
d7AkYHE494EPx4ogoj40JBztGS2UvRb0l4tJlPv50v9x+kPXK+50dEmsW7NrCATt
m1NbIGw2QzL9++SFbocf5P7RBdQBTPpXHQeClFduny6IXjQhWk+NKGIXdsVhMcLi
gMjL83LYeY4VPpurFIU8nGVfa7bdTMlh/W5KHpQX4ecibIjxUza0ddbTWxdMTwUk
XgpzvjIrz1XBy79J6H1AuSmh/+e+azmFv1fzkI+KCHGgtp8Q7uY/lDBeuc4zvARF
YMnOqFyA93sMEL28GqjOJLDfERMLuscjIeibbUNDPjwkVErtnQDu9QqLppfnKtJv
dOn20wNndpeo348k2RBO2yVzDTeuCRWkUnTmuhqYpCRopLVfqdv2q6+xM+paW7ZZ
4/+i1Zc=
-----END ENCRYPTED PRIVATE KEY-----
28 changes: 28 additions & 0 deletions tests/pems/rsa_private_unencrypted.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQDCFfquKiIv9xfl
izfYicVTnYfy0vXhB5fS1pxl1v4DBwfqciH3uHdFIe8YPtfLq5oxGYaV0CdJVjAk
eqgnmPbKdXNT9EWmv7qfw+zRAWDkaaUSplugWMttLRuxFBxdeeok2xUsQ9PdQu+7
WsVFtbMAwWM2002Tlb+7QYTNefcPTeMh2EPwgoB2tNF/RUwP1NEJct0yYRTa+R/U
1VSc5Nv91FflADxO0HKVWei5mSdtq9DQn+k+Uct/FSCwrOh3AOUmZs8b0FbBepAK
Y/QOA4Y0hnVfbzUw4FoZuQ1US6e02MJJpDFCN8AtVVTk0B9qXZzAwg22pqhztZr5
Na+kUQhbAgMBAAECggEAFZPH+NDqWBbKa1Sc8s/uRit/T7mwaEIl2OTPImtSdhe0
A5aIvDef2um44SMrbpM3YzoJQmKP25FfbM7OHwjcdwmztqOzkqRCJTzs+ReEJCCy
n24rRZpZk1uudnNb6/B/3XUV14P66+BjMpsWz3cx3WWimBfJyhyd4j2YfBeRJfw7
GLCJ0Jeplj0hKEC4Yo6Dvppnl0DJn8NnsnXLRTwepjwB/EpSxnrpzwBBwzsMTcx/
2zKC9sZhTE1RDsgbw2IIUiBk1enAhZtmiS/BFT9Y4jWaeXTkkVSnFXPZLYPkdB9W
sHgKGiWOSX/1j90IHaKsSKRFUdn3FHtDTde7o4kGQQKBgQDtdS+WBHfVvBH9iQgw
GWc3KKJPcKHC1m4+GOHhIElb0f5l/y6OTZkvK/bPtKJ8bpufsr9jBVQYubIVfJ2j
ZmO0ukclkzIjzwvY9sSHbnWzFfKbjqNG1zGaZYNe0WM/Lx51pG69hxlVzivLAObf
fqYR0+dt5imD/46FcfHFkTQdCwKBgQDRPchsq4zxxvqMYGxzMfyp7l8y1lLcORHS
j2qkOB0n973DggW2sLIEl3uqf/schpbYO8zFs/1YKrJ5LNnYF14GduugmS5znpnb
YvMJyTXFAqmcbl48ahVUvyOrgxTAOJOfFLRXwZiIVzaAaOop+Ph6A4hEYvXWJ8FW
j6lVr7mz8QKBgFYabArly95At/VLPyDR1U92+IP9v2o6/vadZyqO3org9nJdua/4
C1fDhVeDlHeyU9PwqN1rDTd5/k00RqT9d6IM+cdyPHgnl5AwysqhDyTFDJfDfQku
9tmZfa1gF7DNkSnvWgh3eIRYoiCWTyEzd1x3ji+Xie5HOJLC4nxVTqRJAoGAZQb6
rZWLAPX85ShtVJVvFDFW37nh2hjoBQ1gBRhe43xXsH0n+xSHb3YgrKsMeLJ3RMJi
1ZZZHWfIMn+4UwC9Uku66xjq98I9MVMuW6w9/PiTIkeb0nm6AOgk9dvdeg4XILkj
djewSSwq0YdWgJuIhYkNE0/guN0LGZtVvFyTQlECfy3l4m4VwlaPRSSYUxUzULs7
xc34lL1mf09pWxWebZw4ILQQ90DGWOSD/Zgq6CryRfYgsYqXmGNgDbUFRTWh2DMq
6IoLG3wiqrKSW2oFQL3UOzws0ag7C+6aqKnydpQoEtaP5X+DfAWdAOqnsOP1Ry+W
VTrmtVm4yLiMPBnsw3I=
-----END PRIVATE KEY-----

0 comments on commit ab29b64

Please sign in to comment.