seedwing-io / seedwing-policy

A functional type system for policy inspection, audit and enforcement.
https://www.seedwing.io
Apache License 2.0
12 stars 15 forks source link

Add support for verifying in-toto attestations #211

Closed danbev closed 1 year ago

danbev commented 1 year ago

Currently we have support for verifying in-toto envelopes using intoto::verify-envelope. This issue suggests adding a function named something like intoto::verify-attestation, similar to cosign_verify-blob-attestation.

The use case here is that we want to verify an attestation, and the example here will be the sigstore-js npm package (but this will hopefully be the same for all npm packages that use npm publish provenance).

We can start by accessing the attestation url:

$ curl -s https://registry.npmjs.org/sigstore/1.3.0  | jq -r '.dist.attestations.url'
https://registry.npmjs.org/-/npm/v1/attestations/sigstore@1.3.0

With that url we can find the DSSE envelope using:

$ curl -s https://registry.npmjs.org/-/npm/v1/attestations/sigstore@1.3.0 | jq '.attestations[0].bundle.dsseEnvelope'
{
  "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtL3NpZ3N0b3JlQDEuMy4wIiwiZGlnZXN0Ijp7InNoYTUxMiI6Ijc2MTc2ZmZhMzM4MDhiNTQ2MDJjN2MzNWRlNWM2ZTlhNGRlYjk2MDY2ZGJhNjUzM2Y1MGFjMjM0ZjRmMWY0YzZiMzUyNzUxNWRjMTdjMDZmYmUyODYwMDMwZjQxMGVlZTY5ZWEyMDA3OWJkM2EyYzZmM2RjZjNiMzI5YjEwNzUxIn19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MC4yIiwicHJlZGljYXRlIjp7ImJ1aWxkVHlwZSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vY2xpL2doYS92MiIsImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL2dpdGh1Yi5jb20vYWN0aW9ucy9ydW5uZXIifSwiaW52b2NhdGlvbiI6eyJjb25maWdTb3VyY2UiOnsidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qc0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsic2hhMSI6ImRhZThiZDhlYjQzM2E0MTQ3YjQ2NTVjMDBmZTczZTBmMjJiYzBmYjEifSwiZW50cnlQb2ludCI6Ii5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sIn0sInBhcmFtZXRlcnMiOnt9LCJlbnZpcm9ubWVudCI6eyJHSVRIVUJfRVZFTlRfTkFNRSI6InB1c2giLCJHSVRIVUJfUkVGIjoicmVmcy9oZWFkcy9tYWluIiwiR0lUSFVCX1JFUE9TSVRPUlkiOiJzaWdzdG9yZS9zaWdzdG9yZS1qcyIsIkdJVEhVQl9SRVBPU0lUT1JZX0lEIjoiNDk1NTc0NTU1IiwiR0lUSFVCX1JFUE9TSVRPUllfT1dORVJfSUQiOiI3MTA5NjM1MyIsIkdJVEhVQl9SVU5fQVRURU1QVCI6IjEiLCJHSVRIVUJfUlVOX0lEIjoiNDczNTM4NDI2NSIsIkdJVEhVQl9TSEEiOiJkYWU4YmQ4ZWI0MzNhNDE0N2I0NjU1YzAwZmU3M2UwZjIyYmMwZmIxIiwiR0lUSFVCX1dPUktGTE9XX1JFRiI6InNpZ3N0b3JlL3NpZ3N0b3JlLWpzLy5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJlZnMvaGVhZHMvbWFpbiIsIkdJVEhVQl9XT1JLRkxPV19TSEEiOiJkYWU4YmQ4ZWI0MzNhNDE0N2I0NjU1YzAwZmU3M2UwZjIyYmMwZmIxIn19LCJtZXRhZGF0YSI6eyJidWlsZEludm9jYXRpb25JZCI6IjQ3MzUzODQyNjUtMSIsImNvbXBsZXRlbmVzcyI6eyJwYXJhbWV0ZXJzIjpmYWxzZSwiZW52aXJvbm1lbnQiOmZhbHNlLCJtYXRlcmlhbHMiOmZhbHNlfSwicmVwcm9kdWNpYmxlIjpmYWxzZX0sIm1hdGVyaWFscyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qc0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsic2hhMSI6ImRhZThiZDhlYjQzM2E0MTQ3YjQ2NTVjMDBmZTczZTBmMjJiYzBmYjEifX1dfX0=",
  "payloadType": "application/vnd.in-toto+json",
  "signatures": [
    {
      "sig": "MEQCIAYR4pbfGEzpbBjJc9m8/VeE7qudH9f9MqgtnyiOUxMVAiBSvgyuJpGNN1FpXQB7JbEv0JgqMwgVSuAI2XbDWQAmfA==",
      "keyid": ""
    }
  ]
}

TODO: ~double check the spec regarding the requirement of cert/public_key/keyid~. They are all optional.

We can inspect the payload using:

$ curl -s https://registry.npmjs.org/-/npm/v1/attestations/sigstore@1.3.0 | jq '.attestations[0].bundle.dsseEnvelope'| jq -r '.payload' | base64 -d | jq
{
  "_type": "https://in-toto.io/Statement/v0.1",
  "subject": [
    {
      "name": "pkg:npm/sigstore@1.3.0",
      "digest": {
        "sha512": "76176ffa33808b54602c7c35de5c6e9a4deb96066dba6533f50ac234f4f1f4c6b3527515dc17c06fbe2860030f410eee69ea20079bd3a2c6f3dcf3b329b10751"
      }
    }
  ],
  "predicateType": "https://slsa.dev/provenance/v0.2",
  "predicate": {
    "buildType": "https://github.com/npm/cli/gha/v2",
    "builder": {
      "id": "https://github.com/actions/runner"
    },
    "invocation": {
      "configSource": {
        "uri": "git+https://github.com/sigstore/sigstore-js@refs/heads/main",
        "digest": {
          "sha1": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1"
        },
        "entryPoint": ".github/workflows/release.yml"
      },
      "parameters": {},
      "environment": {
        "GITHUB_EVENT_NAME": "push",
        "GITHUB_REF": "refs/heads/main",
        "GITHUB_REPOSITORY": "sigstore/sigstore-js",
        "GITHUB_REPOSITORY_ID": "495574555",
        "GITHUB_REPOSITORY_OWNER_ID": "71096353",
        "GITHUB_RUN_ATTEMPT": "1",
        "GITHUB_RUN_ID": "4735384265",
        "GITHUB_SHA": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1",
        "GITHUB_WORKFLOW_REF": "sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main",
        "GITHUB_WORKFLOW_SHA": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1"
      }
    },
    "metadata": {
      "buildInvocationId": "4735384265-1",
      "completeness": {
        "parameters": false,
        "environment": false,
        "materials": false
      },
      "reproducible": false
    },
    "materials": [
      {
        "uri": "git+https://github.com/sigstore/sigstore-js@refs/heads/main",
        "digest": {
          "sha1": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1"
        }
      }
    ]
  }
}

With this envelope we can then use it in a seedwing policy and use intoto::verify-envelope to verify it. For example:

Pattern:

pattern blob = *data::from<"sigstore-1.3.0.tgz">

pattern attesters = [                                               
    {name: "dan", public_key: "-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwEOO0UfhGUq2rXxy7jLTHY5VQXgN
N5DmXXONKmoskPBECLY3l25HnymyzNpgMZyOnFJDvcDbi5+HjL5Yto6gKQ==
-----END PUBLIC KEY-----"}                                                      
]

pattern test-pattern = intoto::verify-envelope<attesters, blob>

The blob in this case can be downloaded using the following command:

$ wget `curl -s https://registry.npmjs.org/sigstore/1.3.0 | jq '.dist.tarball' | tr -d '"'`

And the file sigstore-1.3.0.tgz needs to be in a one of the data directories specified to the policy server:

$ cargo r --bin swio -- serve -d path/to/downloaded_file/

This will be used to check the subject.

Input:

{
  "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInN1YmplY3QiOlt7Im5hbWUiOiJwa2c6bnBtL3NpZ3N0b3JlQDEuMy4wIiwiZGlnZXN0Ijp7InNoYTUxMiI6Ijc2MTc2ZmZhMzM4MDhiNTQ2MDJjN2MzNWRlNWM2ZTlhNGRlYjk2MDY2ZGJhNjUzM2Y1MGFjMjM0ZjRmMWY0YzZiMzUyNzUxNWRjMTdjMDZmYmUyODYwMDMwZjQxMGVlZTY5ZWEyMDA3OWJkM2EyYzZmM2RjZjNiMzI5YjEwNzUxIn19XSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MC4yIiwicHJlZGljYXRlIjp7ImJ1aWxkVHlwZSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vY2xpL2doYS92MiIsImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL2dpdGh1Yi5jb20vYWN0aW9ucy9ydW5uZXIifSwiaW52b2NhdGlvbiI6eyJjb25maWdTb3VyY2UiOnsidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qc0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsic2hhMSI6ImRhZThiZDhlYjQzM2E0MTQ3YjQ2NTVjMDBmZTczZTBmMjJiYzBmYjEifSwiZW50cnlQb2ludCI6Ii5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sIn0sInBhcmFtZXRlcnMiOnt9LCJlbnZpcm9ubWVudCI6eyJHSVRIVUJfRVZFTlRfTkFNRSI6InB1c2giLCJHSVRIVUJfUkVGIjoicmVmcy9oZWFkcy9tYWluIiwiR0lUSFVCX1JFUE9TSVRPUlkiOiJzaWdzdG9yZS9zaWdzdG9yZS1qcyIsIkdJVEhVQl9SRVBPU0lUT1JZX0lEIjoiNDk1NTc0NTU1IiwiR0lUSFVCX1JFUE9TSVRPUllfT1dORVJfSUQiOiI3MTA5NjM1MyIsIkdJVEhVQl9SVU5fQVRURU1QVCI6IjEiLCJHSVRIVUJfUlVOX0lEIjoiNDczNTM4NDI2NSIsIkdJVEhVQl9TSEEiOiJkYWU4YmQ4ZWI0MzNhNDE0N2I0NjU1YzAwZmU3M2UwZjIyYmMwZmIxIiwiR0lUSFVCX1dPUktGTE9XX1JFRiI6InNpZ3N0b3JlL3NpZ3N0b3JlLWpzLy5naXRodWIvd29ya2Zsb3dzL3JlbGVhc2UueW1sQHJlZnMvaGVhZHMvbWFpbiIsIkdJVEhVQl9XT1JLRkxPV19TSEEiOiJkYWU4YmQ4ZWI0MzNhNDE0N2I0NjU1YzAwZmU3M2UwZjIyYmMwZmIxIn19LCJtZXRhZGF0YSI6eyJidWlsZEludm9jYXRpb25JZCI6IjQ3MzUzODQyNjUtMSIsImNvbXBsZXRlbmVzcyI6eyJwYXJhbWV0ZXJzIjpmYWxzZSwiZW52aXJvbm1lbnQiOmZhbHNlLCJtYXRlcmlhbHMiOmZhbHNlfSwicmVwcm9kdWNpYmxlIjpmYWxzZX0sIm1hdGVyaWFscyI6W3sidXJpIjoiZ2l0K2h0dHBzOi8vZ2l0aHViLmNvbS9zaWdzdG9yZS9zaWdzdG9yZS1qc0ByZWZzL2hlYWRzL21haW4iLCJkaWdlc3QiOnsic2hhMSI6ImRhZThiZDhlYjQzM2E0MTQ3YjQ2NTVjMDBmZTczZTBmMjJiYzBmYjEifX1dfX0=",
  "payloadType": "application/vnd.in-toto+json",
  "signatures": [
    {
      "sig": "MEQCIAYR4pbfGEzpbBjJc9m8/VeE7qudH9f9MqgtnyiOUxMVAiBSvgyuJpGNN1FpXQB7JbEv0JgqMwgVSuAI2XbDWQAmfA==",
      "keyid": ""
    }
  ]
}

Output:

[
  {
    "predicate_type": "https://slsa.dev/provenance/v0.2",
    "predicate": {
      "buildType": "https://github.com/npm/cli/gha/v2",
      "builder": {
        "id": "https://github.com/actions/runner"
      },
      "invocation": {
        "configSource": {
          "uri": "git+https://github.com/sigstore/sigstore-js@refs/heads/main",
          "digest": {
            "sha1": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1"
          },
          "entryPoint": ".github/workflows/release.yml"
        },
        "parameters": {},
        "environment": {
          "GITHUB_EVENT_NAME": "push",
          "GITHUB_REF": "refs/heads/main",
          "GITHUB_REPOSITORY": "sigstore/sigstore-js",
          "GITHUB_REPOSITORY_ID": "495574555",
          "GITHUB_REPOSITORY_OWNER_ID": "71096353",
          "GITHUB_RUN_ATTEMPT": "1",
          "GITHUB_RUN_ID": "4735384265",
          "GITHUB_SHA": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1",
          "GITHUB_WORKFLOW_REF": "sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main",
          "GITHUB_WORKFLOW_SHA": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1"
        }
      },
      "metadata": {
        "buildInvocationId": "4735384265-1",
        "completeness": {
          "parameters": false,
          "environment": false,
          "materials": false
        },
        "reproducible": false
      },
      "materials": [
        {
          "uri": "git+https://github.com/sigstore/sigstore-js@refs/heads/main",
          "digest": {
            "sha1": "dae8bd8eb433a4147b4655c00fe73e0f22bc0fb1"
          }
        }
      ]
    },
    "attesters_names": [
      "dan"
    ],
    "artifact_names": [
      "pkg:npm/sigstore@1.3.0"
    ]
  }
]

For this we needed to add sha512 digest algorithm support as only sha256 is currently supported in the main branch.

danbev commented 1 year ago

I think having the current intoto::verify-envelope covers the attestations and that my initial motivation for creating this issue is invalid. As long as there is a way to retrieve the DSSE Envelope we can verify the contents.