00:00:08.160
well I know that everyone is probably at this point just dying to hear Matt's
00:00:15.360
keynote and take a long long nap Um it's
00:00:20.560
been a fun week So thank you for showing up for what
00:00:27.119
might be the most boring talk of this entire conference Going to talk about
00:00:32.320
Sig Store Ruby Um just a little show of hands Who here
00:00:39.200
has heard of Sig Store i see a couple of hands Who here
00:00:45.520
has heard of Ruby
00:00:50.800
well you're at Ruby Kaiiki Come on You've heard of Ruby Um so uh one of the two parts of this
00:00:58.559
talk will be a bit more familiar than the other So a little bit about me I am at
00:01:07.439
SE Giddens everywhere on the internet Um my name is Samuel I am the Rubygeems
00:01:15.680
Bundler and Rubygeems.org or uh security lead and a member of those core teams
00:01:22.280
Um by day I am the security engineer and residents at Ruby
00:01:28.280
Central and also by day cuz you know part of my job um I am the developer
00:01:35.200
maintainer and most frequently the debugger of sig store Ruby
00:01:43.360
I just want to say uh if there's anyone else observing Passover this week I did
00:01:50.320
have to bring my own supply of matzah to Japan Um they took up about half of my
00:01:59.079
suitcase Okay now on to the meat of this talk Now as a
00:02:06.520
disclaimer everything in this talk is about the learnings I took away from building Sig Store Ruby In no way do I
00:02:14.239
mean this to be a negative talk Um I know I sometimes sound negative It's not
00:02:21.480
intentional It's just you know being a New Yorker Uh but I want us as a
00:02:27.040
community to take away inspiration on how we can make building this sort of software in Ruby just a little bit
00:02:35.560
easier So Sig Store Ruby is a pure Ruby implementation of SIG store verification
00:02:42.800
and signing There's a lovely GitHub repo that I'm amazed that 18 people have
00:02:49.920
bothered to star Uh it is also a Ruby gem of course
00:02:56.400
the Sig store Ruby gem Um and what's
00:03:01.680
pretty cool about Sig Store Ruby is it powers that very bottom section of the
00:03:09.840
gem page on rubygeems.org That little bit that says Provenence built and signed on GitHub actions the commit the
00:03:18.159
build file the transparency log entry Yeah that's the thing that I spent like
00:03:25.519
all of last year building um just to get that one little bit on this
00:03:31.959
website So SIG store for those of you
00:03:37.040
who aren't familiar it's built to answer the question of where does a gem come
00:03:42.680
from what what commit was it built from what repo when was it built on what CI
00:03:50.080
system and to answer that question in a way that is cryptographically
00:03:55.959
verifiable and in the future all this is going to be used as the basis for
00:04:01.120
policies around what gems to install So you can say things like
00:04:06.439
hm every gem that starts with the prefix
00:04:11.480
AWS where should that come from who should have published it
00:04:16.680
probably the folks over at AWS because you know they've got like 230 of them
00:04:22.479
and sometimes it's hard to keep them straight So what is a sig store client
00:04:30.639
what is it I spent all this time building it can be summarized as two
00:04:36.160
functions Function number one verifies that an
00:04:41.199
artifact and a SIG store bundle match against a trusted route And function
00:04:48.000
number two is cryptographically signing an artifact against a trusted route
00:04:54.160
Piece of cake Two functions No challenges here Okay fun talk I'll see you later
00:05:04.759
Unfortunately those two functions are
00:05:09.800
complicated So getting back into it take a step back What is a SIG
00:05:16.360
store i will quote from their website uh sigstore.dev Software safety
00:05:25.000
integrated a combination of technologies to handle signing verification and
00:05:30.560
provenence checks that respect privacy and work at
00:05:36.120
scale Show of hands if that means anything to
00:05:41.479
you No Okay So we've we've all been uh sort of inoculated against marketing
00:05:47.280
speak Good Good to see
00:05:53.120
Being a bit more specific what is a sig store it's um composed of a bunk a bunch
00:05:58.880
of things Number one is a a uh tough which stands for the update framework
00:06:06.000
It's a tough repository that serves a trusted route Um it is a service that
00:06:12.639
issues signing certificates based on OIDC tokens
00:06:18.800
Don't forget there's also a certificate transparency log so you can see what certificates have been
00:06:25.000
issued There's a service that records stuff in a transparency log and there's
00:06:33.120
a bundle format that has artifacts from all of the above that you can show to
00:06:38.880
people and they can do the cryptographic verification
00:06:44.919
thing Above all you know six door is a crypto system that we're going to use to
00:06:50.319
build trust for software artifacts Super serious stuff So it's important we get
00:06:55.680
it right Now I went and I asked um our favorite professor Professor Chachi BT
00:07:04.319
why is writing a SIG store client from scratch so
00:07:09.479
difficult here's the answer Writing a SIG store client from scratch can be
00:07:14.560
quite difficult for several reasons Primarily due to the complexity of the underlying cryptographic protocols
00:07:21.360
integration with various services and handling security concerns Here's a breakdown of the challenges you might
00:07:28.120
face Uh I I love just getting other people to write my slides for me There's
00:07:34.240
the complexity of the SIG store protocol There's the sheer volume of the
00:07:39.840
cryptographic foundations There's interfacing with all the different services all the security configurations
00:07:46.960
I love how that's just a bullet point You know we're we're building a security library One of the things that one of
00:07:53.360
the challenges is the security that real helpful integration with other
00:07:59.400
tools API design and usability testing and debugging So basically everything
00:08:05.599
about building a SIG store client from scratch is difficult Thanks Chat GPT makes me feel
00:08:13.080
better We had a bunch of implementation goals when I set off to build sig store
00:08:18.520
Ruby Number one was having a pure Ruby implementation of both signing and verification flows
00:08:26.680
Um number two was being 100% vendorable inside of Ruby Gems and
00:08:32.680
Bundler because we want to be able to be to vendor Ruby gems and bundler inside of
00:08:40.479
the Ruby distributions So anything vendored inside of Ruby gems and bundler needs to also be vendorable
00:08:48.160
And um biggest implementation goal was don't trust this guy to write any novel
00:08:54.640
crypto cryptography code Here's a kind of difficult question
00:09:02.640
What does it mean to be pure Ruby i came up with a with a couple of
00:09:10.160
options here Um just let me know when you think I've I've gotten to a good definition Well
00:09:17.519
there's an ISO standard for Ruby So is pure Ruby something that runs on ISO
00:09:24.920
Ruby probably not I don't think I've seen anyone reference that standard in
00:09:30.000
quite a long time Is it only Ruby code that's
00:09:35.680
runnable with mini Ruby is it Ruby code that only requires the
00:09:43.040
standard library or only requires the standard library and bundle
00:09:49.480
gems and you know what about J Ruby and truffle Ruby running Ruby on Wom
00:09:57.080
etc All these are kind of reasonable definitions for what it means to be pure
00:10:02.640
Ruby code And the consensus around theu community
00:10:09.120
seems to be uh you know it's the non-endoflife versions of MRI and the
00:10:17.040
latest version of J Ruby and the latest version of truffle Ruby and using the
00:10:22.320
stuff that's available on all three of them which is the standard library default and bundle gems Um this is what
00:10:31.680
Ruby gems targets This is what bundler targets This is what a bunch of the Ruby
00:10:37.519
gemified standard library targets But writing pure Ruby is still
00:10:45.600
kind of hard You know industry standard
00:10:50.800
libraries for better or more realistically for worse are still mostly
00:10:56.480
written in C They're not written in Ruby
00:11:01.839
Ruby standard library primitives are largely implemented in native code not
00:11:07.519
in Ruby And every layer of wrappers that we
00:11:13.360
deal with leads to sort of an increased impedance mismatch
00:11:20.320
You know trying to write code that digs through like six layers of
00:11:26.000
wrapper code means it's hard to sometimes do exactly what you need to
00:11:32.920
do And finally when you're supporting older Ruby versions and multiple
00:11:40.079
different implementations of Ruby well that means you can take even less for granted you you can only depend on
00:11:46.079
what's in the intersection of all of those different
00:11:51.720
Rubies So it was suggested for a while that
00:11:56.959
everyone building a SIG store client should just write a wrapper around some Rust libraries That was going to be the
00:12:03.959
solution Like Rust fixes everything I was told
00:12:09.760
But you know it turns out that wasn't going to work for us Having a dependency on compiling
00:12:15.600
native code was a no-go for something that needed to be able to ship inside of Ruby gems
00:12:22.760
Um also like writing Rust code that then deals with both Java and C sounds like a
00:12:32.560
miserable time We also want the ability to you know
00:12:39.200
update SIG store Ruby outside of Ruby releases So we weren't going to go and
00:12:44.240
stick that Rust wrapper inside of Ruby itself Um not that they would let me do
00:12:50.160
that anyway And as I said before you know support
00:12:55.279
for all of the different platforms that Ruby runs on makes wrapping a single native
00:13:01.839
library a really hard answer And unfortunately all that you
00:13:08.959
know just rewrite it and rust and everything will be fixed and the world will be perfect um turned out just to
00:13:16.800
not be the panacea we were promised So I started on our pure Ruby
00:13:24.399
implementation of a sixstar client wrote the first line of code last
00:13:30.680
February shipped the first release in October and had its first use in
00:13:37.200
production uh mid November of last
00:13:43.720
year That's a lot of time I spent building this one small library
00:13:51.760
So you know Sam why are you so bad at your job why' this take you so
00:13:58.040
long i heard you all agreeing with that comment Um and it turns out that doing
00:14:04.880
an endto-end store verification flow it does a lot of stuff So I'm just going to
00:14:10.639
roll through the list of all the things here And if you're confused about any of the
00:14:17.480
acronyms well yeah there's just a bunch of acronym soup to learn So SIG store
00:14:25.120
deals with the update framework which is a whole specification unto
00:14:31.800
itself Bunch of different functionality like refreshing tough repositories
00:14:37.360
downloading targets We then uh read a SIG store bundle JSON
00:14:44.320
file and validate the bundle We have to be able to cryptographically hash artifacts to match them against the
00:14:51.320
bundle Establish a trusted source of time from the bundle and X509
00:14:58.560
certificates perform X509 path validation to make sure that the certificates we're
00:15:05.959
verifying actually come from the root of trust that we're
00:15:11.399
expecting Perform signed certificate timestamp validation against those
00:15:17.800
certificates Verify inclusion of log entries in transparency log Verify the
00:15:24.800
certificate against a policy that says "Hey what who's supposed to be able to
00:15:30.160
sign for this artifact?" Verify that the signatures against the artifact and the the signing
00:15:36.959
certificate match and ensure consistency between
00:15:42.720
what's in the payload of the bundle and the policy for the artifacts that we accept and the signing certificate and
00:15:49.759
make sure all that matches And you know cross your fingers and hope you did all
00:15:56.160
1 2 3 4 5 6 7 8 9 10 11 12 13 steps
00:16:02.839
correctly And you know I'm just a Ruby engineer Um it's a lot of plumbing that
00:16:10.880
I was responsible for um in addition to needing to write a lot of these
00:16:16.199
primitives And unfortunately the guarantee that all of these pieces work
00:16:22.040
together is the responsibility of the person implementing a SIG store client
00:16:29.240
And that's me and that's that's kind of scary I don't you know I don't trust
00:16:38.120
me So there are a bunch of primitives that
00:16:44.079
are needed to even start thinking about implementing a sigar client and they're
00:16:49.680
all things that you probably take for granted but it's worth enumerating them
00:16:55.880
So sig store stuff is defined in terms of Google's protocol buffers
00:17:03.519
Fortunately only the JSON encoding of protobuffs is used But that means you
00:17:09.520
need a protobuff implementation You need support for
00:17:14.559
different cryptographic signing algorithms Um just what's required by the spec
00:17:21.360
specification is RSA uh elliptic curve DSA and ED25519
00:17:29.440
You have to support X509 signing certificates RFC 3161
00:17:36.160
uh signed certificate timestamps I'm pretty sure that RFC is signed certificate timestamps If anyone knows
00:17:42.400
off the top of their head that I am wrong congratulations I'm sorry you know that off the top of your head Um there's
00:17:50.080
this signed note format that Go started using for uh the Go Sum DB There's
00:17:57.600
Merkel trees underpinning all of the public transparency log stuff There's
00:18:03.520
two different JSON canonicalization formats because did you know that there
00:18:08.799
exists two different JSON canonicalization formats that's like two times as many as there should
00:18:16.600
be And then there's even more primitives that are needed to sign So there's all
00:18:22.960
of the stuff from the previous slide In addition you have to be able to understand
00:18:29.120
uh JavaScript web tokens that represent an open ID connect
00:18:35.720
identity You have to generate a key do more X509
00:18:42.039
stuff create one of those uh signed
00:18:47.400
timestamps make a signature and then speak the log entry format that SIG store
00:18:56.559
expects which is a whole bunch of acronym soup Um and sometimes I wish I
00:19:02.720
didn't know it because it's not the tasty kind of soup you know And then there's HTTP clients that
00:19:11.200
are needed to talk to two of the different services that constitute SIG store Uh Recor is the transparency log
00:19:19.760
Folio is the uh certificate issuer
00:19:27.080
And as I was building all this I'm like that is a lot of primitives that you're
00:19:33.039
assuming that every language has available and documented and working
00:19:41.799
properly and implemented securely And in the places where that
00:19:47.600
stuff wasn't available or wasn't documented or wasn't working properly I had to go and implement stuff
00:19:55.360
myself And as I said I don't trust me to do that And also that that's a lot of work
00:20:02.799
that I didn't want to do So as a reminder everything I was doing was
00:20:10.480
limited to the Ruby standard library because the goal is to ship a SIG store verifier inside of Ruby
00:20:17.400
Gems and Ruby Gems ships with Ruby and J Ruby and
00:20:24.559
Truffle Ruby So that means no dependencies that can't be vendored
00:20:32.159
which means plain Ruby code So what do we have available we have the
00:20:38.000
OpenSSL gem That's about it for cryptographic primitives We have the JSON
00:20:45.880
module That was everything that the standard library gave me that I needed
00:20:52.039
So diving in OpenSSL gem it is a wrapper around OpenSSL It supports a bunch of
00:21:00.320
different OpenSSL versions Um up until last year I think it supported up to all
00:21:08.080
the way back to like OpenSSL0.9 something which is
00:21:14.360
ancient Um on some of those supported OpenSSL versions it didn't have support
00:21:19.520
for ED25519 which I needed to be able to pass the uh SIG store conformance test
00:21:26.960
suite it's missing some functionality that's present inside of
00:21:33.240
OpenSSL So things like querying about certificate properties
00:21:39.159
Um there's the fact that extension oids and short names get mixed up Um so being
00:21:47.200
confident about what X509 extensions you're talking about is a little
00:21:52.640
difficult um pulling out the to be signed
00:21:59.280
prescertificate bytes from a certificate wasn't implemented until I
00:22:04.320
sent a pull request and even then because we support all the way back to
00:22:10.480
Ruby 3.2 I had to reimplement that in pure Ruby as
00:22:16.600
well Was missing sign certificate timestamp validation which again had to
00:22:22.640
just reimplement in pure Ruby and was also missing RFC
00:22:28.760
3161 validation at my choice of time stamp it would only validate against the
00:22:36.000
current time which is not what Sig store needs Now believe it or not this is the
00:22:44.080
better version of the OpenSSL gem There's also J Ruby
00:22:50.600
OpenSSL And not a knock on the J Ruby team writing a Java based wrapper around
00:22:58.320
a Java library that tries to mimic the OpenSSL API is a challenge and I
00:23:05.760
wouldn't want to do that So kudos to them for doing it but it's missing even
00:23:10.960
more stuff So J Ruby OpenSSL is a bouncy castlebased implementation of the
00:23:17.679
OpenSSL API There is a longstanding bug where
00:23:23.120
you can't do X509 path validation against a certificate chain that has
00:23:29.039
intermediary certificate authorities It's missing ED25519 support
00:23:36.919
entirely It's missing or it was missing I I have to give them credit They they
00:23:42.720
did implement this a few months ago Uh public key dur export
00:23:48.280
support and it's also missing everything else that's missing from the C Ruby
00:23:54.200
gem Now it was so difficult to get some of the stuff working on J Ruby that I had
00:24:01.600
to reach down into the raw Java.security APIs to have support for all the primitives I needed So here's a here's a
00:24:09.360
fun commit that I landed last December where I reimplemented all the
00:24:15.360
missing J Ruby functionality just using I mean just in this room we were talking about how in J Ruby one of the great
00:24:23.360
things is you can just use Java classes and Java methods I had to do that to get
00:24:29.279
sig store Ruby working on J Ruby and uh it was a whole bunch of changes and if
00:24:36.640
you look at those changes they're pretty gnarly
00:24:44.120
So inside of SIG store Ruby itself we also implemented an X509
00:24:51.279
wrapper to allow querying certificate properties and typed extension
00:24:57.320
values Implemented RFC 8785 JSON
00:25:02.360
canonicalization As I said there's multiple JSON canonicalizations This is one of them
00:25:09.360
um implemented by
00:25:14.520
manually shifting around the uh ASN1 bytes of X509 certificates Um grabbing
00:25:22.559
out the to be signed prescertificate during coding Um everything with signed
00:25:28.960
certificate timestamps also had to be implemented outside of an SSL
00:25:35.159
um RFC 3161 support uh support for DESI
00:25:41.520
which is something simple dead simple security envelopes I
00:25:48.559
think is what DESI stand for not that dead simple I had to implement an entire
00:25:56.799
tough client um and you know it was tough to do that Um and inside of TU is
00:26:06.240
where I got to implement the second JSON canonicalization
00:26:12.279
scheme How did this all get so complicated we started by saying we're
00:26:17.679
implementing two functions This is a lot of stuff for two functions
00:26:24.480
And it happened because SIG store is the amalgamation of multiple different
00:26:30.520
systems and uses X509 for PKI TU for a trusted material
00:26:37.240
distribution Merkel trees for transparency log inclusion and signotes for
00:26:42.760
checkpoints All of these have libraries in some ecosystems If if you write go
00:26:48.480
you can you know import the X509 library You can import the tough client You can
00:26:54.640
import the Merkel tree library You can import the sign note library from
00:27:00.760
uh uh Go's module system All of that's there but not all of these things are
00:27:08.080
there for Ruby and they're certainly not all in the standard library And even X509 which is is difficult to work with
00:27:19.679
Additionally all these things have multiple different configuration points
00:27:25.440
different switches you can flip and that leads to a combinatorial explosion of
00:27:30.640
the configuration space Different key types signature schemes two ways of
00:27:36.559
encoding uh the contents of SIG store bundles different desi payload types
00:27:43.200
different in toto statement types That's just a lot of different stuff to
00:27:51.720
implement And you know the point of building all this wasn't even just to build the client It was to integrate it
00:27:59.600
into Ruby gems and rubygeems.org The good news is you know
00:28:06.480
this was not the first sig store client It was one of many And implement
00:28:11.960
re-implementing these things helps mitigate the impact of a bug found in any one
00:28:18.440
implementation Re-implementing this stuff over and over helps make the sig store specification more precise and
00:28:26.080
more portable You know each new implementation including sig store ruby
00:28:31.760
hit different edge cases You know each implementation provides a
00:28:37.200
different way of thinking about the specification we're trying to
00:28:42.679
implement Luckily everything I was doing got a little bit better in 2024 We got
00:28:49.360
multiple conformance test suites so I didn't have to go and write my own tests
00:28:54.640
There was a wonderfully edited SIG store client talk that I could go down and use as a checklist
00:29:01.600
There's convergence between different implementations There's a moxig store implementation and uh my buddy William
00:29:08.720
Woodruff just helped me a ton And so okay we talked a bit about
00:29:16.080
building SIG store clients from scratch The good news is the next client someone builds will be easier You know
00:29:24.320
the Sig store community has gone from prototyping to early adopters to now being sort of a critical and well-used
00:29:30.880
production system Building Sig Store Ruby last year
00:29:35.919
was challenging and I really hope that the next person building a SIG store
00:29:41.600
client has an easier time So come say hi if you want to talk
00:29:48.559
about or contribute to SIG store Ruby Come say hi if you want to make
00:29:54.640
implementing sig store Ruby a little bit easier in Ruby And thank you