From bed56a615a7a31361cf88cece9f31cc303360343 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:58:51 +0000 Subject: [PATCH] Bump the actions group across 1 directory with 5 updates (#124) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Samuel Giddins --- .github/workflows/ci.yml | 22 +++++++++++----------- .github/workflows/codeql.yml | 8 ++++---- .github/workflows/dependency-review.yml | 2 +- .github/workflows/scorecard.yml | 4 ++-- bin/conformance-entrypoint | 1 + bin/sigstore-ruby | 9 +++++++-- lib/sigstore/error.rb | 1 + lib/sigstore/internal/key.rb | 3 +-- lib/sigstore/internal/util.rb | 13 +++++++++++++ lib/sigstore/models.rb | 25 ++++++++++++++++++++++--- lib/sigstore/tuf/error.rb | 1 + lib/sigstore/tuf/roles.rb | 11 ++++++----- lib/sigstore/verifier.rb | 23 ++++++++++++++++++----- 13 files changed, 88 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93ddeb2..1272248 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,9 +38,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Ruby - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1.194.0 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true @@ -76,21 +76,21 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Ruby - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1.194.0 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - name: Run the conformance tests - uses: sigstore/sigstore-conformance@bcbeee6cda143ee50f852f22a96162b111bf8a71 + uses: sigstore/sigstore-conformance@2c7252ea71e5719372869e439ec872167b68341a with: entrypoint: ${{ github.workspace }}/bin/conformance-entrypoint xfail: "${{ matrix.ruby != 'head' && 'test_verify_rejects_bad_tsa_timestamp' }}" if: ${{ matrix.os }} == "ubuntu-latest" - name: Run the conformance tests against staging - uses: sigstore/sigstore-conformance@bcbeee6cda143ee50f852f22a96162b111bf8a71 + uses: sigstore/sigstore-conformance@2c7252ea71e5719372869e439ec872167b68341a with: entrypoint: ${{ github.workspace }}/bin/conformance-entrypoint xfail: "${{ matrix.ruby != 'head' && 'test_verify_rejects_bad_tsa_timestamp' }}" @@ -124,9 +124,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Ruby - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1.194.0 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true @@ -135,7 +135,7 @@ jobs: run: touch requirements.txt - name: Run the TUF conformance tests - uses: theupdateframework/tuf-conformance@307fb63cefc6c47d2c4c20e579f5e2f258c9e83f + uses: theupdateframework/tuf-conformance@f9c4fd0cd872eae20acb02ed339cfe7dfa1554c2 with: entrypoint: ${{ github.workspace }}/bin/tuf-conformance-entrypoint artifact-name: "test repositories ${{ matrix.ruby }} ${{ matrix.os }}" @@ -180,9 +180,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Set up Ruby - uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442 # v1.193.0 + uses: ruby/setup-ruby@c04af2bb7258bb6a03df1d3c1865998ac9390972 # v1.194.0 with: ruby-version: ${{ fromJson(needs.ruby-versions.outputs.latest) }} bundler-cache: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index aad0967..74c2621 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,11 +46,11 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 + uses: github/codeql-action/init@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -60,7 +60,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 + uses: github/codeql-action/autobuild@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 # ℹī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -73,6 +73,6 @@ jobs: # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 + uses: github/codeql-action/analyze@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index c120929..5c03af2 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -22,6 +22,6 @@ jobs: egress-policy: audit - name: 'Checkout Repository' - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: 'Dependency Review' uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 0a833a0..4ca2e58 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -37,7 +37,7 @@ jobs: egress-policy: audit - name: "Checkout code" - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false @@ -73,6 +73,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8 + uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 with: sarif_file: results.sarif diff --git a/bin/conformance-entrypoint b/bin/conformance-entrypoint index 5df951b..1bed1b4 100755 --- a/bin/conformance-entrypoint +++ b/bin/conformance-entrypoint @@ -24,4 +24,5 @@ ENV.update( ) load File.expand_path("sigstore-ruby", __dir__) +ARGV.delete("--verify-digest") Sigstore::CLI.start(ARGV) diff --git a/bin/sigstore-ruby b/bin/sigstore-ruby index 847c6cf..b82c282 100755 --- a/bin/sigstore-ruby +++ b/bin/sigstore-ruby @@ -187,7 +187,7 @@ module Sigstore all_materials = [] files.each do |file| - raise Thor::InvocationError, "File not found: #{file}" unless File.exist?(file) + raise Thor::InvocationError, "File not found: #{file}" unless File.exist?(file) || file.start_with?("sha256:") sig = options[:signature] cert = options[:certificate] @@ -215,7 +215,12 @@ module Sigstore input_map.each do |file, inputs| artifact = Sigstore::Verification::V1::Artifact.new - artifact.artifact = File.binread(file) + case file + when /\Asha256:/ + artifact.artifact_uri = file + else + artifact.artifact = File.binread(file) + end verification_input = Sigstore::Verification::V1::Input.new verification_input.artifact = artifact diff --git a/lib/sigstore/error.rb b/lib/sigstore/error.rb index 0388e66..7f06fc5 100644 --- a/lib/sigstore/error.rb +++ b/lib/sigstore/error.rb @@ -25,6 +25,7 @@ class NoBundle < Error; end class NoSignature < Error; end class InvalidKey < Error; end class InvalidCheckpoint < Error; end + class InvalidVerificationInput < Error; end class Signing < Error; end class InvalidIdentityToken < Error; end diff --git a/lib/sigstore/internal/key.rb b/lib/sigstore/internal/key.rb index b18fd69..93a642b 100644 --- a/lib/sigstore/internal/key.rb +++ b/lib/sigstore/internal/key.rb @@ -97,12 +97,11 @@ def initialize(...) raise ArgumentError, "key must be an OpenSSL::PKey::EC, is #{@key.inspect}" end - raise ArgumentError, "schema must be #{schema}" unless @schema == schema case @schema when "ecdsa-sha2-nistp256" unless @key.group.curve_name == "prime256v1" - raise ArgumentError, "Expected prime256v1 curve, got #{key.group.curve_name}" + raise ArgumentError, "Expected prime256v1 curve, got #{@key.group.curve_name}" end else raise ArgumentError, "Unsupported schema #{schema}" diff --git a/lib/sigstore/internal/util.rb b/lib/sigstore/internal/util.rb index 0c5378b..f237d52 100644 --- a/lib/sigstore/internal/util.rb +++ b/lib/sigstore/internal/util.rb @@ -19,6 +19,19 @@ module Internal module Util module_function + def hash_algorithm_name(algorithm) + case algorithm + when Common::V1::HashAlgorithm::SHA2_256 + "sha256" + when Common::V1::HashAlgorithm::SHA2_384 + "sha384" + when Common::V1::HashAlgorithm::SHA2_512 + "sha512" + else + raise ArgumentError, "Unrecognized hash algorithm #{algorithm}" + end + end + def hex_encode(string) string.unpack1("H*") end diff --git a/lib/sigstore/models.rb b/lib/sigstore/models.rb index 915d3ce..48e7fd7 100644 --- a/lib/sigstore/models.rb +++ b/lib/sigstore/models.rb @@ -86,7 +86,26 @@ def initialize(*) raise Error::InvalidVerificationInput, "bundle with message_signature requires an artifact" end - @hashed_input = OpenSSL::Digest.new("SHA256").update(artifact.artifact) + case artifact.data + when :artifact_uri + unless artifact.artifact_uri.start_with?("sha256:") + raise Error::InvalidVerificationInput, + "artifact_uri must be prefixed with 'sha256:'" + end + + @hashed_input = Common::V1::HashOutput.new.tap do |hash_output| + hash_output.algorithm = Common::V1::HashAlgorithm::SHA2_256 + hexdigest = artifact.artifact_uri.split(":", 2).last + hash_output.digest = Internal::Util.hex_decode(hexdigest) + end + when :artifact + @hashed_input = Common::V1::HashOutput.new.tap do |hash_output| + hash_output.algorithm = Common::V1::HashAlgorithm::SHA2_256 + hash_output.digest = OpenSSL::Digest.new("SHA256").update(artifact.artifact).digest + end + else + raise Error::InvalidVerificationInput, "Unsupported artifact data: #{artifact.data}" + end freeze end @@ -189,8 +208,8 @@ def expected_hashed_rekord_tlog_entry(hashed_input) }, "data" => { "hash" => { - "algorithm" => hashed_input.name.downcase, - "value" => hashed_input.hexdigest + "algorithm" => Internal::Util.hash_algorithm_name(hashed_input.algorithm), + "value" => Internal::Util.hex_encode(hashed_input.digest) } } }, diff --git a/lib/sigstore/tuf/error.rb b/lib/sigstore/tuf/error.rb index 70c7b4b..9d191c2 100644 --- a/lib/sigstore/tuf/error.rb +++ b/lib/sigstore/tuf/error.rb @@ -29,6 +29,7 @@ class TooFewSignatures < RepositoryError; end class BadUpdateOrder < Error; end class InvalidData < Error; end + class DuplicateKeys < Error; end # An error occurred while attempting to download a file. class DownloadError < Error; end diff --git a/lib/sigstore/tuf/roles.rb b/lib/sigstore/tuf/roles.rb index 4362da0..f9eb48a 100644 --- a/lib/sigstore/tuf/roles.rb +++ b/lib/sigstore/tuf/roles.rb @@ -76,9 +76,11 @@ def terminating? end def verify_delegate(type, bytes, signatures) - verified_key_ids = Set.new + if (duplicate_keys = signatures.map { |sig| sig.fetch("keyid") }.tally.select { |_, count| count > 1 }).any? + raise Error::DuplicateKeys, "Duplicate keys found in signatures: #{duplicate_keys.inspect}" + end - signatures.each do |signature| + count = signatures.count do |signature| key_id = signature.fetch("keyid") unless @keys.include?(key_id) logger.warn "Unknown key_id=#{key_id.inspect} in signatures for #{type}" @@ -89,12 +91,11 @@ def verify_delegate(type, bytes, signatures) signature_bytes = [signature.fetch("sig")].pack("H*") verified = key.verify("sha256", signature_bytes, bytes) - added = verified_key_ids.add?(key_id) if verified logger.debug do - "key_id=#{key_id.inspect} type=#{type} verified=#{verified} added=#{added.nil? ? added.inspect : true}" + "key_id=#{key_id.inspect} type=#{type} verified=#{verified}" end + verified end - count = verified_key_ids.size return unless count < @threshold diff --git a/lib/sigstore/verifier.rb b/lib/sigstore/verifier.rb index 23434fb..bf53223 100644 --- a/lib/sigstore/verifier.rb +++ b/lib/sigstore/verifier.rb @@ -175,8 +175,7 @@ def verify(input:, policy:, offline:) case bundle.content when :message_signature - verified = signing_key.verify(input.hashed_input.name, bundle.message_signature.signature, - input.artifact.artifact) + verified = verify_raw(signing_key, bundle.message_signature.signature, input.hashed_input.digest) return VerificationFailure.new("Signature verification failed") unless verified when :dsse_envelope verify_dsse(bundle.dsse_envelope, signing_key) or @@ -198,6 +197,19 @@ def verify(input:, policy:, offline:) private + def verify_raw(public_key, signature, data) + if public_key.respond_to?(:verify_raw) + public_key.verify_raw(nil, signature, data) + else + case public_key + when OpenSSL::PKey::EC + public_key.dsa_verify_asn1(data, signature) + else + raise Error::Unimplemented, "unsupported public key type: #{public_key.class} for raw verification" + end + end + end + def verify_dsse(dsse_envelope, public_key) payload = dsse_envelope.payload payload_type = dsse_envelope.payloadType @@ -224,12 +236,13 @@ def verify_in_toto(input, in_toto_payload) digest = subject.fetch("digest") raise Error::InvalidBundle, "Expected in-toto statement with digest" if !digest || digest.empty? + expected_hexdigest = Internal::Util.hex_encode(input.hashed_input.digest) digest.each do |name, value| - next if input.hashed_input.hexdigest == value + next if expected_hexdigest == value return VerificationFailure.new( - "in-toto subject does not match for #{input.hashed_input.name} of #{subject.fetch("name")}: " \ - "expected #{name} to be #{value}, got #{input.hashed_input.hexdigest}" + "in-toto subject does not match for #{input.hashed_input.algorithm} of #{subject.fetch("name")}: " \ + "expected #{name} to be #{value}, got #{expected_hexdigest}" ) end end