Skip to content

Commit

Permalink
NewTypes for clearer encoding types (#474)
Browse files Browse the repository at this point in the history
* class prototype and script to find instances to change

* script added

* Set up newtype file with example newTypes

* added newtypes to _util.py

* renamed newtypes, added keyID

* deletion of old file

* added hexstr newtype and implemented newtypes for SigningResults

* added newtypes to verify/models.py

* renamed newtypes to follow standardized format

* moved newtypes into _util

* deleted newtypes.py

* Changed sign.py to use _utils and set up basic implementation in verifier

* build(deps-dev): update ruff requirement from <0.0.226 to <0.0.229 (#466)

Updates the requirements on [ruff](https://github.com/charliermarsh/ruff) to permit the latest version.
- [Release notes](https://github.com/charliermarsh/ruff/releases)
- [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](astral-sh/ruff@v0.0.18...v0.0.228)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: emboman13 <[email protected]>

* build(deps-dev): update ruff requirement from <0.0.229 to <0.0.231 (#468)

Updates the requirements on [ruff](https://github.com/charliermarsh/ruff) to permit the latest version.
- [Release notes](https://github.com/charliermarsh/ruff/releases)
- [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](astral-sh/ruff@v0.0.18...v0.0.230)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: emboman13 <[email protected]>

* build(deps-dev): update ruff requirement from <0.0.231 to <0.0.232 (#469)

Updates the requirements on [ruff](https://github.com/charliermarsh/ruff) to permit the latest version.
- [Release notes](https://github.com/charliermarsh/ruff/releases)
- [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md)
- [Commits](astral-sh/ruff@v0.0.18...v0.0.231)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: emboman13 <[email protected]>

* Initial Sigstore bundle support (#465)

* Initial Sigstore bundle support

Signed-off-by: William Woodruff <[email protected]>

* README: update `--help` texts

Signed-off-by: William Woodruff <[email protected]>

* sign: fix bundle generation

Certs are base64'd DER, not PEM, and the canonicalized_body
is the log entry body, not the canonicalized contents that
the SET is signed over.

Signed-off-by: William Woodruff <[email protected]>

* sign: remove TODO

Signed-off-by: William Woodruff <[email protected]>

* sign: update TODO

Signed-off-by: William Woodruff <[email protected]>

* _cli: Make `--bundle` refer to a path and create a `--no-bundle` flag
to control whether Sigstore bundles are emitted by default

Signed-off-by: Alex Cameron <[email protected]>

* _cli: Move variable to correct scope

Signed-off-by: Alex Cameron <[email protected]>

* _cli: Reword warnings for bundle flags

Signed-off-by: Alex Cameron <[email protected]>

* README: Fix sign example

Signed-off-by: Alex Cameron <[email protected]>

* README: Update verify invocations

Signed-off-by: Alex Cameron <[email protected]>

* README: Fix line breaks

Signed-off-by: Alex Cameron <[email protected]>

* _cli: fix sig output

Signed-off-by: William Woodruff <[email protected]>

* _cli: fix sig check, take 2

Signed-off-by: William Woodruff <[email protected]>

Signed-off-by: William Woodruff <[email protected]>
Signed-off-by: Alex Cameron <[email protected]>
Co-authored-by: Alex Cameron <[email protected]>
Signed-off-by: emboman13 <[email protected]>

* CHANGELOG: record changes (#470)

Signed-off-by: William Woodruff <[email protected]>

Signed-off-by: William Woodruff <[email protected]>
Signed-off-by: emboman13 <[email protected]>

* class prototype and script to find instances to change

Signed-off-by: emboman13 <[email protected]>

* script added

Signed-off-by: emboman13 <[email protected]>

* Set up newtype file with example newTypes

Signed-off-by: emboman13 <[email protected]>

* renamed newtypes, added keyID

Signed-off-by: emboman13 <[email protected]>

* deletion of old file

Signed-off-by: emboman13 <[email protected]>

* added hexstr newtype and implemented newtypes for SigningResults

Signed-off-by: emboman13 <[email protected]>

* added newtypes to _util.py

Signed-off-by: emboman13 <[email protected]>

* renamed newtypes to follow standardized format

Signed-off-by: emboman13 <[email protected]>

* added newtypes to verify/models.py

Signed-off-by: emboman13 <[email protected]>

* moved newtypes into _util

Signed-off-by: emboman13 <[email protected]>

* deleted newtypes.py

Signed-off-by: emboman13 <[email protected]>

* Changed sign.py to use _utils and set up basic implementation in verifier

Signed-off-by: emboman13 <[email protected]>

* added newtypes to sigstore/veriry/models.py

* updated newtypes in verify/models.py

Signed-off-by: omartounsi7 <[email protected]>

* Revert "updated newtypes in verify/models.py"

This reverts commit f767d7a.

* Encapsulation of NewTypes in my share of files

Creation of new type 'dercert' that masks DER encoded bytes. Focus
on changing types in files within sigstore/_internal/.  Reformat, lint
lint is successful. 103 Tests pass, 8 are skipped, 2 fail.

* Removed an incorrect b64str newtype in models.py

Signed-off-by: omartounsi7 <[email protected]>

* "added newtypes to _internal/rekor/client.py"

Signed-off-by: omartounsi7 <[email protected]>

* "fixed type errors in sign.py"

Signed-off-by: omartounsi7 <[email protected]>

* Added a b64str newtype in verify/models.py

Signed-off-by: omartounsi7 <[email protected]>

* added a b64str newtype in verify/verifier.py

Signed-off-by: omartounsi7 <[email protected]>

* added a b64str newtype to _internal/fulcio/client.py

Signed-off-by: omartounsi7 <[email protected]>

* added a b64str newtype in _internal/oidc/oauth.py

Signed-off-by: omartounsi7 <[email protected]>

* added a b64str newtype in _internal/rekor/client.py

Signed-off-by: omartounsi7 <[email protected]>

* deleted script

Signed-off-by: omartounsi7 <[email protected]>

* fixed some type errors

* changed keyid to KeyID

Signed-off-by: omartounsi7 <[email protected]>

* anged hexstr to HexStr

Signed-off-by: omartounsi7 <[email protected]>

* changed b64str to B64Str

Signed-off-by: omartounsi7 <[email protected]>

* changed pemcert to PEMCert

Signed-off-by: omartounsi7 <[email protected]>

* changed dercert to DERCert

Signed-off-by: omartounsi7 <[email protected]>

* added docstrings to newtypes in _utils.py

Signed-off-by: omartounsi7 <[email protected]>

* Update sigstore/_utils.py

Co-authored-by: William Woodruff <[email protected]>
Signed-off-by: omartounsi7 <[email protected]>

* Update sigstore/_utils.py

Co-authored-by: William Woodruff <[email protected]>
Signed-off-by: omartounsi7 <[email protected]>

---------

Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: emboman13 <[email protected]>
Signed-off-by: William Woodruff <[email protected]>
Signed-off-by: Alex Cameron <[email protected]>
Signed-off-by: omartounsi7 <[email protected]>
Signed-off-by: omartounsi7 <[email protected]>
Co-authored-by: emboman13 <[email protected]>
Co-authored-by: omartounsi7 <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William Woodruff <[email protected]>
Co-authored-by: Alex Cameron <[email protected]>
Co-authored-by: omartounsi7 <[email protected]>
Co-authored-by: William Woodruff <[email protected]>
  • Loading branch information
8 people authored Feb 14, 2023
1 parent e657188 commit 71ca534
Show file tree
Hide file tree
Showing 12 changed files with 95 additions and 52 deletions.
3 changes: 2 additions & 1 deletion sigstore/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from sigstore._internal.fulcio.client import DEFAULT_FULCIO_URL, FulcioClient
from sigstore._internal.rekor.client import DEFAULT_REKOR_URL, RekorClient
from sigstore._internal.tuf import TrustUpdater
from sigstore._utils import PEMCert
from sigstore.oidc import (
DEFAULT_OAUTH_ISSUER_URL,
STAGING_OAUTH_ISSUER_URL,
Expand Down Expand Up @@ -818,7 +819,7 @@ def _collect_verification_state(
with file.open(mode="rb", buffering=0) as io:
materials = VerificationMaterials(
input_=io,
cert_pem=cert_pem,
cert_pem=PEMCert(cert_pem),
signature=signature,
rekor_entry=entry,
offline=args.offline,
Expand Down
4 changes: 2 additions & 2 deletions sigstore/_internal/ctfe.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec, rsa

from sigstore._utils import key_id, load_pem_public_key
from sigstore._utils import KeyID, key_id, load_pem_public_key


class CTKeyringError(Exception):
Expand Down Expand Up @@ -70,7 +70,7 @@ def add(self, key_pem: bytes) -> None:
key = load_pem_public_key(key_pem)
self._keyring[key_id(key)] = key

def verify(self, *, key_id: bytes, signature: bytes, data: bytes) -> None:
def verify(self, *, key_id: KeyID, signature: bytes, data: bytes) -> None:
"""
Verify that `signature` is a valid signature for `data`, using the
key identified by `key_id`.
Expand Down
8 changes: 5 additions & 3 deletions sigstore/_internal/fulcio/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
)
from pydantic import BaseModel, Field, validator

from sigstore._utils import B64Str

logger = logging.getLogger(__name__)

DEFAULT_FULCIO_URL = "https://fulcio.sigstore.dev"
Expand Down Expand Up @@ -193,9 +195,9 @@ def __init__(self, url: str, session: requests.Session) -> None:

def _serialize_cert_request(req: CertificateSigningRequest) -> str:
data = {
"certificateSigningRequest": base64.b64encode(
req.public_bytes(serialization.Encoding.PEM)
).decode()
"certificateSigningRequest": B64Str(
base64.b64encode(req.public_bytes(serialization.Encoding.PEM)).decode()
)
}
return json.dumps(data)

Expand Down
7 changes: 4 additions & 3 deletions sigstore/_internal/merkle.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import struct
from typing import List, Tuple

from sigstore._utils import HexStr
from sigstore.transparency import LogEntry


Expand Down Expand Up @@ -123,9 +124,9 @@ def verify_merkle_inclusion(entry: LogEntry) -> None:
leaf_hash, inclusion_proof.hashes[:inner], inclusion_proof.log_index
)

calc_hash: str = _chain_border_right(
intermediate_result, inclusion_proof.hashes[inner:]
).hex()
calc_hash: HexStr = HexStr(
_chain_border_right(intermediate_result, inclusion_proof.hashes[inner:]).hex()
)

if calc_hash != inclusion_proof.root_hash:
raise InvalidInclusionProofError(
Expand Down
5 changes: 3 additions & 2 deletions sigstore/_internal/oidc/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import uuid
from typing import Any, Dict, List, Optional, cast

from sigstore._utils import B64Str
from sigstore.oidc import IdentityError, Issuer

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -170,13 +171,13 @@ def __init__(self, client_id: str, client_secret: str, issuer: Issuer):
self._state = str(uuid.uuid4())
self._nonce = str(uuid.uuid4())

self.code_verifier = (
self.code_verifier = B64Str(
base64.urlsafe_b64encode(os.urandom(32)).rstrip(b"=").decode()
)

@property
def code_challenge(self) -> str:
return (
return B64Str(
base64.urlsafe_b64encode(
hashlib.sha256(self.code_verifier.encode()).digest()
)
Expand Down
10 changes: 5 additions & 5 deletions sigstore/_internal/rekor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

from sigstore._internal.ctfe import CTKeyring
from sigstore._internal.tuf import TrustUpdater
from sigstore._utils import base64_encode_pem_cert
from sigstore._utils import B64Str, base64_encode_pem_cert
from sigstore.transparency import LogEntry

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -137,9 +137,9 @@ def get(

def post(
self,
b64_artifact_signature: str,
b64_artifact_signature: B64Str,
sha256_artifact_hash: str,
b64_cert: str,
b64_cert: B64Str,
) -> LogEntry:
"""
Submit a new entry for inclusion in the Rekor log.
Expand Down Expand Up @@ -202,9 +202,9 @@ def post(
"apiVersion": "0.0.1",
"spec": {
"signature": {
"content": base64.b64encode(signature).decode(),
"content": B64Str(base64.b64encode(signature).decode()),
"publicKey": {
"content": base64_encode_pem_cert(certificate),
"content": B64Str(base64_encode_pem_cert(certificate)),
},
},
"data": {
Expand Down
12 changes: 6 additions & 6 deletions sigstore/_internal/sct.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
CTKeyringError,
CTKeyringLookupError,
)
from sigstore._utils import key_id
from sigstore._utils import DERCert, KeyID, key_id

logger = logging.getLogger(__name__)

Expand All @@ -50,7 +50,7 @@ def _pack_signed_entry(
#
# [0]: opaque ASN.1Cert<1..2^24-1>
pack_format = "!BBB{cert_der_len}s"
cert_der = cert.public_bytes(encoding=serialization.Encoding.DER)
cert_der = DERCert(cert.public_bytes(encoding=serialization.Encoding.DER))
elif sct.entry_type == LogEntryType.PRE_CERTIFICATE:
if not issuer_key_id or len(issuer_key_id) != 32:
raise InvalidSctError("API misuse: issuer key ID missing")
Expand All @@ -62,7 +62,7 @@ def _pack_signed_entry(
pack_format = "!32sBBB{cert_der_len}s"

# Precertificates must have their SCT list extension filtered out.
cert_der = cert.tbs_precertificate_bytes
cert_der = DERCert(cert.tbs_precertificate_bytes)
fields.append(issuer_key_id)
else:
raise InvalidSctError(f"unknown SCT log entry type: {sct.entry_type!r}")
Expand All @@ -85,14 +85,14 @@ def _pack_signed_entry(
def _pack_digitally_signed(
sct: SignedCertificateTimestamp,
cert: Certificate,
issuer_key_id: Optional[bytes],
issuer_key_id: Optional[KeyID],
) -> bytes:
"""
Packs the contents of `cert` (and some pieces of `sct`) into a structured
blob, one that forms the signature body of the "digitally-signed" struct
for an SCT.
The format of the digitally signed data is described in IETF's RFC 6962.
The format of the digitaly signed data is described in IETF's RFC 6962.
"""

# No extensions are currently specified, so we treat the presence
Expand Down Expand Up @@ -190,7 +190,7 @@ def verify_sct(
# to expose this trivial single member, so we use the `log_id`
# attribute directly.
ct_keyring.verify(
key_id=sct.log_id, signature=sct.signature, data=digitally_signed
key_id=KeyID(sct.log_id), signature=sct.signature, data=digitally_signed
)
except CTKeyringLookupError as exc:
# We specialize this error case, since it usually indicates one of
Expand Down
34 changes: 29 additions & 5 deletions sigstore/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import base64
import hashlib
import sys
from typing import IO, Union
from typing import IO, NewType, Union

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec, rsa
Expand All @@ -32,8 +32,30 @@
else:
from importlib import resources


PublicKey = Union[rsa.RSAPublicKey, ec.EllipticCurvePublicKey]

HexStr = NewType("HexStr", str)
"""
A newtype for `str` objects that contain hexadecimal strings (e.g. `ffabcd00ff`).
"""
B64Str = NewType("B64Str", str)
"""
A newtype for `str` objects that contain base64 encoded strings.
"""
PEMCert = NewType("PEMCert", str)
"""
A newtype for `str` objects that contain PEM-encoded certificates.
"""
DERCert = NewType("DERCert", bytes)
"""
A newtype for `bytes` objects that contain DER-encoded certificates.
"""
KeyID = NewType("KeyID", bytes)
"""
A newtype for `bytes` objects that contain a key id.
"""


class InvalidKey(Exception):
"""
Expand Down Expand Up @@ -61,15 +83,17 @@ def load_pem_public_key(key_pem: bytes) -> PublicKey:
return key


def base64_encode_pem_cert(cert: Certificate) -> str:
def base64_encode_pem_cert(cert: Certificate) -> B64Str:
"""
Returns a string containing a base64-encoded PEM-encoded X.509 certificate.
"""

return base64.b64encode(cert.public_bytes(serialization.Encoding.PEM)).decode()
return B64Str(
base64.b64encode(cert.public_bytes(serialization.Encoding.PEM)).decode()
)


def key_id(key: PublicKey) -> bytes:
def key_id(key: PublicKey) -> KeyID:
"""
Returns an RFC 6962-style "key ID" for the given public key.
Expand All @@ -80,7 +104,7 @@ def key_id(key: PublicKey) -> bytes:
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)

return hashlib.sha256(public_bytes).digest()
return KeyID(hashlib.sha256(public_bytes).digest())


def sha256_streaming(io: IO[bytes]) -> bytes:
Expand Down
22 changes: 12 additions & 10 deletions sigstore/sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
from sigstore._internal.rekor.client import RekorClient
from sigstore._internal.sct import verify_sct
from sigstore._internal.tuf import TrustUpdater
from sigstore._utils import sha256_streaming
from sigstore._utils import B64Str, HexStr, PEMCert, sha256_streaming
from sigstore.transparency import LogEntry

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -166,7 +166,7 @@ def sign(
artifact_signature = private_key.sign(
input_digest, ec.ECDSA(Prehashed(hashes.SHA256()))
)
b64_artifact_signature = base64.b64encode(artifact_signature).decode()
b64_artifact_signature = B64Str(base64.b64encode(artifact_signature).decode())

# Prepare inputs
b64_cert = base64.b64encode(
Expand All @@ -175,17 +175,19 @@ def sign(

# Create the transparency log entry
entry = self._rekor.log.entries.post(
b64_artifact_signature=b64_artifact_signature,
b64_artifact_signature=B64Str(b64_artifact_signature),
sha256_artifact_hash=input_digest.hex(),
b64_cert=b64_cert.decode(),
b64_cert=B64Str(b64_cert.decode()),
)

logger.debug(f"Transparency log entry created with index: {entry.log_index}")

return SigningResult(
input_digest=input_digest.hex(),
cert_pem=cert.public_bytes(encoding=serialization.Encoding.PEM).decode(),
b64_signature=b64_artifact_signature,
input_digest=HexStr(input_digest.hex()),
cert_pem=PEMCert(
cert.public_bytes(encoding=serialization.Encoding.PEM).decode()
),
b64_signature=B64Str(b64_artifact_signature),
log_entry=entry,
)

Expand All @@ -195,17 +197,17 @@ class SigningResult(BaseModel):
Represents the artifacts of a signing operation.
"""

input_digest: str
input_digest: HexStr
"""
The hex-encoded SHA256 digest of the input that was signed for.
"""

cert_pem: str
cert_pem: PEMCert
"""
The PEM-encoded public half of the certificate used for signing.
"""

b64_signature: str
b64_signature: B64Str
"""
The base64-encoded signature.
"""
Expand Down
6 changes: 4 additions & 2 deletions sigstore/transparency.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
from pydantic import BaseModel, Field, StrictInt, StrictStr, validator
from securesystemslib.formats import encode_canonical

from sigstore._utils import B64Str


@dataclass(frozen=True)
class LogEntry:
Expand All @@ -43,7 +45,7 @@ class LogEntry:
Not present for `LogEntry` instances loaded from Sigstore bundles.
"""

body: str
body: B64Str
"""
The base64-encoded body of the transparency log entry.
"""
Expand Down Expand Up @@ -71,7 +73,7 @@ class LogEntry:
Only present for entries retrieved from online logs.
"""

signed_entry_timestamp: str
signed_entry_timestamp: B64Str
"""
The base64-encoded Signed Entry Timestamp (SET) for this log entry.
"""
Expand Down
Loading

0 comments on commit 71ca534

Please sign in to comment.