Sigstore 클라이언트는 기본적으로 두 가지 핵심 기능을 수행합니다. 첫째, 아티팩트와 Sigstore 번들이 신뢰할 수 있는 루트에 대해 일치하는지 검증합니다. 둘째, 아티팩트를 신뢰할 수 있는 루트에 대해 암호학적으로 서명합니다. 이 두 기능은 겉보기와 달리 매우 복잡하며, Sigstore는 서명, 검증, 출처 확인을 위한 다양한 기술(X509 PKI, TUF, Merkel trees, Signotes)의 통합체입니다.
Sigstore Ruby 구현 목표 및 도전 과제
- 순수 Ruby 구현: 서명 및 검증 흐름 모두 순수 Ruby로 구현하는 것이 목표였습니다. 이는 RubyGems 및 Bundler 내부에 100% 벤더링(vendoring)되어 Ruby 배포판에 포함될 수 있도록 하기 위함입니다.
- 신규 암호화 코드 작성 회피: 기존 암호화 라이브러리를 최대한 활용하고 신규 암호화 코드 작성을 피하는 것이 중요한 목표였습니다.
‘순수 Ruby’의 정의는 ISO Ruby, mini Ruby, 표준 라이브러리만 사용 등 다양하지만, 실제로는 MRI, JRuby, TruffleRuby의 비EOL(End-of-Life) 버전에서 모두 작동하는 코드를 의미합니다. 그러나 업계 표준 라이브러리의 대부분이 C로 작성되어 있고, Ruby 표준 라이브러리의 프리미티브 또한 상당 부분 네이티브 코드로 구현되어 있어 순수 Ruby로 복잡한 암호화 기능을 구현하는 것은 매우 어려운 작업입니다.
암호화 프리미티브의 한계와 직접 구현
Sigstore 클라이언트 구현에는 Google의 Protocol Buffers (JSON 인코딩), RSA, ECC DSA, ED25519와 같은 다양한 암호화 서명 알고리즘, X.509 서명 인증서, RFC 3161 서명 인증서 타임스탬프, Merkel tree, 두 가지 JSON 정규화 형식 등 수많은 프리미티브가 필요합니다. 하지만 Ruby 표준 라이브러리(특히 OpenSSL Gem)는 이러한 요구사항을 모두 충족하지 못했습니다.
- OpenSSL Gem의 한계: 특정 OpenSSL 버전에서 ED25519 미지원, 인증서 속성 쿼리 기능 부족, X.509 확장 OID 처리 문제,
to_be_signed_prescertificate
바이트 추출 미구현, 서명된 인증서 타임스탬프 및 RFC 3161 검증 기능 부족 등이 있었습니다. 이 중 상당수는 Samuel Giddens가 직접 PR을 보내거나 순수 Ruby로 재구현해야 했습니다. - JRuby OpenSSL의 추가적인 한계: JRuby OpenSSL은 Bouncy Castle 기반으로 구현되었으며, 중간 CA를 통한 X.509 경로 검증 버그, ED25519 미지원, 공개 키 DER 내보내기 미지원 등 C Ruby Gem보다 더 많은 기능이 누락되어 있었습니다. 결국 JRuby에서 Sigstore Ruby를 작동시키기 위해 원시
Java.security
API를 직접 사용하는 코드를 작성해야 했습니다.
이러한 제한된 환경으로 인해 Sigstore Ruby 자체적으로 X.509 래퍼, RFC 8785 JSON 정규화, ASN.1 바이트 조작을 통한 인증서 정보 추출, 서명된 인증서 타임스탬프 및 RFC 3161 지원, DESI(Dead Simple Security Envelopes) 지원, 그리고 TUF(The Update Framework) 클라이언트까지 직접 구현해야 했습니다. 이는 두 가지 기능 구현을 위해 예상보다 훨씬 많은 하위 시스템을 구축해야 했음을 의미합니다.