Skip to main content
BlogSecurity

OpenSSF Model Signing: Sign dan Verify Model ML dengan OMS

Saya membaca spec OMS v1.0, README model_signing, post NVIDIA tentang NGC dan paper Juli 2026 untuk melihat cakupan bundle Sigstore terpisah dan di mana enforcement masih kosong.

Matthews Wong

Software Engineer

Oktober 202612 menit baca
OpenSSF Model Signing: Sign dan Verify Model ML dengan OMS

Ringkasan Utama

OpenSSF Model Signing (OMS) menandatangani model ML sebagai satu bundle Sigstore terpisah: envelope DSSE berisi manifest in-toto dengan hash SHA-256 setiap file. Anda sign dengan CLI model_signing memakai key, certificate, atau identitas keyless Sigstore, lalu verify terhadap identitas yang diharapkan. Publisher sudah sign, tetapi enforcement sebelum serving masih banyak yang manual.

Pertanyaan yang membuat saya membaca spesifikasi OpenSSF Model Signing sampai tuntas sederhana saja: bagaimana saya tahu direktori model yang di-pull deployment saat pod start adalah yang disetujui tim release? Container image punya digest yang bisa dicek runtime. Folder berisi safetensors, tokenizer dan config tidak punya, dan jalur download menyajikan byte apa pun yang dikembalikan bucket.

Post ini adalah pembacaan teliti spesifikasi OMS v1.0, README model_signing, post peluncuran Sigstore, tulisan NVIDIA tentang NGC, dan paper arXiv Juli 2026 yang mengukur biaya verification pada jalur download Kubernetes. Saya tidak menjalankan perintah-perintah ini pada model untuk post ini. Setiap flag di bawah dikutip dari sumber yang saya baca, dan bila sumber-sumbernya berbeda, saya sebutkan.

Isi sebenarnya dari sebuah signature OMS

Signature OMS adalah file terpisah (detached) dalam Sigstore Bundle Format, bukan header yang ditempel ke weights. Di dalam bundle ada envelope DSSE yang membawa statement in-toto, signature atas statement itu, dan verification material seperti certificate chain. Predicate statement adalah manifest yang mendaftar setiap file beserta hash-nya, sehingga satu signature bisa mencakup weights, config, tokenizer dan dataset sebagai satu unit yang bisa diverifikasi. Repository spec tidak berisi kode signing: isinya spesifikasi, schema, test vector, dan conformance suite 79 test (31 roundtrip dan 48 verify), sedangkan implementasinya ada di tempat lain.

Spec v1.0 menetapkan detailnya. Predicate type harus persis URI di bawah, payload type DSSE adalah application/vnd.in-toto+json, dan predicate punya dua key wajib, yaitu resources dan serialization. Resources diurutkan berdasarkan name dengan urutan Unicode code point dan entri direktori tidak diperbolehkan. Digest subject adalah SHA-256 dari gabungan byte mentah digest resource, dan selalu SHA-256 apa pun hash_type file-nya. SHA-256 adalah hash default; BLAKE2b adalah alternatifnya.

# Shape of an OMS v1.0 signature bundle (decoded DSSE payload), per the spec.
# Digests are placeholders; "..." marks fields omitted for brevity.
{
  "_type": "https://in-toto.io/Statement/v1",
  "subject": [
    { "name": "bert-base-uncased",            # the model's basename
      "digest": { "sha256": "<root digest>" } }  # SHA-256 over the concatenated
  ],                                             # resource digests, always sha256
  "predicateType": "https://model_signing/signature/v1.0",   # exact URI required
  "predicate": {
    "resources": [                                # sorted by name, code point order
      { "name": "config.json",       "digest": "<hex>", "algorithm": "sha256" },
      { "name": "model.safetensors", "digest": "<hex>", "algorithm": "sha256" },
      { "name": "tokenizer.json",    "digest": "<hex>", "algorithm": "sha256" }
    ],
    "serialization": {
      "method": "files",             # or "shards" (then shard_size is required)
      "hash_type": "sha256",         # BLAKE2b is the alternative
      "allow_symlinks": false,
      "ignore_paths": []
    }
  }
}
# payloadType of the DSSE envelope: application/vnd.in-toto+json
# bundle mediaType in the spec example: application/vnd.dev.sigstore.bundle.v0.3+json

Ada dua konsekuensi. Verification menghitung ulang hash direktori dan membandingkannya dengan manifest, jadi signature melindungi file berdasarkan hash-nya, bukan lokasinya. Dan karena signature adalah file terpisah, spec menyatakan sebaiknya memakai ekstensi .sig, disimpan di direktori model atau parent-nya, dan dikeluarkan dari cakupan signing, kalau tidak bundle akan mencoba menandatangani dirinya sendiri.

Sign dan verify dengan CLI model_signing

Implementasi referensinya adalah package Python model-signing dari proyek Sigstore. Ia punya subcommand sign, verify dan digest, dan metode dipilih lewat subcommand: sigstore adalah default dan boleh dihilangkan, sedangkan key dan certificate ditulis eksplisit. Signing keyless membuka alur OIDC. README mencantumkan Google, GitHub, GitHub Actions dan Microsoft sebagai identity provider, serta GitHub Actions, GitLab CI, Google Cloud Platform dan Buildkite CI sebagai platform workload identity.

# Commands as published in the sigstore/model-transparency README.
# Keyless (default method, opens an OIDC flow in the browser):
model_signing sign bert-base-uncased

# Verify against the identity and issuer you EXPECT, not whatever the bundle says:
model_signing verify bert-base-uncased --signature model.sig \
  --identity "$identity" --identity-provider "$oidc_provider"

# Key-based: the method is a subcommand, not a flag.
model_signing sign key bert-base-uncased --private-key key.priv
model_signing verify key bert-base-uncased --signature resnet.sig --public-key key.pub

# Private Sigstore instance:
model_signing sign bert-base-uncased --trust-config client_trust_config.json

# Print the digest without signing (useful to compare two copies of a model):
model_signing digest bert-base-uncased

Verification meminta identitas yang Anda harapkan, bukan sekadar signature yang valid. Post peluncuran Sigstore menggambarkannya sebagai pengecekan model terhadap claimed identity dan OIDC provider, dan menyebut verification tetap berfungsi setelah certificate berumur pendek itu kedaluwarsa, karena yang dicek adalah certificate yang tercatat di log beserta inclusion proof-nya.

Sumber-sumber ini berbeda dalam penulisan flag. README saat ini memakai --private-key dan --identity-provider dengan tanda hubung, sedangkan post peluncuran April 2025 menulis --identity_provider dan panduan NVIDIA menulis --certificate_chain dengan underscore. Saya tidak menjalankan satu pun, jadi anggap model_signing verify --help pada versi yang Anda install sebagai acuan, dan pin versi package di CI.

Empat cara membangun trust

OMS sengaja tidak terikat PKI tertentu: bare key, self-signed certificate, private PKI dan Sigstore semuanya menghasilkan bentuk bundle yang sama. Yang berubah adalah trust anchor-nya, dan spec menjabarkan apa yang harus dilakukan verifier untuk masing-masing.

MetodeTrust anchorYang dicek verifier (spec v1.0)Cocok untuk
Bare keyPublic key yang sudah Anda pegangSignature terhadap public key di bundle; entri transparency log boleh tidak adaCluster air-gapped; desain di paper arXiv memakai alur key dan certificate di sana
Certificate (self-signed atau private PKI)Root CA yang Anda percayaiChain divalidasi sampai root tepercaya, masa berlaku leaf dicek, lalu signature; entri transparency log boleh tidak adaLangkah verify yang dipublikasikan NVIDIA memakai certificate hasil download lewat flag certificate chain
Sigstore, keyless publikIdentitas OIDC, disertifikasi lewat Fulcio dan dicatat di RekorAturan Sigstore Bundle termasuk verifikasi transparency log; entri log wajib adaModel yang di-build di CI dengan workload identity; perlu akses ke infrastruktur transparency publik
Sigstore, instance privatDeployment Sigstore milik Anda sendiriAturan sama dengan Sigstore publik, dengan file trust config yang diberikan lewat command lineOrganisasi yang tidak bisa memakai instance publik; README menunjukkan flag trust-config saat signing

Saya akan memilih Sigstore keyless sebagai default bila build berjalan di salah satu platform workload identity itu, karena tidak ada signing key berumur panjang yang bisa bocor dan penulis Sigstore merekomendasikannya sebagai default. Saya baru memakai key atau certificate bila verifier tidak bisa menjangkau infrastruktur transparency publik, karena keyless verification membutuhkannya.

Apa yang dicakup signature, dan apa yang diabaikan

Verification gagal secara default bila direktori berisi file yang tidak terdaftar di manifest. Inilah yang membuat signature menjadi pernyataan tentang seluruh folder model, bukan beberapa file saja: orang yang menaruh file tambahan di samping weights yang sudah di-sign akan membuat verification gagal. Opt-out-nya adalah opsi ignore-unsigned-files yang eksplisit, dan bahkan saat itu setiap file yang terdaftar tetap harus lolos verify.

Beberapa aturan spec lain yang perlu diketahui sebelum Anda menabraknya. .git, .gitignore, .gitattributes dan .github di level teratas dikecualikan secara default, dan hanya di root model. Exclusion dari user berupa path relatif persis tanpa wildcard. Symlink tidak diikuti secara default dan seharusnya memunculkan error. Nama file harus UTF-8 yang valid, path dibandingkan byte demi byte dan case-sensitive, dan model tanpa regular file ditolak.

# Default: any file in the directory that the manifest does not list fails.
model_signing verify ./llama-finetune --signature ./llama-finetune.sig \
  --identity "$identity" --identity-provider "$oidc_provider"

# Names from the spec (v1.0, sections on ignore rules and unsigned files);
# confirm the exact spelling with --help on your version.
#   --ignore-paths           exact relative paths, no * ? [ wildcards, "/" separator
#   --ignore-unsigned-files  tolerate unlisted files; listed ones must still verify

# Wrong: pointing the signature INSIDE the signed scope without excluding it.
# Right: the spec says the --signature file is excluded at sign AND verify time,
#        and recommends a .sig name in the model directory or its parent.

Spec juga mendefinisikan metode shards yang memecah tiap file menjadi rentang byte, dengan default yang disarankan 1.000.000.000 byte dan entri bernama path:start:end. Verifier yang tidak mengimplementasikan shards harus menolak bundle dengan error, bukan menebak, jadi pastikan verifier Anda mendukung metode yang dipakai signer sebelum di-rollout ke seluruh fleet.

NVIDIA NGC: produsen sudah melakukan signing

Adopsi di sisi publishing itu nyata. Blog developer NVIDIA menyebut setiap model terbitan NVIDIA di NGC Catalog sudah di-sign dengan OMS sejak Maret 2025, dan menyebut NGC sebagai model hub besar pertama yang menawarkan cryptographic signing untuk model yang dihosting. Bundle memverifikasi weights, file konfigurasi, tokenizer dan aset terkait sekaligus, UI NGC menampilkan badge Signed, dan listing CLI punya kolom Has Signed Version.

# NVIDIA's published verification flow for a signed NGC model.
pip install model-signing     # the post recommends pip hash-checking mode

ngc registry model download-version "nvidia/tao/dashcamnet:pruned_onnx_v1.0.5"
ngc registry model download-version-signature "nvidia/tao/dashcamnet:pruned_onnx_v1.0.5"

# The post's curl line combines -O and -o; I collapsed it to -o here.
curl -o cert.pem https://api.ngc.nvidia.com/v2/catalog/models/public-key

# The consumer verifies with NVIDIA's public certificate, not a Sigstore identity:
model_signing verify <MODEL_PATH> --signature <SIG_PATH> --certificate_chain cert.pem
# On success the post shows "Verification succeeded."

Itu cerita produsen yang lengkap dan cerita konsumen yang manual. Post tersebut tidak menjelaskan rotasi key, pencabutan certificate, atau verification otomatis saat download, dan hanya mencakup model terbitan NVIDIA, jadi model komunitas di luar cakupannya. Saya membaca kebisuan itu sebagai celah yang perlu diantisipasi, bukan bukti bahwa tidak ada.

Di mana enforcement sisi serving masih belum ada

Paper arXiv Juli 2026 karya Georgii Kliukovkin menyatakan celahnya dengan lugas: adopsi di sisi produsen nyata, tetapi enforcement sisi serving pada jalur download masih manual. Tabel jalur delivery KServe di paper itu menjelaskan alasannya. Bila model datang sebagai image reference di pod spec, tool admission seperti Kyverno dan sigstore policy-controller bisa memverifikasinya. Bila storage initializer mengambil file lepas, tidak ada yang mengamati download itu pada saat admission.

JalurSkemaDi-pull olehBisa diverifikasi saat admission
Modelcar sidecaroci://Container runtimeYa
Image volumeoci+native://kubelet (KEP-4639)Ya
Initializer OCI pulloci+fetch://Storage initializerTidak
Object storages3://, gs://, hf://Storage initializerTidak

Tool terdekat saat ini adalah sigstore model-validation-operator, yang di repository-nya disebut proof of concept. Sebuah webhook menyuntikkan init container ke pod yang membawa label yang merujuk ke resource ModelValidation, dan container itu mengecek model terhadap signature. Bila tidak cocok, hasilnya verification failed: the manifests do not match, dan workload tidak berjalan.

# From the sigstore/model-validation-operator README (proof of concept).
apiVersion: ml.sigstore.dev/v1alpha1
kind: ModelValidation
metadata:
  name: demo
spec:
  config:
    sigstoreConfig:
      certificateIdentity: "<URL of the GitHub workflow that signed the model>"
      certificateOidcIssuer: "https://token.actions.githubusercontent.com"
  model:
    path: /data/tensorflow_saved_model
    signaturePath: /data/tensorflow_saved_model/model.sig

# A pod in the same namespace opts in with this label; the webhook then injects
# the verifying init container. Pods labelled AFTER creation are not validated.
#   validation.ml.sigstore.dev/ml: "demo"

Kritik paper itu, operator ini platform-agnostic: ia memverifikasi volume yang sudah ada pada pod yang ditunjuk, tidak tahu apa-apa soal storage URI atau kode download platform serving, dan tidak menawarkan digest pinning. Penulisnya mengusulkan verification di dalam storage initializer KServe (RFC 5789) dan menyebutnya desain yang masih direview, bukan pekerjaan yang sudah di-merge. Anggap itu arah, bukan saklar yang bisa Anda nyalakan.

Biaya verification, dan bagaimana ia gagal

Paper itu mengukur separuh hashing dari verification pada AWS m6i.2xlarge dengan object store MinIO lokal, melaporkan median. Hashing selagi byte mengalir hampir gratis. Hashing setelah download sebagai pass kedua tidak.

ArtifactDownload biasaSHA-256 streamingSHA-256 pasca-download
14 GB190,3 s190,4 s (+0,05%)203,6 s (+7,0%)
140 GB2.220,4 s2.219,8 s (di bawah 0,1%)3.406,8 s (+53,4%)

Dua catatan membuat saya tidak mengutip ini sebagai biaya OMS. Ini angka hashing SHA-256: separuh signature, dan pada mode keyless round trip ke Fulcio dan Rekor, menurut paper tidak bergantung pada ukuran artifact dan belum diukur. Dan setup-nya single-node serta LAN-lokal. Paper juga melaporkan bahwa pada 140 GB storage initializer di-OOM-kill sampai memory limit-nya dinaikkan empat kali lipat, yang penting bila Anda menempelkan verification ke sebuah initializer.

Fail open adalah kesalahan yang mahal. Desain di paper fail closed pada setiap error verifier, bukan hanya saat mismatch, karena penyerang yang mengendalikan storage bisa menghapus file signature di samping artifact. Mode warn tetap menjalankan pod dan, menurut penulisnya, tidak boleh dikira sebagai security control.

Empat batasan lagi dari threat model yang sama. Signature yang valid membuktikan provenance, bukan intent: artifact apa pun yang pernah di-sign oleh identitas yang diizinkan akan lolos, sehingga penyerang yang menguasai storage bisa mengganti dengan revisi lama yang sudah di-sign atau model lain dari identitas release yang sama, itulah sebabnya policy di paper juga mengikat nama model yang diharapkan. Verification berjalan sekali saat initialisation, setelah itu sidecar yang berada di pod yang sama atau operator dengan pods/exec bisa mengubah byte-nya; mode sidecar opsional dari operator melakukan validasi ulang per interval seperti 10m tetapi hanya mencatat kegagalan dan tetap berjalan. Init container tidak dijalankan ulang saat hanya main container yang restart. Dan digest pinning belum menangani revisi hf:// yang mutable dengan baik.

Yang dilakukan hari Senin

Mulai dari bagian yang Anda kendalikan, yaitu publishing. Lalu jadikan verification gerbang yang keras di apa pun yang menjalankan model server Anda, dan baru setelah itu pikirkan mesin admission.

  1. Sign setiap direktori model di CI dengan Sigstore keyless bila platform build Anda ada di daftar workload identity, dan tulis signature di samping model dengan nama .sig.
  2. Catat identitas, OIDC issuer dan nama model yang Anda harapkan di config deployment, dan verify terhadap itu, jangan terhadap apa pun yang diklaim bundle.
  3. Verify sebelum model server memuat weights, dan keluar dengan kode non-zero pada kegagalan apa pun, termasuk file signature yang hilang.
  4. Bila Anda melakukan hash di loader sendiri, hash selagi streaming: pass kedua atas artifact 140 GB di paper memakan tambahan 53,4%.
  5. Putuskan sekarang apa yang dilakukan cluster air-gapped atau privat: verification dengan key atau certificate memakai trust config, karena keyless verification butuh infrastruktur transparency publik.

Script di bawah adalah sketsa saya sendiri, bukan desain KServe dan belum saya uji di sini. Ia hanya mengkodekan aturan fail-closed: tanpa signature, tidak jalan.

#!/bin/sh
# Entrypoint wrapper: no verified signature, no model server. My own sketch.
set -eu

MODEL_DIR=/models/llama-finetune
SIG="$MODEL_DIR.sig"            # kept OUTSIDE the signed scope, next to it
EXPECTED_IDENTITY="$SIGNER_IDENTITY"       # pinned in deployment config
EXPECTED_ISSUER="https://token.actions.githubusercontent.com"

# A missing bundle is a failure, not a skip: whoever controls storage can delete it.
test -s "$SIG" || { echo "signature missing, refusing to start" >&2; exit 1; }

model_signing verify "$MODEL_DIR" --signature "$SIG" \
  --identity "$EXPECTED_IDENTITY" --identity-provider "$EXPECTED_ISSUER" \
  || { echo "verification failed, refusing to start" >&2; exit 1; }

exec "$@"                       # only now start the model server

Signature pada model hanya bernilai sebesar apa yang dilakukan verifier terhadapnya. OMS menyediakan formatnya dan, lewat NGC, publisher yang memakainya. Aturan yang saya bawa pulang: verify di tempat byte dimuat, terhadap identitas dan nama model yang dipin, dan fail closed setiap kali ada bagian verification yang hilang.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa itu OpenSSF Model Signing (OMS)?

OMS adalah spesifikasi OpenSSF untuk menandatangani model ML. Hasilnya file signature terpisah dalam Sigstore Bundle Format, berisi envelope DSSE dengan manifest in-toto yang mendaftar setiap file model beserta hash-nya. Satu signature bisa mencakup weights, config, tokenizer dan dataset sekaligus.

Bagaimana cara sign dan verify model dengan model_signing?

Install package model-signing, lalu jalankan model_signing sign pada direktori model. Sigstore keyless adalah default, sedangkan metode key dan certificate berupa subcommand terpisah. Untuk verify, jalankan model_signing verify dengan file signature serta identitas dan OIDC provider yang Anda harapkan; cek output --help di versi Anda untuk penulisan flag yang tepat.

Apakah OMS bisa dipakai tanpa Sigstore atau akses internet?

Bisa. OMS mendukung bare key, self-signed certificate dan private PKI selain Sigstore. Keyless verification perlu menjangkau infrastruktur transparency publik, jadi cluster air-gapped sebaiknya memakai alur key atau certificate, atau instance Sigstore privat dengan trust config.

Apakah verification gagal bila ada file ditambahkan ke folder model?

Secara default, ya. Verifier gagal bila direktori berisi file yang tidak terdaftar di manifest yang sudah di-sign. Anda bisa opt-out dengan opsi ignore-unsigned-files, dan bahkan saat itu setiap file yang terdaftar di manifest tetap harus lolos verify.

Apakah verifikasi signature otomatis di-enforce saat serving model?

Umumnya belum. NVIDIA menandatangani model NGC dengan OMS, tetapi paper arXiv Juli 2026 melaporkan bahwa enforcement sisi serving pada jalur download masih manual. Operator sigstore model-validation-operator bisa menyuntikkan init container untuk verify, dan usulan enforcement bawaan di KServe masih dalam review.