Slide 1

Slide 1 text

RubyGems.org The Challenges of Building a Sigstore Con 2024 Sigstore Implementation from Scratch Samuel Giddins

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

RubyGems.org My preferred trip to Utah

Slide 4

Slide 4 text

RubyGems.org RubyGems.org Obligatory [bd]ad joke I’ve been consuming SBOMs since before some of you were born Snack Bowls of M&Ms

Slide 5

Slide 5 text

RubyGems.org Disclaimer

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

RubyGems.org … those functions are complicated

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

RubyGems.org First line of code: February First release: October First production usage: this week?

Slide 14

Slide 14 text

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 ● 󰛢

Slide 15

Slide 15 text

RubyGems.org The guarantee of all those pieces working are the responsibility of the client implementor. … me.

Slide 16

Slide 16 text

RubyGems.org RubyGems.org Primitives → Protobuf (but only JSON) → RSA / ECDSA / Ed25519 → X509 → RFC3161 → Signed notes → Merkle trees → 2x JSON Canonicalization

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

RubyGems.org That’s a lot of primitives to assume every language is going to have available. & Documented. & Functioning properly. & Secure.

Slide 19

Slide 19 text

RubyGems.org RubyGems.org Ruby Stdlib → openssl gem → That’s it for cryptographic primitives → JSON

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

RubyGems.org RubyGems.org sigstore-ruby … TUF (it was tough)

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

RubyGems.org How did this get so complicated? Sigstore is the amalgamation of multiple different systems … which have a lot of configuration points

Slide 26

Slide 26 text

RubyGems.org Who is ready to talk about the 🐘 in the room?

Slide 27

Slide 27 text

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.

Slide 28

Slide 28 text

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?

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

RubyGems.org github.com/rubygems/sigstore-verification

Slide 31

Slide 31 text

RubyGems.org RubyGems.org Error: rubygem "sigstore" { attestation { x509.subjectAltName "🤔" } }

Slide 32

Slide 32 text

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=>"🤔"}

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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" } } } } }

Slide 35

Slide 35 text

RubyGems.org The point of building these sigstore clients is to use them in our various software ecosystems.

Slide 36

Slide 36 text

RubyGems.org Multiple implementations mitigate the impact of a bug found in any one implementation.

Slide 37

Slide 37 text

RubyGems.org Multiple implementations help make our specifications more precise and portable.

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

RubyGems.org Building sigstore-ruby in 2024 was challenging. Building sigstore-insert your language here in 2025 will be easier.

Slide 41

Slide 41 text

RubyGems.org Thank you Samuel Giddins Security Engineer in Residence at RubyGems.org Sponsored by