Skip to content

APIs design #172

Open
Open
@laurentsimon

Description

This issues proposes what the (long-term) APIs will look like. Looking for comments, nothing set in stone.

1. Hash engine

Tracked in #140
Why:

  • for callers to customize the hash parameters
  • for callers to customize hash implementation, eg using hw-accelerattion or distributed across machines.
class hash_engine:
  def name() -> str:
  # Shard size
  def shard() -> int
  # Chunk size
  def chunk() -> int
  # Standard hash APIs
  def update(...):..
  def final():..

We will provide default hash engine that we use in this library, with the possibility to customize its parameters:

class shah256pv1:
   def __init__(self, shard=1000000, chunk=memory_available())
   # All class function from hash_engine

A hash name is parameterized. I suggest something simple like <name>$param1$param2.... For the existing sha256p, it could be sha256pv1$1000000 for a shard of 1 GB.

2. Serializer

This will serialize a model (folder or file).
Why: some callers may want to serialize models using our library but not sign it with our library.

class serializer:
   def __init__(self, hash: hash_engine)
      ...
   def run(
          # Path is a file-system path.
          # PathIter is a path iterator, for callers who want to keep things in memory.
          input: Path | PathIter,
          # A list of paths to recompute Tracked in https://github.com/sigstore/model-transparency/issues/160
          recompute_paths: []Path = None,
          # HashedPathIter is an iterator for path+hash computed
          ) -> HashedPathIter:
       
     ...
)

3. Raw Signer / Verifier

This provides low-level signer: It allows to abstract away how private key is managed (TUF, HSM, static key, etc). The raw verifier lets callers set a custom verifier if needed, eg if the signature uses a different encoding, etc.

class RawSigner:
  @abstract
  def sign(raw: bytes) -> bytes
     # Perform signature via HSM API, use PCKS11 API, use raw bytes from memory, etc

We can have helper function for common raw signers like (one will be the default?):

class ECDSARawSigner(RawSigner):
  def from_path(path: filepath.Path):
     ...
  def from_bytes(key: bytes):
     # Verifies that 1) the key is of the right type, eg right length and on the right curve, 2) private and public key correspond to the same key pair. 
    ...
  def sign(raw):
   # Use the raw key
   ...

4. Signer / Verifier

This will create a generic signer class that can be instantiated for Sigstore, PKI, etc.

class Signer:
  def sign(
          # Path is a file-system path.
          # PathIter is a path iterator, for callers who want to keep things in memory.
          # HashedPathIter is an iterator for pre-computed path+hash.
          inputs: Path | PathIter | HashedPathIter,
	) -> bytes
     ...

class Verifier:
  def verify(
          input: Path | PathIter | HashedPathIter,
          sig: Path | bytes,
          ) -> bool

Example for Sigstore keyless:

# sigstore.py
class SigstoreKeylessSigner(Signer)
  def __init__(self, rekor_url, fulcio_cert, ...)
  def sign(input) -> bytes

class SigstoreKeylessVerifier(Verifier)
  def __init__(self, rekor_url, fulcio_tuf, ...)
  def verify(input, sig) -> bool

Example for Sigstore key flow:

# sigstore.py
class SigstoreKeyedSigner(Signer)
  def __init__(self, rekor_url, raw_signer, ...)
  def sign(input) -> bytes

class SigstoreKeyedVerifier(Verifier)
  def __init__(self, rekor_url, raw_verifier, ...)
  def verify(input, sig) -> bool

Example for PKI:

# pki.py
class PKISigner(Signer)
  def __init__(self, certs_chain, raw_signer, ...)
  def sign(input) -> bytes

class PKIVerifier(Verifier)
  def __init__(self, certs_chain, raw_verifier, ...)
  def verify(input, sig) -> bool

Example for ECSigner (no cert):

# ec.py
class ECSigner(Signer)
  def __init__(self, raw_signer, ...)
  def sign(input) -> bytes

class ECVerifier(Verifier)
  def __init__(self, raw_verifier, ...)
  def verify(input, sig) -> bool

4. model sign / verify

This is the main entry point for callers to use.

# model.py
def sign(
      input: Path | PathIter | HashedPathIter,
      sig: Path  = defaultSigPath(input),
      signer: Signer = SigstoreSigner(),
      he: hash_engine = shah256pv1(),
      recompute_paths: []Path = None,
      ignored_paths: []Path = [".git"],
    ) -> bytes
   # High level logic.
   s = serializer(he)
   iter = s.run(input, recompute_paths)
   m = manifest(iter)
   c = signer.sign(m)
   sig.write(c)
   return c

def verify(
      input: Path | PathIter | HashedPathIter,
      sig: Path | bytes = defaultSigPath(input),
      verifier: Verifier = SigstoreVerifier(),
      he: hash_engine = shah256pv1(),
      ignored_paths: []Path = [".git"],
    ) -> bool
   # High level logic.
   s = serializer(he)
   iter = s.run(input, recompute_paths)
   m = manifest(iter)
   return verifier.verify(m, sig)

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions