Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Challenges of Building a Sigstore Client fr...

The Challenges of Building a Sigstore Client from Scratch

Delivered at SigstoreCon 2024 in Salt Lake City, Utah on November 12, 2024

Samuel E. Giddins

November 12, 2024
Tweet

More Decks by Samuel E. Giddins

Other Decks in Technology

Transcript

  1. RubyGems.org @segiddins ▪ Samuel Giddins ▪ RubyGems, Bundler, RubyGems.org maintainer

    & security lead ▪ Security Engineer in Residence @ Ruby Central ▪ Developer, maintainer, debugger of sigstore-ruby Your intrepid presenter
  2. RubyGems.org What is a Sigstore Client? Any guesses? A sigstore

    client can be summarized as two functions: • A function that verifies an artifact + sigstore bundle against a trusted root • A function that signs an artifact against a trusted root
  3. RubyGems.org RubyGems.org What is a Sigstore? → A TUF repository

    to serve the trusted root → A service that issues signing certs based on OIDC tokens → Don’t forget the CT Log! → A service that records handwaves in a transparency log → A bundle format that has artifacts from all of the above
  4. RubyGems.org Above all, Sigstore is a cryptosystem that we’re going

    to use to build trust for software artifacts. So it’s important we get it right.
  5. RubyGems.org RubyGems.org Professor ChatGPT why is writing a sigstore client

    from scratch so difficult Writing a Sigstore client from scratch can be quite difficult for several reasons, primarily due to the complexity of the underlying cryptographic protocols, integration with various services, and handling security concerns. Here's a breakdown of the challenges you might face: 1. Complexity of the Sigstore Protocol 2. Cryptographic Foundations 3. Interfacing with Sigstore Services 4. Security Considerations 5. Integration with Other Tools 6. API Design and Usability 7. Testing and Debugging
  6. RubyGems.org sigstore-ruby implementation goals • Pure Ruby implementation of both

    signing & verification flows • 100% vendorable inside of RubyGems & Bundler ◦ And by extension, all Ruby distributions themselves • Don’t trust Sam to write novel cryptography code
  7. RubyGems.org Why not a rust wrapper? • Dependency on compiling

    native code is a no-go for language-level dependency • Need to be able to update sigstore-ruby outside of ruby releases • WASM, JVM, etc. • “Rewrite it in Rust” isn’t a panacea
  8. RubyGems.org An end-to-end sigstore verification flow does a lot! •

    TUF ◦ Repository refresh ◦ Target download • Read bundle JSON & validate bundle • Hash artifact • Establish a trusted source of time • Perform x509 path validation • Perform signed certificate timestamp validation • Verify inclusion of the log entry in the transparency log • Verify the certificate against a policy • Verify signatures against the artifact & signing certificate • Ensure consistency between DSSE payload & policy & signing cert • 󰛢
  9. RubyGems.org The guarantee of all those pieces working are the

    responsibility of the client implementor. … me.
  10. RubyGems.org RubyGems.org Primitives → Protobuf (but only JSON) → RSA

    / ECDSA / Ed25519 → X509 → RFC3161 → Signed notes → Merkle trees → 2x JSON Canonicalization
  11. RubyGems.org RubyGems.org Signing Primitives → OIDC / JWTs → Key

    generation → More X509 → RFC3161 timestamp creation → Signature creation → Speaking hashedrekord / DSSE + intoto + SLSA + ACRONYM SOUP → Rekor & Fulcio HTTP Clients
  12. RubyGems.org That’s a lot of primitives to assume every language

    is going to have available. & Documented. & Functioning properly. & Secure.
  13. RubyGems.org → Wrapper around openssl → Multiple openssl versions →

    Ed25519 support? Sometimes! → Missing functionality → Basic querying about cert properties → Mixing up extension OIDs and short names → tbs_precert bytes → SCT validation → rfc3161 validation at a given timestamp RubyGems.org openssl gem
  14. RubyGems.org RubyGems.org → bouncycastle-based implementation of the openssl API →

    Broken X509 path validation with intermediary CAs → Missing Ed25519 support → Missing public key der export → … plus everything else missing from the C-ruby gem jruby- openssl
  15. RubyGems.org RubyGems.org → X509 wrapper to allow querying cert properties

    & typed extension values → RFC8785 JSON Canonicalization → X509 tbs_certificate_der → SCTs in general → rfc3161 support for arbitrary validation times → DSSE validation sigstore- ruby
  16. RubyGems.org How did this get so complicated? Sigstore is the

    amalgamation of multiple different systems • X509 for PKI • TUF for trusted material distribution • Merkle trees for transparency log inclusion • Signed notes for checkpoints
  17. RubyGems.org How did this get so complicated? Sigstore is the

    amalgamation of multiple different systems … which have a lot of configuration points
  18. RubyGems.org What does it mean to “add” sigstore? Now we’ve

    got a client. How do I add sigstore to RubyGems? This is our big unanswered question as a community.
  19. RubyGems.org What does it mean to “add” sigstore? • Where

    are attestations stored? • What “policy” should the sigstore client use to verify artifacts against their attestations? • How does all this change over time?
  20. RubyGems.org RubyGems.org What does it mean to “add” sigstore? →

    Users can specify expected mappings between packages & attestations → Attestations have agreed-upon meanings → Attestations are validated, stored, and served by package registries
  21. RubyGems.org RubyGems.org Error: policy.sigstore-error.kdl:0:74:error: policy failed to match rubygem "sigstore"

    { attestation { x509.subjectAltName "🤔" } } x509.subjectAltName "🤔": {:property=>"x509.subjectAltName", :actual=>"URI:https://github.com/sigstore/sigstor e-ruby/.github/workflows/release.yml@refs/tags/v0 .1.1", :expected=>"🤔"}
  22. RubyGems.org RubyGems.org TOFU: Offline mode: using cached trusted root No

    policy found for rubygem sigstore-0.1.1 Proposed policy: /* Added automatically by sigstore-verification */ rubygem "sigstore" { (call)githubAttestation owner="sigstore" repository="sigstore-ruby" workflow="release.yml" } Would you like to add it to policy.empty.kdl? no ERROR: Error installing sigstore: pre-install hook at /Users/segiddins/Development/github.com/rubygems/sigstore- verification/lib/rubygems_plugin.rb:3 failed for sigstore-0.1.1
  23. RubyGems.org RubyGems.org github Attestation (def)githubAttestation owner="string" repository="string" workflow="string" environment="string?" {

    attestation { x509 { issuerV2 "https://token.actions.githubusercontent.com" (var)buildConfigURI { (op)"+" "https://github.com/" (var)"owner" "/" (var)"repository" "/.github/workflows/" (var)"workflow" "@" (attr)"sourceRepositoryRef"; } (var)san { (op)"+" "URI:" (var)"buildConfigURI" ; } buildConfigURI (var)"buildConfigURI" subjectAltName (var)"san" } messageSignature (oneof)_ { messageSignature dsseEnvelope { payloadType "application/vnd.in-toto+json" payload { _type "https://in-toto.io/Statement/v1" predicateType "https://slsa.dev/provenance/v1" predicate.buildDefinition.externalParameters.workflow.repository (var)"repository" } } } } }
  24. RubyGems.org The point of building these sigstore clients is to

    use them in our various software ecosystems.
  25. RubyGems.org RubyGems.org Everything got a little bit better in 2024

    → sigstore-conformance → tuf-conformance → Edited sigstore client doc → Convergence between different independent implementations → Mock sigstore implementations for testing → William Woodruff saved the day more times than I could count
  26. RubyGems.org We’ve learned about building sigstore clients from scratch. Things

    will be clearer & easier as we continue evolving: Prototype⇒Early Adopters⇒Critical System