Compare commits

...

258 Commits

Author SHA1 Message Date
Junjie Gao 6063ebe30f
chore: update maintainer list: Junjie Gao retired (#551)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2025-06-30 17:46:34 +08:00
dependabot[bot] f457e85917
build(deps): bump golang.org/x/mod from 0.24.0 to 0.25.0 (#550) 2025-06-20 07:15:57 +00:00
dependabot[bot] 8c17f1cfc2
build(deps): bump golang.org/x/crypto from 0.38.0 to 0.39.0 (#549) 2025-06-16 07:22:29 +00:00
dependabot[bot] 2bc67e7695
build(deps): bump oras.land/oras-go/v2 from 2.5.0 to 2.6.0 (#548) 2025-05-12 01:58:18 +00:00
dependabot[bot] 9d3ac1c22d
build(deps): bump golang.org/x/crypto from 0.37.0 to 0.38.0 (#547) 2025-05-12 01:55:26 +00:00
Jakub Jarosz e1a37eb756
chore: show openssf scorecard (#543)
Signed-off-by: Jakub Jarosz <jakub@jarosz.dev>
2025-05-09 18:01:53 +08:00
Junjie Gao 626ac1d9c0
fix: remove generate-envelope plugin support for blob signing (#546)
resolves #544

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2025-05-09 16:27:41 +08:00
Patrick Zheng de3655adc0
feat: `artifactType` support in signature manifest (#542)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-05-08 17:01:58 +08:00
dependabot[bot] 02cc632b8b
build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.10 to 3.4.11 (#539) 2025-04-28 08:58:14 +00:00
Patrick Zheng 287b8785c6
bump: bump up dependencies (#535)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-04-18 10:53:59 +08:00
Jakub Jarosz 81056bd695
fix: staticcheck complains - replace deprecated functions (#533)
This PR fixes a part of `staticcheck` complains reported in
https://github.com/notaryproject/notation-go/issues/531

The remaining issues will be addressed in a separate PRs.

Signed-off-by: Jakub Jarosz <jakub@jarosz.dev>
2025-04-16 13:31:47 +08:00
dependabot[bot] 0caf40c9ae
build(deps): bump golang.org/x/crypto from 0.36.0 to 0.37.0 (#530)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from
0.36.0 to 0.37.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="959f8f3db0"><code>959f8f3</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="769bcd6997"><code>769bcd6</code></a>
ssh: use the configured rand in kex init</li>
<li><a
href="d0a798f774"><code>d0a798f</code></a>
cryptobyte: fix typo 'octects' into 'octets' for asn1.go</li>
<li><a
href="acbcbef23f"><code>acbcbef</code></a>
acme: remove unnecessary []byte conversion</li>
<li><a
href="376eb14006"><code>376eb14</code></a>
x509roots: support constrained roots</li>
<li><a
href="b369b723c8"><code>b369b72</code></a>
crypto/internal/poly1305: implement function update in assembly on
loong64</li>
<li><a
href="6b853fbea3"><code>6b853fb</code></a>
ssh/knownhosts: check more than one key</li>
<li>See full diff in <a
href="https://github.com/golang/crypto/compare/v0.36.0...v0.37.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=golang.org/x/crypto&package-manager=go_modules&previous-version=0.36.0&new-version=0.37.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 09:57:16 +08:00
Jakub Jarosz dbbeca1679
refactor: update error names to align with Go convention (#529)
This PR fixes https://github.com/notaryproject/notation-go/issues/528
and adds the following changes:

- add new custom errors (naming aligned with [Go
convention](https://go.dev/doc/effective_go#errors))
- mark old custom error deprecated

---------

Signed-off-by: Jakub Jarosz <jakub@jarosz.dev>
2025-04-11 15:24:06 +08:00
dependabot[bot] 3bd0ac92b2
build(deps): bump github.com/opencontainers/image-spec from 1.1.0 to 1.1.1 (#523) 2025-03-25 09:39:58 +00:00
dependabot[bot] 02ff112615
build(deps): bump golang.org/x/crypto from 0.35.0 to 0.36.0 (#525) 2025-03-25 09:36:18 +00:00
dependabot[bot] d6e291d617
build(deps): bump golang.org/x/mod from 0.23.0 to 0.24.0 (#524) 2025-03-25 09:12:14 +00:00
dependabot[bot] fd2a749d85
build(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.1 to 4.5.2 (#526) 2025-03-25 09:09:36 +00:00
Patrick Zheng fcc1ce32f8
fix: update error message for signing key config (#527)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-03-24 14:25:55 +08:00
Junjie Gao fdcf9cc476
feat: add `SignOCI` function to return both artifact and signature manifest descriptor (#522)
Feat:
- added `SignOCI` function and **deprecated** the `Sign` function to
return both artifact and signature manifest descriptor.

Fix:
- handled referrer index deletion error as a warning to avoid confusion
about the final signing status.

Resolve part of https://github.com/notaryproject/notation/issues/695

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2025-03-10 14:03:48 +08:00
dependabot[bot] 4b058c99ef
build(deps): bump golang.org/x/crypto from 0.33.0 to 0.35.0 (#520) 2025-03-07 03:30:54 +00:00
Patrick Zheng bb2ee7a8a7
fix: timestamp against signing time (#518)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-02-21 16:11:11 +08:00
Junjie Gao 752832c674
bump: update go v1.23 (#512)
bump:
- updated go v1.23

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2025-02-20 16:05:20 +08:00
dependabot[bot] 93b25a3e46
build(deps): bump golang.org/x/crypto from 0.32.0 to 0.33.0 (#510) 2025-02-17 02:30:16 +00:00
dependabot[bot] c2b5474983
build(deps): bump golang.org/x/mod from 0.22.0 to 0.23.0 (#509) 2025-02-14 01:08:20 +00:00
Patrick Zheng 6eb53a50d6
refactor: updated verifier constructor (#508)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-01-22 15:22:55 +08:00
Patrick Zheng 96b7133718
bump: bump up dependencies (#504)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-01-15 14:15:29 +08:00
dependabot[bot] 851cbabbc4
build(deps): bump golang.org/x/crypto from 0.31.0 to 0.32.0 (#503) 2025-01-14 00:44:47 +00:00
Patrick Zheng b40566d885
chore: clean up (#502)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-01-14 07:55:12 +08:00
Patrick Zheng 26ce0894a6
docs: fix docs (#498)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2025-01-07 08:36:20 +08:00
dependabot[bot] e5eef5e899
build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.8 to 3.4.10 (#501) 2024-12-31 00:28:04 +00:00
Junjie Gao 9fe530b5fd
fix: `check-line-endings` command of Makefile (#499)
Fix:
- update the command to search script file in `.` instead of `scripts/`
folder to avoid returning an error.

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
Co-authored-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-12-30 10:13:10 +08:00
Patrick Zheng 3eeef95a40
chore(verifier): improve log (#497)
This PR improves log readability of the library.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-12-29 00:56:17 -08:00
Junjie Gao cefd007065
fix: limit the plugin output size (#484)
Fix:
- set the plugin output limit for STDOUT and STDERR to be 10MiB

Test:
- when the plugin output size exceeds 64MiB, the output pipe breaks, and
the plugin process outputs an error to STDERR

Spec changes: https://github.com/notaryproject/specifications/pull/320
Resolves #187

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-12-13 08:19:49 +08:00
dependabot[bot] d1d64e7041
build(deps): bump golang.org/x/crypto from 0.29.0 to 0.31.0 (#491) 2024-12-13 00:15:40 +00:00
Patrick Zheng 57c5e0dadf
bump: bump up dependencies (#488)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-12-10 07:57:44 +08:00
Junjie Gao 95bac00829
perf(log): encode objects only when logged (#481)
Fix:
- replaced `.String()` with the `%v` format to avoid rendering the
string before actually logging it.

Resolves #480

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-12-02 10:03:54 +08:00
Patrick Zheng e99be1954a
fix: enable timestamping cert chain revocation check during signing (#482)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-12-02 08:30:56 +08:00
Junjie Gao 240181a5eb
fix: add warning message for non-revokable certificate (#479)
Fix:
- added warning message for non-revokable certificate

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-11-29 08:38:08 +08:00
Patrick Zheng 5a323330d0
fix: timestamping (#478)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
Co-authored-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-11-15 16:09:00 +08:00
Pritesh Bandi 7b9636e239
chore: Improve error message in case of plugin timeout (#472)
When a plugin exceeds the specified timeout or deadline for content
processing, the current error message displayed is ```signal: killed```.
This PR updates the error message to a more informative message:
```[plugin_name] [command_name] command execution timeout: signal:
killed```

---------

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-11-14 11:06:45 -08:00
dependabot[bot] 8797d86735
build(deps): bump golang.org/x/mod from 0.21.0 to 0.22.0 (#477) 2024-11-14 19:03:25 +00:00
Patrick Zheng cf70e77099
fix: crl cache log and err msg (#475)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-11-12 07:51:07 +08:00
dependabot[bot] 0191e75373
build(deps): bump golang.org/x/crypto from 0.28.0 to 0.29.0 (#476) 2024-11-11 17:44:50 +00:00
dependabot[bot] f332ed9212
build(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#474) 2024-11-11 17:42:01 +00:00
dependabot[bot] f855f25526
build(deps): bump golang.org/x/crypto from 0.27.0 to 0.28.0 (#468) 2024-11-05 04:44:48 +00:00
Patrick Zheng c6c8cc7f66
chore: add crl cache debug logs (#473)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-11-03 20:19:01 +08:00
Patrick Zheng 1dc55d0add
fix: added tsa trust store root cert validation (#471)
This PR adds tsa trust store root cert validation while getting
certificates from trust store. This is to fail fast if cert in TSA trust
store is not a root CA certificate.

Resolves #470

---------

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-11-01 10:40:07 -07:00
Shiwei Zhang e1b80e2f2e
Merge commit from fork
fix: OS error when setting CRL cache leads to denial of signature verification
2024-11-01 10:35:22 +08:00
Junjie Gao 7d11fa2346
fix: OS error when setting CRL cache leads to denial of signature verification
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-10-23 02:59:09 +00:00
Patrick Zheng 82014a953f
chore: update logs (#469)
This PR updates logs.
Resolves #430. Also should resolve issue https://github.com/notaryproject/notation/issues/1004.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-10-16 10:31:10 -07:00
dependabot[bot] 11866a54a0
build(deps): bump github.com/notaryproject/notation-core-go from 1.1.1-0.20240920045731-0786f51de737 to 1.2.0-rc.1 (#466) 2024-10-01 05:51:37 +00:00
dependabot[bot] 4c2d986035
build(deps): bump github.com/veraison/go-cose from 1.1.0 to 1.3.0 (#467) 2024-10-01 05:48:38 +00:00
AdamKorcz a86f8da6ea
test: add fuzz test (#459)
Adds a fuzz test from cncf-fuzzing:
https://github.com/cncf/cncf-fuzzing/blob/main/projects/notary/fuzz_pkix_test.go

Signed-off-by: Adam Korczynski <adam@adalogics.com>
2024-09-29 14:11:20 -07:00
Patrick Zheng 84c2ec0762
feat: crl cache (#462)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-09-26 09:57:24 +08:00
dependabot[bot] 9faa6e2747
build(deps): bump golang.org/x/crypto from 0.26.0 to 0.27.0 (#455) 2024-09-20 04:58:03 +00:00
Junjie Gao 7c20eba012
feat: crl support (#458)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-09-19 07:53:58 +08:00
Patrick Zheng 694e3a0314
bump: update golang version to v1.22.0 (#457)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-09-10 13:37:32 +08:00
JasonTheDeveloper 4d76f9a415
fix: dir no longer panics when HOME and XDG_CONFIG_HOME are not set (#449)
This PR addresses the issue #446

In this PR I:

- I removed the `init()` function from `dir/path`
- When `userConfigDir()` returns an error, instead of `panic(err)` I
default to the current directory instead
- Split `loadUserPath()` into two new functions used to setup and return
the values for `UserConfigDir` and `UserLibexecDir`
- Added additional unit tests for the two new functions and to test the
default directory is used when `HOME` is set to `""`

---------

Signed-off-by: Jason <jagoodse@microsoft.com>
Signed-off-by: JasonTheDeveloper <jagoodse@microsoft.com>
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
Co-authored-by: Shiwei Zhang <shizh@microsoft.com>
Co-authored-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-09-04 15:05:30 +08:00
Patrick Zheng 115509e289
bump: bump up notation-core-go and signingAgent (#444)
This PR targets on main branch.

---------

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-09-04 08:59:43 +08:00
Patrick Zheng 4403efa431
docs: create release checklist on main (#443)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-09-04 08:47:35 +08:00
Patrick Zheng 974c2916fa
bump: upgrade golang to v1.22 (#440)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-08-27 09:59:07 +08:00
dependabot[bot] 8e2131d192
build(deps): bump golang.org/x/crypto from 0.25.0 to 0.26.0 (#434) 2024-08-19 03:22:44 +00:00
dependabot[bot] aaadf0b342
build(deps): bump golang.org/x/mod from 0.19.0 to 0.20.0 (#433) 2024-08-18 17:23:22 +00:00
Patrick Zheng 3c5a659c1d
refactor!: update revocation based on notation-core-go (#429)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-08-14 08:46:21 +08:00
Junjie Gao c3b2f51140
feat: support distinguished name state(S) (#432)
Resolves #431 
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-08-07 10:07:09 +08:00
Junjie Gao 09e32d7940
fix: improve error message for plugin (#406)
Fix:
- Simplified the error message for plugin errors.
- Logged the details of the failing plugin command instead of printing
them out in the CLI output, as users may not understand what the plugin
command is, which can mislead them about the important part of the error
message.
- Logged the error code instead of printing it out in the CLI output to
simplify the error message.

Previous example:
```
notation sign xxx.azurecr.io/hello-app:v1 --plugin azure-kv --id https://xxx.vault.azure.net/certificates --plugin-config credential_type=azurecli 
Warning: Always sign the artifact using digest(@sha256:...) rather than a tag(:v1) because tags are mutable and a tag reference can point to a different artifact than the one signed.
Error: failed to execute the describe-key command for azure-kv: VALIDATION_ERROR: Invalid input passed to "--id". Please follow this format to input the ID "https://<vault-name>.vault.azure.net/certificates/<certificate-name>/[certificate-version]"
```

Current example:
```
notation sign xxx.azurecr.io/hello-app:v1 --plugin azure-kv --id https://xxx.vault.azure.net/certificates --plugin-config credential_type=azurecli 
Warning: Always sign the artifact using digest(@sha256:...) rather than a tag(:v1) because tags are mutable and a tag reference can point to a different artifact than the one signed.
Error: failed to sign with the plugin azure-kv: Invalid input passed to "--id". Please follow this format to input the ID "https://<vault-name>.vault.azure.net/certificates/<certificate-name>/[certificate-version]"
```
Resolves #404 
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
Co-authored-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-07-30 09:34:01 +08:00
Patrick Zheng b3b8cbe0cc
bump: bump up notation-core-go (#426)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-07-23 08:13:10 +08:00
Patrick Zheng 8340920475
fix: fix usage of SignerInfo.AuthenticSigningTime (#424)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-07-19 12:57:53 +08:00
Patrick Zheng 8ada12a746
chore: fix logs (#417)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-07-17 12:44:22 +08:00
Patrick Zheng b52583166f
feat: Timestamp (#418)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-07-15 12:40:11 +08:00
dependabot[bot] 1c3e3783d5
build(deps): bump golang.org/x/crypto from 0.24.0 to 0.25.0 (#420) 2024-07-12 07:26:11 +00:00
dependabot[bot] c2cd70f095
build(deps): bump golang.org/x/mod from 0.18.0 to 0.19.0 (#421) 2024-07-11 15:34:44 +00:00
Junjie Gao 54b73d8a69
fix(ci): pass CODECOV_TOKEN to reusable-build.yml (#419)
Depends on https://github.com/notaryproject/notation-core-go/pull/209

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-07-09 09:05:24 +08:00
Pritesh Bandi a1825db13d
feat: Implements VerifyBlob functionality (#394)
This PR implements VerifyBlob functionality as per
https://github.com/notaryproject/specifications/blob/main/specs/trust-store-trust-policy.md
---------

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-06-27 00:10:28 -07:00
dependabot[bot] a6b0a8c2e5
build(deps): bump golang.org/x/crypto from 0.23.0 to 0.24.0 (#414) 2024-06-12 20:05:29 +00:00
dependabot[bot] aba7ba74b2
build(deps): bump golang.org/x/mod from 0.17.0 to 0.18.0 (#413) 2024-06-10 18:23:07 +00:00
Junjie Gao 1a5b3e354f
ci: enable ci for release branch (#409)
To enable a safe way to create a release branch, we need to enable CI for the release branch as proposed in #408

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-05-30 08:16:17 -07:00
Junjie Gao 254dfcde66
bump: bump up notation-core-go v1.0.3 (#407)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-05-29 14:00:44 +08:00
Junjie Gao b7fde5134d
fix: error message for dangling reference index (#402) 2024-05-29 11:51:05 +08:00
Junjie Gao b8508d04e9
test: improve test coverage to 80% (#405)
Test:
- improved test coverage to over 80.16%

Fix:
- added ORAS copy library to copy the OCI Layout test data to a
temporary directory and avoid generating test time temporary files in
the repository directory.

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-05-28 10:33:03 -07:00
dependabot[bot] 5e98995bd1
build(deps): bump golang.org/x/crypto from 0.22.0 to 0.23.0 (#403) 2024-05-22 16:54:35 +00:00
dependabot[bot] 378ee8371c
build(deps): bump golang.org/x/crypto from 0.21.0 to 0.22.0 (#396) 2024-04-27 22:52:28 +00:00
dependabot[bot] a901939275
build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.7 to 3.4.8 (#399) 2024-04-27 22:50:09 +00:00
dependabot[bot] 97a5a86d56
build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.6 to 3.4.7 (#395) 2024-04-18 16:52:40 +00:00
dependabot[bot] 442ece7b0d
build(deps): bump golang.org/x/mod from 0.16.0 to 0.17.0 (#397) 2024-04-18 16:50:46 +00:00
Toddy Mladenov 2d65f6e3cb
Moved org maintainers to emeritus (#393)
Addressing https://github.com/notaryproject/.github/issues/66 and
https://github.com/notaryproject/.github/issues/68 also addressed
https://github.com/notaryproject/.github/issues/67 and
https://github.com/notaryproject/.github/issues/69

---------

Signed-off-by: Toddy Mladenov <toddysm@gmail.com>
2024-04-04 10:46:21 -07:00
dependabot[bot] fbf15e6c8c
build(deps): bump actions/stale from 8 to 9 (#391) 2024-04-02 04:23:42 +00:00
Patrick Zheng 57ff8e68a0
bump: bump golang and dependency versions (#392)
bumping up oras-go to v2.5.0 along with golang version to v1.21.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-03-27 09:52:54 -07:00
Pritesh Bandi b8136e2c80
fix: Add contract version to plugin sign request and plugin verify request (#390)
Add contract version to plugin sign request and plugin verify request.
As per [specification](https://github.com/notaryproject/specifications/blob/main/specs/plugin-extensibility.md) `contractVersion` is a mandatory field.

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-03-21 12:40:21 -07:00
dependabot[bot] e686d8b995
build(deps): bump golang.org/x/mod from 0.15.0 to 0.16.0 (#388) 2024-03-21 17:04:45 +00:00
dependabot[bot] 85df759836
build(deps): bump golang.org/x/crypto from 0.20.0 to 0.21.0 (#389) 2024-03-18 17:55:42 +00:00
Yi Zha d9a44b5901
chore: add GitHub action for stale issues and PRs (#365)
This PR enables the `notation-go` repo to run stale action at 1:30 every
day to label or close stale PRs and issues. See guideline
https://github.com/marketplace/actions/close-stale-issues

This is the definition for stale PRs or issues that we discussed during
community call and to be updated in [contributing
PR](https://github.com/notaryproject/.github/pull/25).

"A stale issue is one that remains inactive or without updates for a
period of 60 days. A stale pull request (PR) is one that remains
inactive or without updates for a period of 45 days. When an issue or PR
becomes stale, it is labelled as `stale`. Normally maintainers will
comment on stale issues or PRs to prompt participants to take action. If
there is no activity for additional 30 days, this issue or PR will be
closed. If an update/comment occur on stale issues or pull requests, the
stale label will be removed, and the timer will restart"

Signed-off-by: Yi Zha <yizha1@microsoft.com>
2024-03-18 10:52:39 -07:00
Pritesh Bandi ec42378613
feat: add support for signing blob (#379)
This PR adds support for signing blobs.

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-03-14 11:16:03 -07:00
dependabot[bot] 7fa8404e79
build(deps): bump golang.org/x/crypto from 0.19.0 to 0.20.0 (#387) 2024-03-06 17:49:01 +00:00
Pritesh Bandi 2efb4a76bb
chore: updated/added deprecation message (#382)
* updated/added deprecation message.
* Also removed usage of deprecated types in `algorithms.go` file

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-03-06 09:45:49 -08:00
dependabot[bot] a86ca0d20c
build(deps): bump golang.org/x/mod from 0.14.0 to 0.15.0 (#384) 2024-02-29 06:51:52 +00:00
dependabot[bot] 345951e59c
build(deps): bump github.com/opencontainers/image-spec from 1.1.0-rc6 to 1.1.0 (#385) 2024-02-29 06:45:36 +00:00
dependabot[bot] 4f3cb65cc0
build(deps): bump golang.org/x/crypto from 0.18.0 to 0.19.0 (#383) 2024-02-29 06:43:37 +00:00
Pritesh Bandi 9ff189134f
chore: start using plugin-framework package (#372)
**Note:** we introduced a small breaking change; the type for
`GenerateSignatureResponse#SigningAlgorithm` has been changed from
`string` to `plugin.SignatureAlgorithm`

---------

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2024-02-01 11:30:33 -08:00
Patrick Zheng 4606472ebd
bump: bump up oras-go and image-spec (#381)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-02-01 15:39:33 +08:00
Junjie Gao 1752918878
fix: update error message (#380)
Previous:
```
Error: describe-key command failed: failed to execute the describe-key command for plugin azure-kv: CertificateNotFound: A certificate with (name/id) self-signed-pkcs13/versions/70747b2064c0488e936eba7a29acc4c6 was not found in this key vault. If you recently deleted this certificate you may be able to recover it using the correct recovery command. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125182
```

Current:
```
Error: failed to execute the describe-key command for plugin azure-kv: CertificateNotFound: A certificate with (name/id) self-signed-pkcs13/versions/70747b2064c0488e936eba7a29acc4c6 was not found in this key vault. If you recently deleted this certificate you may be able to recover it using the correct recovery command. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125182
```

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-01-29 09:01:16 -08:00
Patrick Zheng b7cd8a01fc
bump: bump up notation-core-go (#377)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-01-24 10:55:58 +08:00
Junjie Gao 690448ee67
fix: update PluginExecutableFileError type (#375)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-01-18 21:19:49 +08:00
Junjie Gao d52ca7162a
fix: improve plugin error message (#371)
- added `PluginUnknownError`, `PluginValidityError`,
`PluginDirectoryError`. For each error types, Notation CLI should
provide a recommanded suggestion to solve them.
- improved error message

Resolve part of: https://github.com/notaryproject/notation/issues/824
Resolve part of: https://github.com/notaryproject/notation/issues/867

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2024-01-18 09:24:19 +08:00
dependabot[bot] 66da1e313c
build(deps): bump golang.org/x/crypto from 0.17.0 to 0.18.0 (#373) 2024-01-16 05:16:42 +00:00
Toddy Mladenov e56ee18161
Updated CODEOWNERS and MAINTAINERS files (#370)
Addressing https://github.com/notaryproject/.github/issues/55
https://github.com/notaryproject/.github/issues/56 and
https://github.com/notaryproject/.github/issues/57

Signed-off-by: Toddy Mladenov <toddysm@gmail.com>
2024-01-14 14:05:42 -08:00
Patrick Zheng b315de42f9
feat: plugin install iteration 2 (#369)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2024-01-12 10:17:49 +08:00
dependabot[bot] 706eab815e
build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#367) 2023-12-26 05:35:10 +00:00
Patrick Zheng 85a5bb9826
feat: add install method to plugin CLIManager (#364)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-12-18 21:23:18 +08:00
dependabot[bot] 966c6b7e42
build(deps): bump golang.org/x/crypto from 0.15.0 to 0.16.0 (#366) 2023-12-08 13:38:38 +00:00
Feynman Zhou e8c8d224b2
docs: update README to align with the new project brand (#343)
Signed-off-by: Feynman Zhou <feynmanzhou@microsoft.com>
Signed-off-by: Feynman Zhou <feynman@kubesphere.io>
Co-authored-by: Pritesh Bandi <priteshbandi@gmail.com>
2023-11-27 15:42:13 +08:00
Patrick Zheng 5de0d58b21
feat: add uninstall to CLIManager (#363)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-11-23 11:15:46 +08:00
dependabot[bot] ce0c457700
build(deps): bump golang.org/x/crypto from 0.14.0 to 0.15.0 (#362) 2023-11-20 22:30:13 +00:00
dependabot[bot] 1bc5a3f8c4
build(deps): bump golang.org/x/mod from 0.13.0 to 0.14.0 (#361) 2023-11-07 04:55:49 +00:00
Patrick Zheng 765d02b5be
fix: update error message from notation-go (#345)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-10-28 08:57:34 +08:00
dependabot[bot] cb6f009f97
build(deps): bump github.com/notaryproject/notation-core-go from 1.0.0 to 1.0.1 (#358) 2023-10-27 21:30:35 +00:00
dependabot[bot] 36a0831e46
build(deps): bump oras.land/oras-go/v2 from 2.3.0 to 2.3.1 (#359) 2023-10-27 21:27:47 +00:00
dependabot[bot] effa7cb950
build(deps): bump golang.org/x/crypto from 0.13.0 to 0.14.0 (#355) 2023-10-27 07:24:02 +00:00
dependabot[bot] 18b3c680b4
build(deps): bump github.com/opencontainers/image-spec from 1.1.0-rc4 to 1.1.0-rc5 (#352) 2023-10-10 18:38:05 +00:00
dependabot[bot] f89ec21bc8
build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.5 to 3.4.6 (#351) 2023-10-10 18:34:58 +00:00
dependabot[bot] 0ff7d26fb9
build(deps): bump golang.org/x/mod from 0.12.0 to 0.13.0 (#356) 2023-10-10 18:23:00 +00:00
Junjie Gao 60d9cdcc59
bump: update oras-go to v2.3.0 (#347)
- Update oras-go to v2.3.0.
- Replace oras.Pack() with oras.PackManifest() as it is deprecated in
v2.3.0.
- Generate an empty config blob manually, as oras.PackManifest() does
not generate the config blob with the notation artifact type as the
media type.

Resolves #346 
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>

---------

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2023-09-15 09:20:01 +08:00
dependabot[bot] c6bc5e0d38
build(deps): bump golang.org/x/crypto from 0.12.0 to 0.13.0 (#350) 2023-09-14 22:13:59 +00:00
Patrick Zheng b684acb231
chore: update go version to 1.20 (#349)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-09-14 15:08:39 -07:00
dependabot[bot] f1706c2868
build(deps): bump golang.org/x/crypto from 0.11.0 to 0.12.0 (#344) 2023-08-22 06:20:43 +00:00
Shiwei Zhang 553b866ed4
bump: upgrade notation-core-go to v1.0.0 (#342)
bump up `notation-core-go` from `v1.0.0-rc.4` to `v1.0.0`.

Signed-off-by: Shiwei Zhang <shizh@microsoft.com>
2023-07-25 08:12:38 +08:00
Sajay Antony f2cdfee211
errors: add error for wild card scope validation (#340)
Signed-off-by: Sajay Antony <sajaya@microsoft.com>
2023-07-19 15:06:39 -07:00
Patrick Zheng 99bc2bc420
fix: quick fix to use correct sign/verify plugin (#338)
Changes in this PR:
1. `signer.NewFromPlugin(plugin plugin.SignPlugin, ...)`
2. `verifer.executePlugin(ctx context.Context, installedPlugin
plugin.VerifyPlugin, ...)`

---------

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-07-19 15:23:36 +08:00
Patrick Zheng 3981f69fb5
chore: add license header to files and github action workflow to check license (#334) 2023-07-18 10:12:41 +08:00
dependabot[bot] 052e405b51
build(deps): bump golang.org/x/crypto from 0.10.0 to 0.11.0 (#331) 2023-07-18 01:03:57 +00:00
dependabot[bot] abaaa0bc7c
build(deps): bump golang.org/x/mod from 0.11.0 to 0.12.0 (#333) 2023-07-17 15:03:01 +00:00
dependabot[bot] 757d01c8bb
build(deps): bump oras.land/oras-go/v2 from 2.2.0 to 2.2.1 (#332)
Bumps [oras.land/oras-go/v2](https://github.com/oras-project/oras-go)
from 2.2.0 to 2.2.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/oras-project/oras-go/releases">oras.land/oras-go/v2's
releases</a>.</em></p>
<blockquote>
<h2>v2.2.1</h2>
<h2>Bug Fixes</h2>
<ul>
<li>Fix the bug where the &quot;blobs&quot; directory was not being
ensured during the initialization of <a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.2.0/content/oci#Store"><code>oci.Store</code></a>
(<a
href="https://redirect.github.com/oras-project/oras-go/issues/520">#520</a>)</li>
</ul>
<h2>Other Changes</h2>
<ul>
<li>Improve error messages</li>
<li>Upgrade <code>image-spec</code> to <a
href="https://github.com/opencontainers/image-spec/releases/tag/v1.1.0-rc4"><code>v1.1.0-rc4</code></a>
to mitigate the missing annotation keys issue</li>
</ul>
<blockquote>
<p><strong>Note</strong>: <code>oras-go</code> remains on
<code>distribution-spec</code> <a
href="https://github.com/opencontainers/distribution-spec/releases/tag/v1.1.0-rc1">v1.1.0-rc1</a>
and has not implemented <a
href="https://github.com/opencontainers/distribution-spec/releases/tag/v1.1.0-rc2">v1.1.0-rc2</a>
yet.</p>
</blockquote>
<h2>Detailed Commits</h2>
<ul>
<li>build(deps): bump golang.org/x/sync from 0.2.0 to 0.3.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/oras-project/oras-go/pull/524">oras-project/oras-go#524</a></li>
<li>fix(oci): create blobs dir and define blobs dir name constant by <a
href="https://github.com/sparr"><code>@​sparr</code></a> in <a
href="https://redirect.github.com/oras-project/oras-go/pull/520">oras-project/oras-go#520</a></li>
<li>fix: improve errors for <code>ReadAll</code> and
<code>loadIndexfile</code> by <a
href="https://github.com/sparr"><code>@​sparr</code></a> in <a
href="https://redirect.github.com/oras-project/oras-go/pull/526">oras-project/oras-go#526</a></li>
<li>refactor: upgrade go mod to <code>image-spec v1.1.0-rc4</code> and
fix the missing types by <a
href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a> in <a
href="https://redirect.github.com/oras-project/oras-go/pull/536">oras-project/oras-go#536</a></li>
<li>chore: enable workflows for release branches by <a
href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a> in <a
href="https://redirect.github.com/oras-project/oras-go/pull/537">oras-project/oras-go#537</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/sparr"><code>@​sparr</code></a> made
their first contribution in <a
href="https://redirect.github.com/oras-project/oras-go/pull/520">oras-project/oras-go#520</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/oras-project/oras-go/compare/v2.2.0...v2.2.1">https://github.com/oras-project/oras-go/compare/v2.2.0...v2.2.1</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a1527322f4"><code>a152732</code></a>
chore: enable workflows for release branches (<a
href="https://redirect.github.com/oras-project/oras-go/issues/537">#537</a>)</li>
<li><a
href="cd01930ccd"><code>cd01930</code></a>
refactor: upgrade go mod to <code>image-spec v1.1.0-rc4</code> and fix
the missing types...</li>
<li><a
href="f3d7906854"><code>f3d7906</code></a>
fix: improve errors for <code>ReadAll</code> and
<code>loadIndexfile</code> (<a
href="https://redirect.github.com/oras-project/oras-go/issues/526">#526</a>)</li>
<li><a
href="94805254ef"><code>9480525</code></a>
fix(oci): create blobs dir and define blobs dir name constant (<a
href="https://redirect.github.com/oras-project/oras-go/issues/520">#520</a>)</li>
<li><a
href="f1c44e178f"><code>f1c44e1</code></a>
build(deps): bump golang.org/x/sync from 0.2.0 to 0.3.0 (<a
href="https://redirect.github.com/oras-project/oras-go/issues/524">#524</a>)</li>
<li>See full diff in <a
href="https://github.com/oras-project/oras-go/compare/v2.2.0...v2.2.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=oras.land/oras-go/v2&package-manager=go_modules&previous-version=2.2.0&new-version=2.2.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-12 11:46:48 -07:00
Patrick Zheng 2882bafab6
fix: quick fix typo in error msg (#328)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-06-30 22:42:36 -07:00
Pritesh Bandi 432c931622
fix: update timeout for OCSP call to 2 seconds (#327)
updated timeout for OCSP call to 2 seconds.

Updates based on updates in https://github.com/notaryproject/notaryproject/pull/249
2023-06-27 17:55:14 -07:00
Patrick Zheng 983e97dc54
fix: fixed error messages of trust policy (#326)
A quick PR to fix error messages of trust policy.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-06-26 08:57:53 -07:00
dependabot[bot] a973c8b50a
build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.4 to 3.4.5 (#321) 2023-06-26 08:58:36 +00:00
dependabot[bot] d65bba32ae
build(deps): bump golang.org/x/crypto from 0.9.0 to 0.10.0 (#324) 2023-06-26 02:13:20 +00:00
dependabot[bot] 6df5e38e05
build(deps): bump golang.org/x/mod from 0.10.0 to 0.11.0 (#325) 2023-06-26 02:09:57 +00:00
Yi Zha fa4ddc8f86
chore: add issue template (#293)
Add issue template for `notation-go`, which is copied from `notation` repo.

Signed-off-by: Yi Zha <yizha1@microsoft.com>
2023-06-14 08:21:55 -07:00
Pritesh Bandi 69cf11bbbc
feat: add validations for symlink (#316)
Added validations to make sure singingkeys.json, trustpolicy.json and config.json doesnt read's symlink

closes https://github.com/notaryproject/notation/issues/634

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2023-05-24 23:56:39 -07:00
Patrick Zheng 9b0e472c02
update: bump up dependencies (#318) 2023-05-25 14:26:17 +08:00
byronchien eba60f5aed
fix: add digest check for Sign (#317)
This PR adds digest check on Sign.

Signed-off-by: Byron Chien <byronc@ucla.edu>
Co-authored-by: Patrick Zheng <patrickzheng@microsoft.com>

---------

Signed-off-by: Byron Chien <byronc@ucla.edu>
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
Co-authored-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-05-25 14:14:13 +08:00
Patrick Zheng c3f8c33305
fix: updated error message of errExceededMaxVerificationLimit (#314)
This PR updates error message of `errExceededMaxVerificationLimit` to be more user friendly.

Signed-off-by: patrick <zongjunzheng@hotmail.com>
2023-05-23 23:23:07 -07:00
Patrick Zheng 39c8ed050a
fix: added digest check on verify (#313)
This PR adds digest check on verify.

Signed-off-by: patrick <zongjunzheng@hotmail.com>
2023-05-23 12:15:16 -07:00
Yi Zha 7de640bd5a
chore: update account info for Patrick Zheng (#310)
Resolve https://github.com/notaryproject/.github/issues/27

Signed-off-by: Yi Zha <yizha1@microsoft.com>
2023-05-23 10:13:17 +08:00
dependabot[bot] c8c1fbddec
build(deps): bump github.com/veraison/go-cose from 1.0.0 to 1.1.0 (#312) 2023-05-22 16:59:03 +00:00
Patrick Zheng 15c8ff82d5
chore: running some chores for notation-go (#311)
In this PR:
1. Resolves #287; updated `Notary v2` to `Notation`.
2. Updated error messages of trust policy to improve user experience. Related issue: https://github.com/notaryproject/notation/issues/621

Signed-off-by: patrick <zongjunzheng@hotmail.com>
2023-05-19 15:16:19 -07:00
dependabot[bot] 1b9b7f2d80
build(deps): bump golang.org/x/crypto from 0.8.0 to 0.9.0 (#309) 2023-05-16 00:26:25 +00:00
Patrick Zheng 4e790cea6b
update: removed Sign with OCI artifact manifest (#308)
This PR removes notation-go `Sign`'s support of OCI artifact manifest as
it is removed from [image-spec
v1.0.0-rc.3](https://github.com/opencontainers/image-spec/releases/tag/v1.1.0-rc.3).
In other words, Notation will only generate signatures stored as OCI
image manifest in the future.
(Note: Notation can still consume signatures already stored as OCI
artifact manifest for backwards compatibility.)

Related issue: https://github.com/notaryproject/notation/issues/660

---------

Signed-off-by: patrick <zongjunzheng@hotmail.com>
2023-05-11 14:29:25 +08:00
Shiwei Zhang efa353a55e
build: bump image-spec to v1.1.0-rc.3 and oras-go to v2.1.0 (#306)
Closes #305
Closes #307

Signed-off-by: Shiwei Zhang <shizh@microsoft.com>
2023-05-10 22:16:43 +08:00
dependabot[bot] f9c78f5340
build(deps): bump golang.org/x/crypto from 0.7.0 to 0.8.0 (#304) 2023-05-05 14:34:49 +00:00
Pritesh Bandi 06d04cc0b2
chore: Updating notation-core-go dependency to rc.3 (#302)
Updating notation-core-go dependency to rc.3

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
Co-authored-by: Pritesh Bandi <pritesb@amazon.com>
2023-04-22 08:28:21 +08:00
Kody Kimberl 3dd11cbc7e
feat: adding OCSP revocation checks to Verify (#295)
This PR adds OCSP revocation checking to the Verify function using the notation-core-go's revocation package.
This PR addresses part of the following issue: https://github.com/notaryproject/notation-core-go/issues/124. It is dependent on https://github.com/notaryproject/notation-core-go/pull/134

Signed-off-by: Kody Kimberl <kody.kimberl.work@gmail.com>
Signed-off-by: Kody Kimberl <59657721+kody-kimberl@users.noreply.github.com>
2023-04-20 09:54:52 -07:00
Patrick Zheng 67a477f0c6
fix: added truststore.ValidateCerts (#285)
This PR aims to resolve #284.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-04-19 22:22:48 -07:00
Patrick Zheng cd1a135381
feat: add local sign/verification for OCI layout directory (#288) 2023-04-19 13:01:35 +08:00
dependabot[bot] 5cf34ace71
build(deps): bump golang.org/x/mod from 0.9.0 to 0.10.0 (#299) 2023-04-18 05:33:05 +00:00
Patrick Zheng dfb41c5a7c
chore: updated to go 1.19 (#297)
This PR resolves #266.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-04-13 11:40:59 +08:00
Billy Zha 753b6b12cb
chore: update err msg for policy verification (#294)
This PR rephrased error message returned during trust policy
verification.

---------

Signed-off-by: Billy Zha <jinzha1@microsoft.com>
2023-04-07 09:57:21 +08:00
dependabot[bot] fb79e6df1a
build(deps): bump golang.org/x/mod from 0.8.0 to 0.9.0 (#283) 2023-03-22 06:31:06 +00:00
dependabot[bot] 7301607dce
build(deps): bump oras.land/oras-go/v2 from 2.0.0 to 2.0.2 (#291)
Bumps [oras.land/oras-go/v2](https://github.com/oras-project/oras-go) from 2.0.0 to 2.0.2.

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-21 13:12:58 -07:00
Pritesh Bandi 1200ba5fb4
fix: don't add user-metadata to manifest's subject annotations (#290)
When signing with metadata, notation needs adds the metadata in signature blob as descriptor's annotation but it looks like we are also added it to image-manifest's subject's descriptor, which is undesirable.
This PR removes user-metadata from manifest's subject annotations

```bash
# Notation command
notation sign %IMAGE% --user-metadata name=pmt
```
```
# Generated Image manifest

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.cncf.notary.signature",
    "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
    "size": 2
  },
  "layers": [
    {
      "mediaType": "application/jose+json",
      "digest": "sha256:73a6eda31ccc00fc2b8058b79d728429246feebf790f877a0df6b57a0fcd86ad",
      "size": 4931
    }
  ],
  "subject": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:72932c375f25b11f9cff7265d3518341c9814e2ed7aa578afaa659c4e0411de6",
    "size": 942,
    "annotations": {
      "name": "pmt"
    }
  },
  "annotations": {
    "io.cncf.notary.x509chain.thumbprint#S256": "[\"24f098767fcceb4a4b0274001b81d3993c2b54410fc1bf0080f4d17964c19281\",\"e3bb10c07cf8c4e77a38d161ebba482230bd9b4d9e150a6eadac26c8885dd094\",\"574440599c0484b6acd845a373cee98d5b8eb867d6c11c5603ef939045e88da8\",\"43a8466811e4000dbab1610461f0c83a0969844ddcf4badbebb2a028714fbf34\"]",
    "org.opencontainers.image.created": "2023-03-15T18:17:07Z"
  }
}
```

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2023-03-17 10:26:35 -07:00
Kody Kimberl 9920fb7393
update: improve missing trustpolicy error message (#282)
This PR improves the error message for a missing trustpolicy file [notaryproject/notation/#128](https://github.com/notaryproject/notation/issues/128#issuecomment-1448683454). This is the output when the trustpolicy is missing:
```
c889f3b9d811:notation kodysk$ ./bin/notation verify $IMAGE
Error: Trust policy is not present, please create trust policy at /Users/kodysk/Library/Application Support/notation/trustpolicy.json
```

Signed-off-by: Kody Kimberl kody.kimberl.work@gmail.com
2023-03-02 17:47:56 -08:00
Yi Zha 8c3ed9217d
fix: fix the CODEOWNERS format issue (#280)
Fix the format issue of CODEOWNERS file
- The owners should be listed on one line
- org-level owners are also added as sub-repo code owners

Signed-off-by: Yi Zha <yizha1@microsoft.com>

Signed-off-by: Yi Zha <yizha1@microsoft.com>
2023-02-21 17:16:53 -08:00
Toddy Mladenov d872396066
Added CODEOWNERS and MAINTAINERS files (#272)
This PR combines all the changes from the following PRs for updating the maintainers for *notaryproject/notation-go* sub-project:
[Add Junjie Gao](https://github.com/notaryproject/notation-go/pull/265) #265
[Add Milind Gokarn](https://github.com/notaryproject/notation-go/pull/259) #259
[Add Patrick Zheng](https://github.com/notaryproject/notation-go/pull/264) #264
[Add Pritesh Bandi](https://github.com/notaryproject/notation-go/pull/258) #258
[Add Rakesh Gariganti](https://github.com/notaryproject/notation-go/pull/260) #260
[Add Shiwei Zhang](https://github.com/notaryproject/notation-go/pull/263) #263
The proposal is to abandon the above PRs and merge only this one.

Signed-off-by: Toddy Mladenov <toddysm@gmail.com>
2023-02-16 10:04:57 -08:00
Pritesh Bandi de8b7ddaa5
bump: update notation-core-go dependency (#278)
Update notation-core-go dependency

Signed-off-by: Pritesh Bandi <priteshbandi@gmail.com>
2023-02-15 19:07:17 -08:00
byronchien 064887e1ee
fix: add check for unsupported subject fields (#275)
Fails trusted identity verification if the trust policy identity or the
subject of the leaf certificate contains "=#".

example logs:
```
DEBU[2023-02-10T16:40:51-08:00] Validating cert chain
DEBU[2023-02-10T16:40:51-08:00] Validating trust identity
ERRO[2023-02-10T16:40:51-08:00] authenticity validation failed. Failure reason: notation does not support x509 identities containing "=#"
WARN[2023-02-10T16:40:51-08:00] Signature sha256:075dbeb4c3b7110104c652756116797f04ec60eb4618b990f698e9692b1ebdde failed verification with error: notation does not support x509 identities containing "=#"
```

Signed-off-by: Byron Chien <byronc@ucla.edu>
2023-02-15 16:38:50 -08:00
dependabot[bot] 156ceddb01
build(deps): bump golang.org/x/mod from 0.7.0 to 0.8.0 (#277)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.7.0 to
0.8.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b71060237c"><code>b710602</code></a>
sumdb/dirhash: correct documentation of hash</li>
<li><a
href="a42224db2b"><code>a42224d</code></a>
all: replace io/ioutil with io and os package</li>
<li><a
href="77d797e6e3"><code>77d797e</code></a>
sumdb/dirhash: fix a panic when argument is not a directory</li>
<li>See full diff in <a
href="https://github.com/golang/mod/compare/v0.7.0...v0.8.0">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-15 14:55:56 -08:00
Patrick Zheng 7987c85e46
chore: clean up comments format and removed unused code (#273)
go fmt.
Cleaned up comments format.
Removed unused code in unit tests.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-02-10 14:28:08 +08:00
Pritesh Bandi 1b2ec27aea
Create config with user only permission (#269)
Create config with user only permission

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2023-02-08 11:07:38 -08:00
byronchien 6ef3544efa
feat: add support for signed user metadata (#242)
Adds support for signed user metadata in `notation sign` and `notation verify`. [Relevant spec](https://github.com/notaryproject/notation/pull/498)

example sign usage:
notation % notation sign $IMAGE --user-metadata io.wabbit-networks.buildId=123 --user-metadata io.wabbit-networks.buildTime=123
Successfully signed localhost:5000/net-monitor@sha256:5a07385af4e6b6af81b0ebfd435aedccdfa3507f0609c658209e1aba57159b2b

example verification:
```
notation % notation verify $IMAGE --user-metadata io.wabbit-networks.buildTime=123
Successfully verified signature for localhost:5000/net-monitor@sha256:5a07385af4e6b6af81b0ebfd435aedccdfa3507f0609c658209e1aba57159b2b

The artifact was signed with the following user metadata.
KEY                            VALUE
io.wabbit-networks.buildTime   123
io.wabbit-networks.buildId     123
```

Signed-off-by: Byron Chien <chienb@amazon.com>
2023-02-07 19:20:42 -08:00
Patrick Zheng 5e5cba1e9a
Update: added ErrorPushSignatureFailed (#271)
This PR adds a `ErrorPushSignatureFailed` error type so that users of notation.Sign() would be able to check error type on Signing failures. On notation CLI side, if the error is of type `ErrorPushSignatureFailed`, a suggestion of using OCI image manifest will be printed out.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-02-07 10:15:33 -08:00
Pritesh Bandi 920ded2440
fix: Appends annotations returned by plugin to signature manifest's annotations (#262)
Another way to implement https://github.com/notaryproject/notation-go/pull/253 as per suggestion
from @patrickzheng200

-------

As per
spec([link](https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#generate-envelope)) annotations returned by plugin should be appened to signature manifest's annotations and this PR introduces aforementioned behavior

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2023-02-03 10:20:31 -08:00
Pritesh Bandi d44cb5d18d
Adds more unit test for keys.go (#268)
Adds more unit test for keys.go

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2023-02-03 10:07:38 -08:00
Pritesh Bandi 510def1a3f
feat!: add signingkeys.json validation check (#246)
Change to validate signingkeys are valid(unique name) for both read and write operation.

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2023-02-02 19:19:35 -08:00
Jeswanth Galla 32d23721c0
feat: plugin version comparison functionality (#237)
Ensures that the the verification plugin version is higher than the minimum plugin version emitted from the signature

Signed-off-by: Jeswanth <jeshga@amazon.com>
Co-authored-by: Pritesh Bandi <priteshbandi@gmail.com>
2023-02-02 10:43:12 -08:00
Patrick Zheng ed6ff0c142
chore: logs and tests clean-up (#256)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-02-02 09:28:40 +08:00
dependabot[bot] e290acfef6
build(deps): bump github.com/veraison/go-cose from 1.0.0-rc.2 to 1.0.0 (#247) 2023-01-30 10:46:45 +00:00
dependabot[bot] e8f14a5503
build(deps): bump oras.land/oras-go/v2 from 2.0.0-rc.6 to 2.0.0 (#248) 2023-01-30 10:43:42 +00:00
Patrick Zheng 36d97f79ab
feat: support OCI image manifest (#241)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-01-19 10:08:04 +08:00
Patrick Zheng e11bfd8f2c
doc: add examples for sign and verify (#238)
This PR adds examples for notation-go sign and verify including fully
functional examples (can run in go playground) for local sign and local
verify.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2023-01-13 18:22:14 +08:00
Pritesh Bandi c90d5213ae
Fix error message (#236)
Fix error message: Instead of printing bytes in error message convert them to string.

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-12-22 10:50:29 -08:00
Patrick Zheng 6c88d3de12
update: logs and error messages (#235)
This PR adds more logs to the library and improves error messages.

This PR intends to resolve following issues:
https://github.com/notaryproject/notation/issues/462
https://github.com/notaryproject/notation/issues/128 -> comments related
to trust policy

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-12-19 22:17:57 -08:00
dependabot[bot] 7b22cec0e5
build(deps): bump oras.land/oras-go/v2 from 2.0.0-rc.5 to 2.0.0-rc.6 (#234)
Bumps [oras.land/oras-go/v2](https://github.com/oras-project/oras-go)
from 2.0.0-rc.5 to 2.0.0-rc.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/oras-project/oras-go/releases">oras.land/oras-go/v2's
releases</a>.</em></p>
<blockquote>
<h2>v2.0.0-rc.6</h2>
<h2>New Features</h2>
<ul>
<li>Upgrade package <a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6/content/oci"><code>content/oci</code></a>
<ul>
<li>Support creating <a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6/content/oci#ReadOnlyStore">read-only
OCI store</a> from <a
href="https://pkg.go.dev/io/fs#FS"><code>fs.FS</code></a></li>
<li>Support reading <a
href="https://github.com/opencontainers/image-spec/blob/v1.1.0-rc2/image-layout.md">OCI
layout</a> from tar archives
<ul>
<li>Compatible with tar archives generated by <code>docker buildx
build</code> with option <a
href="https://docs.docker.com/engine/reference/commandline/buildx_build/#output"><code>--output=type=oci</code></a></li>
</ul>
</li>
<li>Support <a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6/content/oci#Store.Resolve">resolving</a>
a digest to a descriptor</li>
<li>Support <a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6/content/oci#Store.Predecessors">discovering
predecessors</a> that are not tagged in <a
href="https://github.com/opencontainers/image-spec/blob/v1.1.0-rc2/image-layout.md#indexjson-file"><code>index.json</code></a></li>
</ul>
</li>
<li><a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6#Copy"><code>oras.Copy()</code></a>
supports copying images with <a
href="https://github.com/opencontainers/image-spec/blob/main/layer.md#non-distributable-layers">non-distributable</a>
/ <a
href="https://docs.docker.com/registry/spec/manifest-v2-2/#media-types">foreign</a>
layers by skipping.</li>
</ul>
<h2>Bug Fixes</h2>
<ul>
<li>fix <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/368">#368</a>:
Range query is attempted for fetching blob from registries without range
query support</li>
<li>fix <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/374">#374</a>:
<a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6/registry/remote#Repository.FetchReference"><code>Repository.FetchReference()</code></a>
fails when registry responds with chunked transfer encoding</li>
<li>fix <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/383">#383</a>:
<a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6#Copy"><code>oras.Copy()</code></a>
may fail when the option <a
href="https://pkg.go.dev/oras.land/oras-go/v2@v2.0.0-rc.6#CopyGraphOptions"><code>FindSuccessors</code></a>
finds successors from external sources</li>
</ul>
<h2>Other Changes</h2>
<ul>
<li><strong>BREAKING CHANGE</strong>: Change the type of
<code>Concurrency</code> options from <code>int64</code> to
<code>int</code></li>
<li>Improve code quality (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/363">#363</a>,
<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/364">#364</a>)</li>
<li>Improve the readability of GoDoc</li>
</ul>
<h2>What's Changed</h2>
<ul>
<li>doc: fix testable example name by <a
href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/365">oras-project/oras-go#365</a></li>
<li>fix: avoid copying Repository mutex state by <a
href="https://github.com/abursavich"><code>@​abursavich</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/364">oras-project/oras-go#364</a></li>
<li>fix: attempt range query only if server explicitly supports by <a
href="https://github.com/shizhMSFT"><code>@​shizhMSFT</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/369">oras-project/oras-go#369</a></li>
<li>doc: make the default options displayed next to the option type on
godoc by <a
href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/372">oras-project/oras-go#372</a></li>
<li>fix: avoid panic from releasing unacquired semaphore by <a
href="https://github.com/abursavich"><code>@​abursavich</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/363">oras-project/oras-go#363</a></li>
<li>refactor!: update the type of <code>Concurrency</code> options to
<code>int</code> by <a
href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/376">oras-project/oras-go#376</a></li>
<li>fix: allow chunked responses via HEAD request fallback by <a
href="https://github.com/AaronFriel"><code>@​AaronFriel</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/370">oras-project/oras-go#370</a></li>
<li>feat: support creating read-only OCI store from <code>fs.FS</code>
by <a href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a>
in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/367">oras-project/oras-go#367</a></li>
<li>feat: support reading OCI layout from tarballs by <a
href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/379">oras-project/oras-go#379</a></li>
<li>feat: change foreign layer error message at repository.Fetch() by <a
href="https://github.com/wangxiaoxuan273"><code>@​wangxiaoxuan273</code></a>
in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/377">oras-project/oras-go#377</a></li>
<li>feat: skip foreign layers on <code>oras.Copy</code> by <a
href="https://github.com/shizhMSFT"><code>@​shizhMSFT</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/380">oras-project/oras-go#380</a></li>
<li>fix: use proxy when copying node with successors by <a
href="https://github.com/qweeah"><code>@​qweeah</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/384">oras-project/oras-go#384</a></li>
<li>refactor: refactor OCI store to fully support
<code>Predecessors()</code> and <code>Resolve()</code> by <a
href="https://github.com/Wwwsylvia"><code>@​Wwwsylvia</code></a> in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/385">oras-project/oras-go#385</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/abursavich"><code>@​abursavich</code></a> made
their first contribution in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/364">oras-project/oras-go#364</a></li>
<li><a
href="https://github.com/AaronFriel"><code>@​AaronFriel</code></a> made
their first contribution in <a
href="https://github-redirect.dependabot.com/oras-project/oras-go/pull/370">oras-project/oras-go#370</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/oras-project/oras-go/compare/v2.0.0-rc.5...v2.0.0-rc.6">https://github.com/oras-project/oras-go/compare/v2.0.0-rc.5...v2.0.0-rc.6</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="4e58192a51"><code>4e58192</code></a>
refactor: refactor OCI store to fully support
<code>Predecessors()</code> and `Resolve()...</li>
<li><a
href="4b1d01612d"><code>4b1d016</code></a>
fix: use proxy when copying node with successors (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/384">#384</a>)</li>
<li><a
href="79e13bc490"><code>79e13bc</code></a>
feat: skip foreign layers on <code>oras.Copy</code> (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/380">#380</a>)</li>
<li><a
href="8500e54ba3"><code>8500e54</code></a>
feat: change foreign layer error message at repository.Fetch() (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/377">#377</a>)</li>
<li><a
href="0382be2668"><code>0382be2</code></a>
feat: support reading OCI layout from tarballs (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/379">#379</a>)</li>
<li><a
href="04d11f9cc6"><code>04d11f9</code></a>
feat: support creating read-only OCI store from <code>fs.FS</code> (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/367">#367</a>)</li>
<li><a
href="253d5ec980"><code>253d5ec</code></a>
fix: allow chunked responses via HEAD request fallback (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/370">#370</a>)</li>
<li><a
href="275b94010f"><code>275b940</code></a>
refactor!: update the type of <code>Concurrency</code> options to
<code>int</code> (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/376">#376</a>)</li>
<li><a
href="a5be49a42a"><code>a5be49a</code></a>
fix: avoid panic from releasing unacquired semaphore (<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/363">#363</a>)</li>
<li><a
href="ff0519565c"><code>ff05195</code></a>
doc: make the default options displayed next to the option type on godoc
(<a
href="https://github-redirect.dependabot.com/oras-project/oras-go/issues/372">#372</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/oras-project/oras-go/compare/v2.0.0-rc.5...v2.0.0-rc.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=oras.land/oras-go/v2&package-manager=go_modules&previous-version=2.0.0-rc.5&new-version=2.0.0-rc.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-19 16:23:23 -08:00
Yi Zha bce61623c0
build: bump up versions for rc.1 release (#232)
update to `notation-core-go`  `v1.0.0-rc.1`

Signed-off-by: Yi Zha <yizha1@microsoft.com>
2022-12-07 11:17:10 +08:00
Patrick Zheng 6514ba4431
fix: updated notation artifact type to application/vnd.cncf.notary.signature (#231)
This PR changes the notation artifact type from
`application/vnd.cncf.notary.v2.signature` to
`application/vnd.cncf.notary.signature`.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-12-07 09:43:19 +08:00
Pritesh Bandi 180ad994fe
Improving debug log for plugin (#228)
Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-12-06 13:15:03 +08:00
Patrick Zheng 1a9436d7a1
update: bump up notation-core-go in notation-go (#227)
All PRs in notation-core-go are merged. Updating dependency in
notation-go.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-12-06 10:47:46 +08:00
Junjie Gao e8a1ad03ee
fix: optimize verification level skip check (#226)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-12-06 10:32:36 +08:00
Jonathan Donas 0355e8eb49
Add additional header validation for payload (#178)
Addresses TODO's tracked in [Issue 80](https://github.com/notaryproject/notation-go/issues/80)

- Note that new tests for NewSignerFromFiles were already added back [in September](2bcfd343f9)
- Validations around no top-level attributes being added are from [5.c.c](https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#signing-workflow-using-plugin-1) in the spec

Signed-off-by: Jonathan Donas <jddonas@amazon.com>
2022-12-05 10:00:44 -08:00
Junjie Gao e9545a7183
feat: add required log (#221)
Example
## sign ## 
```
➜  ./notation sign $IMAGE -e 2s             
Warning: Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:v1`) because tags are mutable and a tag reference can point to a different artifact than the one signed
Resolved artifact tag `v1` to digest `sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47` before signing
sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47

➜  ./notation sign $IMAGE -e 2s -v
Warning: Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:v1`) because tags are mutable and a tag reference can point to a different artifact than the one signed
Resolved artifact tag `v1` to digest `sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47` before signing
sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47

➜  ./notation sign $IMAGE -e 2s -d
Warning: Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:v1`) because tags are mutable and a tag reference can point to a different artifact than the one signed
Resolved artifact tag `v1` to digest `sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47` before signing
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/v1" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "HEAD"                      
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "200 OK"                   
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "942"                   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Type": "application/vnd.docker.distribution.manifest.v2+json" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Content-Digest": "sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "Etag": "\"sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47\"" 
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
WARN[2022-12-02T13:10:25+08:00] Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:v1`) because tags are mutable and a tag reference can point to a different artifact than the one signed 
INFO[2022-12-02T13:10:25+08:00] Resolved artifact tag `v1` to digest `sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47` before signing 
DEBU[2022-12-02T13:10:25+08:00] generic signing for sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 
DEBU[2022-12-02T13:10:25+08:00] sign request:                                
DEBU[2022-12-02T13:10:25+08:00]   ContentType: application/vnd.cncf.notary.payload.v1+json 
DEBU[2022-12-02T13:10:25+08:00]   Content: {"targetArtifact":{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47","size":942}} 
DEBU[2022-12-02T13:10:25+08:00]   Expiry: 2022-12-02 13:10:26.218713827 +0800 CST m=+4.043056722 
DEBU[2022-12-02T13:10:25+08:00]   SigningTime: 2022-12-02 13:10:25.683371981 +0800 CST m=+3.507714874 
DEBU[2022-12-02T13:10:25+08:00]   SigningScheme: notary.x509                 
DEBU[2022-12-02T13:10:25+08:00]   SigningAgent: Notation/1.0.0               
DEBU[2022-12-02T13:10:25+08:00] generate annotation                          
DEBU[2022-12-02T13:10:25+08:00] push signature, artifact descriptor: {MediaType:application/vnd.docker.distribution.manifest.v2+json Digest:sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 Size:942 URLs:[] Annotations:map[] Data:[] Platform:<nil> ArtifactType:}, annotations: map[io.cncf.notary.x509chain.thumbprint#S256:["676ae98f2cc491ce67cf897b3f7f59583a62193282c80d384814c900e4958c16"]] 
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/blobs/uploads/" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "POST"                      
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "202 Accepted"             
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Upload-Uuid": "97835b52-8e9c-4f35-82d8-8e19b434a738" 
DEBU[2022-12-02T13:10:25+08:00]    "Location": "http://localhost:5000/v2/net-monitor/blobs/uploads/97835b52-8e9c-4f35-82d8-8e19b434a738?_state=LU0rqXS4CRHkO8Y3wL1-YxFRn2rqX55hlt9cI7NwYB97Ik5hbWUiOiJuZXQtbW9uaXRvciIsIlVVSUQiOiI5NzgzNWI1Mi04ZTljLTRmMzUtODJkOC04ZTE5YjQzNGE3MzgiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjItMTItMDJUMDU6MTA6MjUuNjg2OTgxNDg0WiJ9" 
DEBU[2022-12-02T13:10:25+08:00]    "Range": "0-0"                            
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "0"                     
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/blobs/uploads/97835b52-8e9c-4f35-82d8-8e19b434a738?_state=LU0rqXS4CRHkO8Y3wL1-YxFRn2rqX55hlt9cI7NwYB97Ik5hbWUiOiJuZXQtbW9uaXRvciIsIlVVSUQiOiI5NzgzNWI1Mi04ZTljLTRmMzUtODJkOC04ZTE5YjQzNGE3MzgiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjItMTItMDJUMDU6MTA6MjUuNjg2OTgxNDg0WiJ9&digest=sha256%3A472efea7f2acae601d8f052ff89fdd9cbe66a172cb0f8ddf2f1396b99d07fd38" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "PUT"                       
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "Content-Type": "application/octet-stream" 
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "201 Created"              
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "0"                     
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Content-Digest": "sha256:472efea7f2acae601d8f052ff89fdd9cbe66a172cb0f8ddf2f1396b99d07fd38" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "Location": "http://localhost:5000/v2/net-monitor/blobs/sha256:472efea7f2acae601d8f052ff89fdd9cbe66a172cb0f8ddf2f1396b99d07fd38" 
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "PUT"                       
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "Content-Type": "application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "201 Created"              
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Content-Digest": "sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "Location": "http://localhost:5000/v2/net-monitor/manifests/sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e" 
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "0"                     
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/referrers/sha256:0000000000000000000000000000000000000000000000000000000000000000" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "404 Not Found"            
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "Content-Type": "text/plain; charset=utf-8" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "19"                    
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256-cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "200 OK"                   
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Content-Digest": "sha256:829256e18b2ee0980a39a2ff86182c8459303b15b676b00dc7006e123e7599ee" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "Etag": "\"sha256:829256e18b2ee0980a39a2ff86182c8459303b15b676b00dc7006e123e7599ee\"" 
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "901"                   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Type": "application/vnd.oci.image.index.v1+json" 
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256-cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "PUT"                       
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "Content-Type": "application/vnd.oci.image.index.v1+json" 
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "201 Created"              
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "0"                     
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Content-Digest": "sha256:490010607becd94467b45783303458b5b1533bcc17a813dbaf60a4f4aa96f582" 
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "Location": "http://localhost:5000/v2/net-monitor/manifests/sha256:490010607becd94467b45783303458b5b1533bcc17a813dbaf60a4f4aa96f582" 
DEBU[2022-12-02T13:10:25+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256:829256e18b2ee0980a39a2ff86182c8459303b15b676b00dc7006e123e7599ee" 
DEBU[2022-12-02T13:10:25+08:00]  Request method: "DELETE"                    
DEBU[2022-12-02T13:10:25+08:00]  Request headers:                            
DEBU[2022-12-02T13:10:25+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:10:25+08:00]  Response Status: "202 Accepted"             
DEBU[2022-12-02T13:10:25+08:00]  Response headers:                           
DEBU[2022-12-02T13:10:25+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:10:25+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:10:25+08:00]    "Date": "Fri, 02 Dec 2022 05:10:25 GMT"   
DEBU[2022-12-02T13:10:25+08:00]    "Content-Length": "0"                     
sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47
```
## verify ## 
```
➜  ./notation verify $IMAGE       
Warning: Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:v1`) because tags are mutable and a tag reference can point to a different artifact than the one signed
Resolved artifact tag `v1` to digest `sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47` before signing
Error: signature verification failed

➜  ./notation verify $IMAGE -v
Warning: Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:v1`) because tags are mutable and a tag reference can point to a different artifact than the one signed
Resolved artifact tag `v1` to digest `sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47` before signing
INFO passing a nil signature to check 'skip' level 
ERRO integrity validation failed. Failure reason: unable to parse the digital signature, error : signature envelope format with media type "" is not supported 
INFO check over. not 'skip' level                 
INFO processing signature with digest: sha256:6e0a5084fc479f071a51cb11518f70b795a9f160ae62851dd34d821e3c7b371a 
ERRO expiry validation failed. Failure reason: digital signature has expired on "Fri, 02 Dec 2022 13:09:58 +0800" 
INFO processing signature with digest: sha256:74bd7d7fb3a0a9a26e542a0849c5c6f803b5a8f53c7d02a1d2471b8f4ec808e0 
ERRO expiry validation failed. Failure reason: digital signature has expired on "Fri, 02 Dec 2022 13:10:04 +0800" 
INFO processing signature with digest: sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e 
ERRO expiry validation failed. Failure reason: digital signature has expired on "Fri, 02 Dec 2022 13:10:26 +0800" 
Error: signature verification failed

➜  ./notation verify $IMAGE -d
Warning: Always sign the artifact using digest(`@sha256:...`) rather than a tag(`:v1`) because tags are mutable and a tag reference can point to a different artifact than the one signed
Resolved artifact tag `v1` to digest `sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47` before signing
INFO[2022-12-02T13:14:39+08:00] passing a nil signature to check 'skip' level 
DEBU[2022-12-02T13:14:39+08:00] verify signature against artifact  referenced as localhost:5000/net-monitor@sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 
DEBU[2022-12-02T13:14:39+08:00] verification level: &{Name:strict Enforcement:map[authenticTimestamp:enforce authenticity:enforce expiry:enforce integrity:enforce revocation:enforce]} 
ERRO[2022-12-02T13:14:39+08:00] integrity validation failed. Failure reason: unable to parse the digital signature, error : signature envelope format with media type "" is not supported 
INFO[2022-12-02T13:14:39+08:00] check over. not 'skip' level                 
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "HEAD"                      
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]    "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "200 OK"                   
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47\"" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "942"                   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/vnd.docker.distribution.manifest.v2+json" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00] fetch signature manifest                     
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/referrers/sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47?artifactType=application%2Fvnd.cncf.notary.v2.signature" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "404 Not Found"            
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "text/plain; charset=utf-8" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "19"                    
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256-cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "Accept": "application/vnd.docker.distribution.manifest.v2+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.oci.image.index.v1+json, application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "200 OK"                   
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "1308"                  
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/vnd.oci.image.index.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:490010607becd94467b45783303458b5b1533bcc17a813dbaf60a4f4aa96f582" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:490010607becd94467b45783303458b5b1533bcc17a813dbaf60a4f4aa96f582\"" 
INFO[2022-12-02T13:14:39+08:00] processing signature with digest: sha256:6e0a5084fc479f071a51cb11518f70b795a9f160ae62851dd34d821e3c7b371a 
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256:6e0a5084fc479f071a51cb11518f70b795a9f160ae62851dd34d821e3c7b371a" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "Accept": "application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "200 OK"                   
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:6e0a5084fc479f071a51cb11518f70b795a9f160ae62851dd34d821e3c7b371a\"" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "628"                   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:6e0a5084fc479f071a51cb11518f70b795a9f160ae62851dd34d821e3c7b371a" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/blobs/sha256:9e27c57b266d8bcd206a90af96dba94a6c2d9ac8fe93d47979aaf7ce47a34f68" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "Range": "bytes=0-2220"                   
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "206 Partial Content"      
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "2221"                  
DEBU[2022-12-02T13:14:39+08:00]    "Content-Range": "bytes 0-2220/2221"      
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Accept-Ranges": "bytes"                  
DEBU[2022-12-02T13:14:39+08:00]    "Cache-Control": "max-age=31536000"       
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/octet-stream" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:9e27c57b266d8bcd206a90af96dba94a6c2d9ac8fe93d47979aaf7ce47a34f68" 
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:9e27c57b266d8bcd206a90af96dba94a6c2d9ac8fe93d47979aaf7ce47a34f68\"" 
DEBU[2022-12-02T13:14:39+08:00] verify signature against artifact sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 referenced as localhost:5000/net-monitor@sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 
DEBU[2022-12-02T13:14:39+08:00] verification level: &{Name:strict Enforcement:map[authenticTimestamp:enforce authenticity:enforce expiry:enforce integrity:enforce revocation:enforce]} 
DEBU[2022-12-02T13:14:39+08:00] verify cert chain                            
DEBU[2022-12-02T13:14:39+08:00] verify trust identity                        
DEBU[2022-12-02T13:14:39+08:00] verify expiry                                
ERRO[2022-12-02T13:14:39+08:00] expiry validation failed. Failure reason: digital signature has expired on "Fri, 02 Dec 2022 13:09:58 +0800" 
INFO[2022-12-02T13:14:39+08:00] processing signature with digest: sha256:74bd7d7fb3a0a9a26e542a0849c5c6f803b5a8f53c7d02a1d2471b8f4ec808e0 
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256:74bd7d7fb3a0a9a26e542a0849c5c6f803b5a8f53c7d02a1d2471b8f4ec808e0" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "Accept": "application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "200 OK"                   
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:74bd7d7fb3a0a9a26e542a0849c5c6f803b5a8f53c7d02a1d2471b8f4ec808e0" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:74bd7d7fb3a0a9a26e542a0849c5c6f803b5a8f53c7d02a1d2471b8f4ec808e0\"" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "628"                   
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/blobs/sha256:b804160dff6d263d918c4ec4088876a325f4b59f003c0eaba55fd71419f73557" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "Range": "bytes=0-2220"                   
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "206 Partial Content"      
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Accept-Ranges": "bytes"                  
DEBU[2022-12-02T13:14:39+08:00]    "Cache-Control": "max-age=31536000"       
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "2221"                  
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:b804160dff6d263d918c4ec4088876a325f4b59f003c0eaba55fd71419f73557" 
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:b804160dff6d263d918c4ec4088876a325f4b59f003c0eaba55fd71419f73557\"" 
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Range": "bytes 0-2220/2221"      
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/octet-stream" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00] verify signature against artifact sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 referenced as localhost:5000/net-monitor@sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 
DEBU[2022-12-02T13:14:39+08:00] verification level: &{Name:strict Enforcement:map[authenticTimestamp:enforce authenticity:enforce expiry:enforce integrity:enforce revocation:enforce]} 
DEBU[2022-12-02T13:14:39+08:00] verify cert chain                            
DEBU[2022-12-02T13:14:39+08:00] verify trust identity                        
DEBU[2022-12-02T13:14:39+08:00] verify expiry                                
ERRO[2022-12-02T13:14:39+08:00] expiry validation failed. Failure reason: digital signature has expired on "Fri, 02 Dec 2022 13:10:04 +0800" 
INFO[2022-12-02T13:14:39+08:00] processing signature with digest: sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e 
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/manifests/sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "Accept": "application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "200 OK"                   
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e\"" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "628"                   
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/vnd.oci.artifact.manifest.v1+json" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:117f4c3c03f228776cdf9727f7ce77c75f95a98d9fa7a22455f30c8639f4ed4e" 
DEBU[2022-12-02T13:14:39+08:00]  Request URL: "http://localhost:5000/v2/net-monitor/blobs/sha256:472efea7f2acae601d8f052ff89fdd9cbe66a172cb0f8ddf2f1396b99d07fd38" 
DEBU[2022-12-02T13:14:39+08:00]  Request method: "GET"                       
DEBU[2022-12-02T13:14:39+08:00]  Request headers:                            
DEBU[2022-12-02T13:14:39+08:00]    "Range": "bytes=0-2220"                   
DEBU[2022-12-02T13:14:39+08:00]    "User-Agent": "notation/v0.12.0-beta.1+unreleased" 
DEBU[2022-12-02T13:14:39+08:00]  Response Status: "206 Partial Content"      
DEBU[2022-12-02T13:14:39+08:00]  Response headers:                           
DEBU[2022-12-02T13:14:39+08:00]    "Content-Range": "bytes 0-2220/2221"      
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Content-Digest": "sha256:472efea7f2acae601d8f052ff89fdd9cbe66a172cb0f8ddf2f1396b99d07fd38" 
DEBU[2022-12-02T13:14:39+08:00]    "Docker-Distribution-Api-Version": "registry/2.0" 
DEBU[2022-12-02T13:14:39+08:00]    "X-Content-Type-Options": "nosniff"       
DEBU[2022-12-02T13:14:39+08:00]    "Date": "Fri, 02 Dec 2022 05:14:39 GMT"   
DEBU[2022-12-02T13:14:39+08:00]    "Accept-Ranges": "bytes"                  
DEBU[2022-12-02T13:14:39+08:00]    "Content-Length": "2221"                  
DEBU[2022-12-02T13:14:39+08:00]    "Etag": "\"sha256:472efea7f2acae601d8f052ff89fdd9cbe66a172cb0f8ddf2f1396b99d07fd38\"" 
DEBU[2022-12-02T13:14:39+08:00]    "Cache-Control": "max-age=31536000"       
DEBU[2022-12-02T13:14:39+08:00]    "Content-Type": "application/octet-stream" 
DEBU[2022-12-02T13:14:39+08:00] verify signature against artifact sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 referenced as localhost:5000/net-monitor@sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 
DEBU[2022-12-02T13:14:39+08:00] verification level: &{Name:strict Enforcement:map[authenticTimestamp:enforce authenticity:enforce expiry:enforce integrity:enforce revocation:enforce]} 
DEBU[2022-12-02T13:14:39+08:00] verify cert chain                            
DEBU[2022-12-02T13:14:39+08:00] verify trust identity                        
DEBU[2022-12-02T13:14:39+08:00] verify expiry                                
ERRO[2022-12-02T13:14:39+08:00] expiry validation failed. Failure reason: digital signature has expired on "Fri, 02 Dec 2022 13:10:26 +0800" 
DEBU[2022-12-02T13:14:39+08:00] Signature verification failed for all the signatures associated with digest sha256:cd5eef6b6a6750c9850a8d7b1a5435f35f1a1808d66c74e265f6b7ec290bea47 
Error: signature verification failed
```

> warning logs for successful verification will added in
https://github.com/notaryproject/notation/pull/450/ By Patrick

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-12-04 21:22:02 -08:00
Pritesh Bandi bc022cc61d
Pass expiry to envelope-generator plugin (#222)
There will be two PRs, one in notation-go([PR#222](https://github.com/notaryproject/notation-go/pull/222))
and other in notation([PR#458](https://github.com/notaryproject/notation/pull/458)) repo.

*Tests are failing because this PR depends on https://github.com/notaryproject/notation-go/pull/222*

Issue: https://github.com/notaryproject/notation/issues/443

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-12-01 20:05:23 -08:00
Patrick Zheng 35d90607a6
feat: added tag reference log for notation.Sign and notation.Verify (#223)
This PR adds tag reference logs in notation.Sign and notation.Verify.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-12-02 10:18:27 +08:00
Patrick Zheng 7ae1f5fd07
update: bump up dependencies (#219)
Bump up to use the most recent notation-core-go.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-28 20:30:56 -08:00
Pritesh Bandi 2da0327d37
Use minimum(user only) file permissions (#216)
Since we are not implementing system config behavior in rc1, updating
code to have only user(r,w,x) permission. Also, its a good practice to use minimum permission model

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-11-28 14:27:41 -08:00
Patrick Zheng ab113ebd2a
update: check SignatureMediaType in notation.Verify (#208)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-25 10:20:16 +08:00
Junjie Gao 7e391f7562
fix: update plugin to add ContractVersion (#207)
- Add ContractVersion argument when executing plugin request
- Check ContractVersion when getting plugin metadata
- Moved the path existence check from manager.Get() to NewCLIPlugin()

Test:
Added invalid contract version unit test.

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-11-23 14:45:16 +08:00
Patrick Zheng 38198df806
Add: added set data structure (#210)
This PR adds a generic Set data structure into the library.

Resolves #203.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-23 14:39:31 +08:00
Patrick Zheng 6548adc2e8
update: replaced strings.Index with strings.Cut (#211)
This PR scans for strings.Index() in notation-go and replaced it with
strings.Cut.

It also adds checks on outputs of strings.Cut based on the specs.

Resolves #204.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-23 14:27:41 +08:00
Patrick Zheng 27251a828d
update: upgraded to oras-go v2.0.0-rc.5 (#209)
This PR upgrades notation-go to use oras-go v2.0.0-rc.5.
Main change: ReferrerFinder is added into registry.Repository interface.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-18 05:00:49 -08:00
Patrick Zheng 2573c88a5f
update: Package signature refactoring (#200)
Files intended to be reviewed in this PR: All files inside the signer package (Refactoring of signer package in this PR incorporates the updated plugin and plugin/proto packages.)

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-17 06:38:17 -08:00
Patrick Zheng ed31122368
update: updated verifier design (#206)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-17 11:25:23 +08:00
Patrick Zheng 9d6b902999
feat: Added log package (#202)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-16 09:15:19 +08:00
Patrick Zheng e43b2925ba
update: Package verification refactoring (#186)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-15 14:28:19 +08:00
Junjie Gao fc5044e670
refactor: update plugin for notation package (#199)
Replaced the old internal/plugin with new plugin package in notation
package

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-11-14 12:54:32 +08:00
Patrick Zheng d7c82a39dc
update: Package notation refactoring (#191)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-11 15:24:32 +08:00
Junjie Gao f9c0b87a90
fix: dir package userConfigDir typo (#196)
1. fixed userConfigDir
2. fixed unit test on windows
3. added testdata for SysFS Open

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-11-10 23:14:48 +08:00
Junjie Gao 61ffb09828
refactor: plugin package (#184)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-11-10 09:06:52 +08:00
Patrick Zheng 79b49af853
feat: Added trustpolicy and truststore packages under verification (#192)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-09 17:11:49 +08:00
Patrick Zheng c3e6e07814
update: Package registry refactoring (#190)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-11-09 16:47:46 +08:00
Rakesh Gariganti 49ba3a341f
Fix VerifySignature command json marshaling (#188)
Temporary fix for
https://github.com/notaryproject/notation-core-go/issues/88

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-11-08 19:20:48 -08:00
Junjie Gao a44d663e7b
refactor: config package (#182)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-11-04 13:42:50 +08:00
Rakesh Gariganti faeac45740
Remove plugin name and version from ProcessedAttributes response from… (#183)
Currently, the Verifier is expecting VerificationPlugin and
VerificationPluginMinVersion extended attributes to be processed by a
verification plugin, which just doesn't make sense. This PR will stop
sending those two attributes to a plugin.

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-11-04 13:25:23 +08:00
Junjie Gao 75b3248459
refactor: dir package (#179)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-11-03 23:55:41 +08:00
Pritesh Bandi 55b8746fbc
updating notation-core-go to v0.2.0-beta.1 (#180)
Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-11-02 13:00:20 +08:00
Patrick Zheng 82ec47d568
update: bump up notation-core-go (#177)
This PR bumps up notation-core-go. Since extended attribute key type is
changed to interface{}, notation-go code needs to be updated
accordingly.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-10-31 20:50:07 +08:00
Junjie Gao bccecbc830
fix: rename envelopeType to signatureFormat (#175)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-10-31 09:50:31 +08:00
Patrick Zheng 7ad4eca1a5
update: further clean up on notation-go (#173)
1. Removed crypto, as it is already moved into notation-core-go.
2. Removed internal/encoding. 

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-10-25 09:13:37 +08:00
Patrick Zheng f35c52efb5
update: clean up main branch (#172)
1. Deprecated the old verification workflow.
2. Removed usage of cache.

Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-10-20 10:33:29 +08:00
Junjie Gao e0892fc600
feat: add envelope type config (#159)
Add `EnvelopeType` parameter in config.json file

Resolves: https://github.com/notaryproject/notation/issues/325
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-10-17 15:14:13 +08:00
Yi Zha 3d8de5f05f
build: bump dependencies (#165) 2022-10-11 09:53:27 -07:00
zaihaoyin e2ae1fecf0
refactor: support cose envelope (#146)
Signed-off-by: zaihaoyin <zaihaoyin@microsoft.com>
2022-10-09 22:33:10 +08:00
zaihaoyin 2bcfd343f9
refactor: refactor pluginSigner to support new signature interface (#131)
## What
Refactor `notation-go` to support multiple envelope types.
Background can be checked in
https://github.com/notaryproject/notation/discussions/278
I wthe whole PR into two PRs to help review, this is the first PR. More
unit test cases will be added in the next PR.
The whole picture is here
https://github.com/notaryproject/notation-go/pull/146

## Major Changes
- Use package `github.com/notaryproject/notation-core-go/signature` to
sign and verify.
- Combine `runner` and `signer` into a `provider` for `pluginSigner` to
sign and remove the `pluginSigProvider`.
- Add `builtinProvider` to support local signing and `externalProvider`
to support signing by plugin.
- Move the payload media type and its checks to `signature` package as
mentioned in https://github.com/notaryproject/notation-core-go/pull/73
- Support new
[keySpec](https://github.com/notaryproject/notaryproject/blob/main/signature-specification.md#algorithm-selection)
and plugin contract.
- Get verification plugin and version from extended attributes.
- Add `SpeculateSignatureEnvelopeFormat` to inspect signature (This
function may change later to better inspect a signature)
- Add sign/verify from file test cases.
Signed-off-by: zaihaoyin <zaihaoyin@microsoft.com>

Signed-off-by: zaihaoyin <zaihaoyin@microsoft.com>
Co-authored-by: zaihaoyin <zaihaoyin@microsoft.com>
2022-09-27 10:09:50 +08:00
Pritesh Bandi f89d6cfa96
fix the certs validation in trust store (#151)
Follow up change from
https://github.com/notaryproject/notation-go/pull/147#discussion_r978845793

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-09-26 09:13:01 -07:00
Binbin Li 84d4de1c21
fix: fix the certs validation in trust store (#147)
Signed-off-by: Binbin Li <libinbin@microsoft.com>
2022-09-23 21:54:10 +08:00
dependabot[bot] ea9ee15677
Bump oras.land/oras-go/v2 from 2.0.0-rc.2 to 2.0.0-rc.3 (#135) 2022-09-22 09:31:01 -07:00
Junjie Gao 76d4ca4e5e
feat: update to go 1.18 (#124)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-09-09 11:37:43 +08:00
Rakesh Gariganti a9fb055b8d
Plugin integration with verification workflow (#101)
* Verification Plugin integration with verification workflow
* Add signing authority verification unit tests
* unit tests boilerplate
* add verification plugin unit tests
* support plugin config

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
Co-authored-by: David Tesar <david.tesar@microsoft.com>
2022-08-19 10:52:29 -04:00
Shiwei Zhang 3f1301e307
build: bump dependencies (#112) 2022-08-17 16:50:08 -07:00
dependabot[bot] 78bc70a154
Bump github.com/go-ldap/ldap/v3 from 3.4.3 to 3.4.4 (#97)
Bumps [github.com/go-ldap/ldap/v3](https://github.com/go-ldap/ldap) from 3.4.3 to 3.4.4.
- [Release notes](https://github.com/go-ldap/ldap/releases)
- [Commits](https://github.com/go-ldap/ldap/compare/v3.4.3...v3.4.4)

---
updated-dependencies:
- dependency-name: github.com/go-ldap/ldap/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-16 15:42:16 +08:00
Junjie Gao c350ef73e5
fix: dir package optimize (#104)
1. GetPath logic: if the given path doesn't exist, return the first
   possible in the union directories, so when create a new file the
   UnionDirFS will select user directory which was provided as the first
   directory when create the UnionDirFS
2. change the constant name

Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-08-16 09:37:43 +08:00
Pritesh Bandi f3bae8315a
Updates based on signing scheme update in notation-go-core (#85)
Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-08-09 14:49:13 -07:00
Binbin Li 55fd020cc2
test: add registry unit test (#96) 2022-08-09 12:04:57 -07:00
zaihaoyin 234866cbd7
hash fix (#98) 2022-08-09 09:29:38 -07:00
Rakesh Gariganti e360d6886f
Use Notation's PathManager in verification workflow (#100)
Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-08-04 09:52:02 -07:00
Rakesh Gariganti e698b39314
Implement custom signature verification level (#84)
* Implement custom signature verification level
* Update custom verification with the latest spec changes
* fix merge conflicts

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-08-03 16:57:54 -07:00
Rakesh Gariganti 6312370a35
Basic Signature Verification (#79)
* verifier changes
* fix expiry data formatting
* use RFC1123Z for human readable time format
* support signingAuthority trust store

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-08-02 13:04:09 -07:00
Junjie Gao ba141e111a
feat: add config package & optimize dir package (#90)
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-08-02 13:30:46 +08:00
Shiwei Zhang 34be24db50
Add more badges to README.md (#94) 2022-07-28 10:37:51 -07:00
Patrick Zheng aaca74d9f2
resolving remote signature envelope digest as blob instead of manifest (#89)
Signed-off-by: Patrick Zheng <patrickzheng@microsoft.com>
2022-07-28 12:56:47 +08:00
Juncheng Zhu cdf50d9ff2
Migrate to codecov.io (#92)
Signed-off-by: Juncheng Zhu <junczhu@microsoft.com>
2022-07-28 12:50:31 +08:00
Binbin Li 7af715044c
feat: Use PackArtifact to push signature manifest (#87)
Signed-off-by: Binbin Li <libinbin@microsoft.com>
2022-07-27 17:01:34 +08:00
Junjie Gao c78f4ad635
Implementing Notation directory structure (#73)
* Directory Structure API
Signed-off-by: Junjie Gao <junjiegao@microsoft.com>
2022-07-20 20:54:29 -07:00
Jonathan Donas 5abc8e3074
Add command shapes for verify-signature and get-plugin-metadata (#76)
Signed-off-by: Jonathan Donas <jddonas@amazon.com>
2022-07-14 14:07:53 -07:00
Pritesh Bandi 962d79cd40
Refactor to use notation-core-go's SignatureEnvelope (#77)
* Refactor to use notation-core-go's SignatureEnvelope
* move code from signature/jws to signature

Signed-off-by: Pritesh Bandi <pritesb@amazon.com>
2022-07-12 10:56:03 -07:00
Rakesh Gariganti 3d22fbc121
Verification helpers (#72)
* Add verification helpers
* fix build

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-07-07 10:55:31 -07:00
David Tesar 4a649a91cc
add to project template (#66)
Signed-off-by: David Tesar <davete@microsoft.com>
2022-07-05 16:35:08 -07:00
Lixia (Sylvia) Lei ed2b227d00
Run unit tests in Github workflow (#60)
Signed-off-by: Lixia (Sylvia) Lei <lixia_lei@outlook.com>
2022-06-30 11:20:24 +08:00
dependabot[bot] ff39f29419
Bump actions/cache from 3.0.2 to 3.0.4 (#53)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.2 to 3.0.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.2...v3.0.4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-27 23:26:35 +08:00
dependabot[bot] 68e41ad3c9
Bump github.com/golang-jwt/jwt/v4 from 4.4.1 to 4.4.2 (#68)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.1 to 4.4.2.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.1...v4.4.2)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-27 23:25:42 +08:00
Binbin Li d65bdf6a09
refactor: refactor registry interface (#67)
Signed-off-by: Binbin Li <libinbin@microsoft.com>
2022-06-27 23:24:45 +08:00
Binbin Li 52fcb4aae8
chore: move registry directory under root (#64)
Signed-off-by: Binbin Li <libinbin@microsoft.com>
2022-06-23 17:02:30 +08:00
Rakesh Gariganti 44bffb0b61
Add SignatureVerificationLevel type (#55)
* Add SignatureVerificationLevel type

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-06-22 12:34:13 -07:00
Binbin Li 45f149e1b5
feat: move pkg/registry from notaryproject/notation (#63)
Signed-off-by: Binbin Li <libinbin050215@gmail.com>
2022-06-22 11:12:08 +08:00
Lixia (Sylvia) Lei 9f26faa2eb
Create codeql.yml (#56)
Signed-off-by: Sylvia Lei <lixia_lei@outlook.com>
2022-06-19 01:08:44 +08:00
Rakesh Gariganti 54c8362669
Fix trust store tests issue on Windows (#58)
* Fix trust store tests issue on Windows

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-06-17 16:39:45 -07:00
Rakesh Gariganti 0757b20223
Verify X509 trusted identities (#54)
* Verify X509 trusted identities

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-06-17 16:38:40 -07:00
Rakesh Gariganti a4fdff4190
Add GetApplicableTrustPolicy function (#51)
* Add GetApplicableTrustPolicy function
* move artifact path parsing to a separate method

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-06-17 16:37:49 -07:00
Rakesh Gariganti d75cdbdd5e
Add support for trust store parsing (#44)
* Add trust store parsing
* Reject non CA certificates in a trust store
* Support DER certificates
* Remove cert/key utils
* use corex509

Signed-off-by: rgnote <5878554+rgnote@users.noreply.github.com>
2022-06-02 13:50:10 -07:00
228 changed files with 19049 additions and 6165 deletions

21
.github/.codecov.yml vendored Normal file
View File

@ -0,0 +1,21 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
coverage:
status:
project:
default:
target: 80%
patch:
default:
target: 80%

View File

@ -0,0 +1,60 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: 🐛 Bug or Issue
description: Something is not working as expected or not working at all! Report it here!
labels: [bug, triage]
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this issue report. 🛑 Please check existing issues first before continuing: https://github.com/notaryproject/notation-go/issues
- type: textarea
id: verbatim
validations:
required: true
attributes:
label: "What is not working as expected?"
description: "In your own words, describe what the issue is."
- type: textarea
id: expect
validations:
required: true
attributes:
label: "What did you expect to happen?"
description: "A clear and concise description of what you expected to happen."
- type: textarea
id: reproduce
validations:
required: true
attributes:
label: "How can we reproduce it?"
description: "Detailed steps to reproduce the behavior, code snippets are welcome."
- type: textarea
id: environment
validations:
required: true
attributes:
label: Describe your environment
description: "OS and Golang version"
- type: textarea
id: version
validations:
required: true
attributes:
label: What is the version of your notation-go Library?
description: "Check the `go.mod` file for the library version."
- type: markdown
attributes:
value: |
If you want to contribute to this project, we will be happy to guide you through the contribution process especially when you already have a good proposal or understanding of how to fix this issue. Join us at https://slack.cncf.io/ and choose #notary-project channel.

18
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,18 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://slack.cncf.io/
about: "Join #notary-project channel on CNCF Slack"

View File

@ -0,0 +1,53 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: 🚀 Feature Request
description: Suggest an idea for this project.
labels: [enhancement, triage]
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to suggest a useful feature for the project!
- type: textarea
id: problem
validations:
required: true
attributes:
label: "Is your feature request related to a problem?"
description: "A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]"
- type: textarea
id: solution
validations:
required: true
attributes:
label: "What solution do you propose?"
description: "A clear and concise description of what you want to happen."
- type: textarea
id: alternatives
validations:
required: true
attributes:
label: "What alternatives have you considered?"
description: "A clear and concise description of any alternative solutions or features you've considered."
- type: textarea
id: context
validations:
required: false
attributes:
label: "Any additional context?"
description: "Add any other context or screenshots about the feature request here."
- type: markdown
attributes:
value: |
If you want to contribute to this project, we will be happy to guide you through the contribution process especially when you already have a good proposal or understanding of how to improve the functionality. Join us at https://slack.cncf.io/ and choose #notary-project channel.

View File

@ -1,3 +1,16 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
version: 2
updates:
- package-ecosystem: "gomod"

44
.github/licenserc.yml vendored Normal file
View File

@ -0,0 +1,44 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
header:
license:
spdx-id: Apache-2.0
content: |
Copyright The Notary Project Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
paths-ignore:
- '**/*.md'
- 'CODEOWNERS'
- 'LICENSE'
- 'MAINTAINERS'
- 'go.mod'
- 'go.sum'
- '**/testdata/**'
comment: on-failure
dependency:
files:
- go.mod

27
.github/workflows/add-to-project.yml vendored Normal file
View File

@ -0,0 +1,27 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
on:
issues:
types:
- opened
jobs:
add-to-project:
name: Add issue to project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/notaryproject/projects/10
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}

30
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,30 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: build
on:
push:
branches:
- main
- release-*
pull_request:
branches:
- main
- release-*
jobs:
build:
uses: notaryproject/notation-core-go/.github/workflows/reusable-build.yml@main
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

30
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,30 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: "CodeQL"
on:
push:
branches:
- main
- release-*
pull_request:
branches:
- main
- release-*
schedule:
- cron: '29 2 * * 5'
jobs:
analyze:
uses: notaryproject/notation-core-go/.github/workflows/reusable-codeql.yml@main

32
.github/workflows/license-checker.yml vendored Normal file
View File

@ -0,0 +1,32 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: License Checker
on:
push:
branches:
- main
- release-*
pull_request:
branches:
- main
- release-*
permissions:
contents: write
pull-requests: write
jobs:
check-license:
uses: notaryproject/notation-core-go/.github/workflows/reusable-license-checker.yml@main

33
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,33 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: "Close stale issues and PRs"
on:
schedule:
- cron: "30 1 * * *"
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
stale-issue-message: "This issue is stale because it has been opened for 60 days with no activity. Remove stale label or comment. Otherwise, it will be closed in 30 days."
stale-pr-message: "This PR is stale because it has been opened for 45 days with no activity. Remove stale label or comment. Otherwise, it will be closed in 30 days."
close-issue-message: "Issue closed due to no activity in the past 30 days."
close-pr-message: "PR closed due to no activity in the past 30 days."
days-before-issue-stale: 60
days-before-pr-stale: 45
days-before-issue-close: 30
days-before-pr-close: 30
exempt-all-milestones: true

View File

@ -1,42 +0,0 @@
name: test
on:
push:
branches: main
pull_request:
branches: main
jobs:
build:
name: Continuous Testing
runs-on: ubuntu-20.04
strategy:
matrix:
go-version: [1.17]
fail-fast: true
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Check out code
uses: actions/checkout@v3
- name: Cache Go modules
uses: actions/cache@v3.0.2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Build and test
run: make test
line_endings:
name: Check Line Endings
runs-on: ubuntu-20.04
strategy:
fail-fast: true
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Check line endings
run: make check-line-endings

23
.gitignore vendored
View File

@ -1 +1,24 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Code Editors
.vscode
.idea
*.sublime-project
*.sublime-workspace
# Custom
coverage.txt
# tmp directory was generated by example_remoteVerify_test.go
tmp/

View File

@ -1 +1,3 @@
* @notaryproject/notation-maintainers
# Repo-Level Owners (in alphabetical order)
# Note: This is only for the notaryproject/notation-go repo
* @gokarnm @niazfk @priteshbandi @rgnote @shizhMSFT @toddysm @Two-Hearts @vaninrao10 @yizha1

21
MAINTAINERS Normal file
View File

@ -0,0 +1,21 @@
# Org-Level Maintainers (in alphabetical order)
# Pattern: [First Name] [Last Name] <[Email Address]> ([GitHub Handle])
Niaz Khan <niazfk@amazon.com> (@niazfk)
Pritesh Bandi <priteshbandi@gmail.com> (@priteshbandi)
Shiwei Zhang <shizh@microsoft.com> (@shizhMSFT)
Toddy Mladenov <toddysm@gmail.com> (@toddysm)
Vani Rao <vaninrao@amazon.com> (@vaninrao10)
Yi Zha <yizha1@microsoft.com> (@yizha1)
# Repo-Level Maintainers (in alphabetical order)
# Note: This is for the notaryproject/notation-go repo
Milind Gokarn <gokarnm@amazon.com> (@gokarnm)
Patrick Zheng <patrickzheng@microsoft.com> (@Two-Hearts)
Rakesh Gariganti <garigant@amazon.com> (@rgnote)
# Emeritus Org Maintainers (in alphabetical order)
Justin Cormack <justin.cormack@docker.com> (@justincormack)
Steve Lasker <StevenLasker@hotmail.com> (@stevelasker)
# Emeritus Repo-Level Maintainers (in alphabetical order)
Junjie Gao <junjiegao@microsoft.com> (@JeyJeyGao)

View File

@ -1,13 +1,26 @@
# Copyright The Notary Project Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}'
.PHONY: all
all: check-line-endings test
all: test
.PHONY: test
test: ## run unit tests
go test ./...
test: check-line-endings ## run unit tests
go test -race -v -coverprofile=coverage.txt -covermode=atomic ./...
.PHONY: clean
clean:
@ -16,6 +29,7 @@ clean:
.PHONY: check-line-endings
check-line-endings: ## check line endings
! find . -name "*.go" -type f -exec file "{}" ";" | grep CRLF
! find . -name "*.sh" -type f -exec file "{}" ";" | grep CRLF
.PHONY: fix-line-endings
fix-line-endings: ## fix line endings

View File

@ -1,15 +1,28 @@
# Notation
A collection of libraries for supporting Notation sign, verify, push, pull of oci artifacts. Based on Notary V2 standard.
# notation-go
[![Build Status](https://github.com/notaryproject/notation-go/actions/workflows/build.yml/badge.svg?event=push&branch=main)](https://github.com/notaryproject/notation-go/actions/workflows/build.yml?query=workflow%3Abuild+event%3Apush+branch%3Amain)
[![Codecov](https://codecov.io/gh/notaryproject/notation-go/branch/main/graph/badge.svg)](https://codecov.io/gh/notaryproject/notation-go)
[![Go Reference](https://pkg.go.dev/badge/github.com/notaryproject/notation-go.svg)](https://pkg.go.dev/github.com/notaryproject/notation-go@main)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/notaryproject/notation-go/badge)](https://scorecard.dev/viewer/?uri=github.com/notaryproject/notation-go)
notation-go contains libraries for signing and verification of artifacts as per [Notary Project specifications](https://github.com/notaryproject/specifications). notation-go is being used by [notation](https://github.com/notaryproject/notation) CLI for signing and verifying artifacts.
notation-go reached a stable release as of July 2023 and continues to be actively developed and maintained.
Please visit [README](https://github.com/notaryproject/.github/blob/main/README.md) to know more about Notary Project.
> [!NOTE]
> The Notary Project documentation is available [here](https://notaryproject.dev/docs/).
## Table of Contents
- [Core Documents](#core-documents)
- [Documentation](#documentation)
- [Code of Conduct](#code-of-conduct)
- [License](#license)
## Core Documents
## Documentation
* [Governance for Notation](https://github.com/notaryproject/notary/blob/master/GOVERNANCE.md)
* [Maintainers and reviewers list](https://github.com/notaryproject/notary/blob/master/MAINTAINERS)
Library documentation is available at [Go Reference](https://pkg.go.dev/github.com/notaryproject/notation-go).
## Code of Conduct

36
RELEASE_CHECKLIST.md Normal file
View File

@ -0,0 +1,36 @@
# Release Checklist
## Overview
This document describes the checklist to publish a release for notation-go.
## Release Process from main
1. Check if there are any security vulnerabilities fixed and security advisories published before a release. Security advisories should be linked on the release notes.
2. Determine a [SemVer2](https://semver.org/)-valid version prefixed with the letter `v` for release. For example, `version="v1.0.0-rc.1"`.
3. If there is new release in [notation-core-go](https://github.com/notaryproject/notation-core-go) library that are required to be upgraded in notation-go, update the dependency versions in the follow `go.mod` and `go.sum` files of notation-go:
- [go.mod](go.mod), [go.sum](go.sum)
4. Open a bump up PR and submit the changes in step 3 to the notation-go repository.
5. After PR from step 4 is merged. Create another PR to update the value of `signingAgent` defined in file [signer/signer.go](signer/signer.go) with `notation-go/<version>`, where `<version>` is `$version` from step 2 without the `v` prefix. For example, `notation-go/1.0.0-rc.1`. The commit message MUST follow the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) and could be `bump: release $version`. Record the digest of that commit as `<commit_digest>`. This PR is also used for voting purpose of the new release. Add the link of change logs and repo-level maintainer list in the PR's description. The PR title could be `bump: release $version`. Make sure to reach a majority of approvals from the [repo-level maintainers](MAINTAINERS) before merging it. This PR MUST be merged using [Create a merge commit](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/about-merge-methods-on-github) method in GitHub.
6. After the voting PR is merged, execute `git clone https://github.com/notaryproject/notation-go.git` to clone the repository to your local file system.
7. Enter the cloned repository and execute `git checkout <commit_digest>` to switch to the specified branch based on the voting result.
8. Create a tag by running `git tag -am $version $version -s`.
9. Run `git tag` and ensure the desired tag name in the list looks correct, then push the new tag directly to the repository by running `git push origin $version`.
10. On notation-go GitHub page, goto [Tags](https://github.com/notaryproject/notation-go/tags). Your newly pushed tag should be shown on the top. Create a new release from the tag. Generate the release notes, revise the release description and change logs, and publish the release.
11. Announce the new release in the Notary Project community.
## Release Process from a release branch
1. Check if there are any security vulnerabilities fixed and security advisories published before a release. Security advisories should be linked on the release notes.
2. Determine a [SemVer2](https://semver.org/)-valid version prefixed with the letter `v` for release. For example, `version="v1.2.0-rc.1"`.
3. If a new release branch is needed, from main branch's [commit list](https://github.com/notaryproject/notation-go/commits/main/), find the commit that you want to cut the release. Click `<>` (Browse repository at this point). Create branch with name `release-<version>` from the commit, where `<version>` is `$version` from step 2 with the major and minor versions only. For example `release-1.2`. If the release branch already exists, skip this step.
4. If there is new release in [notation-core-go](https://github.com/notaryproject/notation-core-go) library that are required to be upgraded in notation-go, update the dependency versions in the follow `go.mod` and `go.sum` files of notation-go:
- [go.mod](go.mod), [go.sum](go.sum)
5. Open a bump up PR and submit the changes in step 4 to the release branch.
6. After PR from step 5 is merged. Create another PR to update the value of `signingAgent` defined in file `signer/signer.go` with `notation-go/<version>`, where `<version>` is `$version` from step 2 without the `v` prefix. For example, `notation-go/1.2.0-rc.1`. The commit message MUST follow the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) and could be `bump: release $version`. Record the digest of that commit as `<commit_digest>`. This PR is also used for voting purpose of the new release. Add the link of change logs and repo-level maintainer list in the PR's description. The PR title could be `bump: release $version`. Make sure to reach a majority of approvals from the [repo-level maintainers](MAINTAINERS) before merging it. This PR MUST be merged using [Create a merge commit](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/about-merge-methods-on-github) method in GitHub.
7. After the voting PR is merged, execute `git clone https://github.com/notaryproject/notation-go.git` to clone the repository to your local file system.
8. Enter the cloned repository and execute `git checkout <commit_digest>` to switch to the specified branch based on the voting result.
9. Create a tag by running `git tag -am $version $version -s`.
10. Run `git tag` and ensure the desired tag name in the list looks correct, then push the new tag directly to the repository by running `git push origin $version`.
11. On notation-go GitHub page, goto [Tags](https://github.com/notaryproject/notation-go/tags). Your newly pushed tag should be shown on the top. Create a new release from the tag. Generate the release notes, revise the release description and change logs, and publish the release.
12. Announce the new release in the Notary Project community.

67
config/base.go Normal file
View File

@ -0,0 +1,67 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"fmt"
"io/fs"
"os"
"path/filepath"
"github.com/notaryproject/notation-go/dir"
)
// save stores the cfg struct to file
func save(filePath string, cfg interface{}) error {
dir := filepath.Dir(filePath)
if err := os.MkdirAll(dir, 0700); err != nil {
return err
}
file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
return encoder.Encode(cfg)
}
// load reads file, parses json and stores in cfg struct
func load(filePath string, cfg interface{}) error {
path, err := dir.ConfigFS().SysPath(filePath)
if err != nil {
return err
}
// throw error if path is a directory or is a symlink or does not exist.
fileInfo, err := os.Lstat(path)
if err != nil {
return err
}
mode := fileInfo.Mode()
if mode.IsDir() || mode&fs.ModeSymlink != 0 {
return fmt.Errorf("%q is not a regular file (symlinks are not supported)", path)
}
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
return json.NewDecoder(file).Decode(cfg)
}

51
config/base_test.go Normal file
View File

@ -0,0 +1,51 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"fmt"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/notaryproject/notation-go/dir"
)
func TestLoadNonExistentFile(t *testing.T) {
dir.UserConfigDir = "testdata/valid"
var config string
err := load("non-existent", &config)
if err == nil {
t.Fatalf("load() expected error but not found")
}
}
func TestLoadSymlink(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
root := t.TempDir()
dir.UserConfigDir = root
fileName := "symlink"
os.Symlink("testdata/valid/config.json", filepath.Join(root, fileName))
expectedError := fmt.Sprintf("\"%s/%s\" is not a regular file (symlinks are not supported)", dir.UserConfigDir, fileName)
var config string
err := load(fileName, &config)
if err != nil && err.Error() != expectedError {
t.Fatalf("load() expected error= %s but found= %v", expectedError, err)
}
}

61
config/config.go Normal file
View File

@ -0,0 +1,61 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package config provides the ability to load and save config.json and
// signingkeys.json.
package config
import (
"errors"
"io/fs"
"github.com/notaryproject/notation-go/dir"
)
// Config reflects the config.json file.
// Specification: https://github.com/notaryproject/notation/pull/76
type Config struct {
InsecureRegistries []string `json:"insecureRegistries"`
CredentialsStore string `json:"credsStore,omitempty"`
CredentialHelpers map[string]string `json:"credHelpers,omitempty"`
// SignatureFormat defines the signature envelope type for signing
SignatureFormat string `json:"signatureFormat,omitempty"`
}
// NewConfig creates a new config file
func NewConfig() *Config {
return &Config{}
}
// Save stores the config to file
func (c *Config) Save() error {
path, err := dir.ConfigFS().SysPath(dir.PathConfigFile)
if err != nil {
return err
}
return save(path, c)
}
// LoadConfig reads the config from file or return a default config if not found.
func LoadConfig() (*Config, error) {
var config Config
err := load(dir.PathConfigFile, &config)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return NewConfig(), nil
}
return nil, err
}
return &config, nil
}

65
config/config_test.go Normal file
View File

@ -0,0 +1,65 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"reflect"
"testing"
"github.com/notaryproject/notation-go/dir"
)
var sampleConfig = &Config{
InsecureRegistries: []string{
"registry.wabbit-networks.io",
},
SignatureFormat: "jws",
}
func TestLoadFile(t *testing.T) {
dir.UserConfigDir = "./testdata/valid"
got, err := LoadConfig()
if err != nil {
t.Fatalf("LoadConfig() error. err = %v", err)
}
if !reflect.DeepEqual(got, sampleConfig) {
t.Errorf("loadFile() = %v, want %v", got, sampleConfig)
}
}
func TestSaveFile(t *testing.T) {
root := t.TempDir()
dir.UserConfigDir = root
sampleConfig.Save()
config, err := LoadConfig()
if err != nil {
t.Fatal("Load config file from temp dir failed")
}
if !reflect.DeepEqual(sampleConfig, config) {
t.Fatal("save config file failed.")
}
}
func TestLoadNonExistedConfig(t *testing.T) {
dir.UserConfigDir = "./testdata/non-existed"
got, err := LoadConfig()
if err != nil {
t.Fatalf("LoadConfig() error. err = %v", err)
}
if !reflect.DeepEqual(got, NewConfig()) {
t.Errorf("loadFile() = %v, want %v", got, NewConfig())
}
}

35
config/errors.go Normal file
View File

@ -0,0 +1,35 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"errors"
"fmt"
)
// ErrKeyNameEmpty is used when key name is empty.
var ErrKeyNameEmpty = errors.New("key name cannot be empty")
// KeyNotFoundError is used when key is not found in the signingkeys.json file.
type KeyNotFoundError struct {
KeyName string
}
// Error returns the error message.
func (e KeyNotFoundError) Error() string {
if e.KeyName != "" {
return fmt.Sprintf("signing key %s not found", e.KeyName)
}
return "signing key not found"
}

28
config/errors_test.go Normal file
View File

@ -0,0 +1,28 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import "testing"
func TestErrorKeyNotFound(t *testing.T) {
e := KeyNotFoundError{}
if e.Error() != "signing key not found" {
t.Fatalf("ErrorKeyNotFound.Error() = %v, want %v", e.Error(), "signing key not found")
}
e = KeyNotFoundError{KeyName: "testKey"}
if e.Error() != `signing key testKey not found` {
t.Fatalf("ErrorKeyNotFound.Error() = %v, want %v", e.Error(), "signing key testKey not found")
}
}

251
config/keys.go Normal file
View File

@ -0,0 +1,251 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"context"
"crypto/tls"
"errors"
"fmt"
"io/fs"
"github.com/notaryproject/notation-go/internal/slices"
"github.com/notaryproject/notation-go/log"
"github.com/notaryproject/notation-go/plugin"
"github.com/notaryproject/notation-go/dir"
set "github.com/notaryproject/notation-go/internal/container"
)
// X509KeyPair contains the paths of a public/private key pair files.
type X509KeyPair struct {
KeyPath string `json:"keyPath,omitempty"`
CertificatePath string `json:"certPath,omitempty"`
}
// ExternalKey contains the necessary information to delegate
// the signing operation to the named plugin.
type ExternalKey struct {
ID string `json:"id,omitempty"`
PluginName string `json:"pluginName,omitempty"`
PluginConfig map[string]string `json:"pluginConfig,omitempty"`
}
// KeySuite is a named key suite.
type KeySuite struct {
Name string `json:"name"`
*X509KeyPair
*ExternalKey
}
// SigningKeys reflects the signingkeys.json file.
type SigningKeys struct {
Default *string `json:"default,omitempty"`
Keys []KeySuite `json:"keys"`
}
// NewSigningKeys creates a new signingkeys config file
func NewSigningKeys() *SigningKeys {
return &SigningKeys{Keys: []KeySuite{}}
}
// Add adds new signing key
func (s *SigningKeys) Add(name, keyPath, certPath string, markDefault bool) error {
if name == "" {
return ErrKeyNameEmpty
}
_, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return err
}
ks := KeySuite{
Name: name,
X509KeyPair: &X509KeyPair{
KeyPath: keyPath,
CertificatePath: certPath,
},
}
return s.add(ks, markDefault)
}
// AddPlugin adds new plugin based signing key
func (s *SigningKeys) AddPlugin(ctx context.Context, keyName, id, pluginName string, pluginConfig map[string]string, markDefault bool) error {
logger := log.GetLogger(ctx)
logger.Debugf("Adding key with name %v and plugin name %v", keyName, pluginName)
if keyName == "" {
return ErrKeyNameEmpty
}
if id == "" {
return errors.New("missing key id")
}
if pluginName == "" {
return errors.New("plugin name cannot be empty")
}
mgr := plugin.NewCLIManager(dir.PluginFS())
_, err := mgr.Get(ctx, pluginName)
if err != nil {
return err
}
ks := KeySuite{
Name: keyName,
ExternalKey: &ExternalKey{
ID: id,
PluginName: pluginName,
PluginConfig: pluginConfig,
},
}
if err = s.add(ks, markDefault); err != nil {
logger.Error("Failed to add key with error: %v", err)
return err
}
logger.Debugf("Added key with name %s - {%+v}", keyName, ks)
return nil
}
// Get returns signing key for the given name
func (s *SigningKeys) Get(keyName string) (KeySuite, error) {
if keyName == "" {
return KeySuite{}, ErrKeyNameEmpty
}
idx := slices.IndexIsser(s.Keys, keyName)
if idx < 0 {
return KeySuite{}, KeyNotFoundError{KeyName: keyName}
}
return s.Keys[idx], nil
}
// GetDefault returns default signing key
func (s *SigningKeys) GetDefault() (KeySuite, error) {
if s.Default == nil {
return KeySuite{}, errors.New("default signing key not set." +
" Please set default signing key or specify a key name")
}
return s.Get(*s.Default)
}
// Remove deletes given signing keys and returns a slice of deleted key names
func (s *SigningKeys) Remove(keyName ...string) ([]string, error) {
var deletedNames []string
for _, name := range keyName {
if name == "" {
return deletedNames, ErrKeyNameEmpty
}
idx := slices.IndexIsser(s.Keys, name)
if idx < 0 {
return deletedNames, KeyNotFoundError{KeyName: name}
}
s.Keys = slices.Delete(s.Keys, idx)
deletedNames = append(deletedNames, name)
if s.Default != nil && *s.Default == name {
s.Default = nil
}
}
return deletedNames, nil
}
// UpdateDefault updates default signing key
func (s *SigningKeys) UpdateDefault(keyName string) error {
if keyName == "" {
return ErrKeyNameEmpty
}
if !slices.ContainsIsser(s.Keys, keyName) {
return KeyNotFoundError{KeyName: keyName}
}
s.Default = &keyName
return nil
}
// Save SigningKeys to signingkeys.json file
func (s *SigningKeys) Save() error {
path, err := dir.ConfigFS().SysPath(dir.PathSigningKeys)
if err != nil {
return err
}
if err := validateKeys(s); err != nil {
return err
}
return save(path, s)
}
// LoadSigningKeys reads the signingkeys.json file
// or return a default config if not found.
func LoadSigningKeys() (*SigningKeys, error) {
var config SigningKeys
err := load(dir.PathSigningKeys, &config)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return NewSigningKeys(), nil
}
return nil, err
}
if err := validateKeys(&config); err != nil {
return nil, err
}
return &config, nil
}
// LoadExecSaveSigningKeys loads signing key, executes given function and
// then saves the signing key
func LoadExecSaveSigningKeys(fn func(keys *SigningKeys) error) error {
// core process
signingKeys, err := LoadSigningKeys()
if err != nil {
return err
}
if err := fn(signingKeys); err != nil {
return err
}
return signingKeys.Save()
}
// Is checks whether the given name is equal with the Name variable
func (k KeySuite) Is(name string) bool {
return k.Name == name
}
func (s *SigningKeys) add(key KeySuite, markDefault bool) error {
if slices.ContainsIsser(s.Keys, key.Name) {
return fmt.Errorf("signing key with name %q already exists", key.Name)
}
s.Keys = append(s.Keys, key)
if markDefault {
s.Default = &key.Name
}
return nil
}
func validateKeys(config *SigningKeys) error {
keys := config.Keys
uniqueKeyNames := set.NewWithSize[string](len(keys))
for _, key := range keys {
if len(key.Name) == 0 {
return fmt.Errorf("malformed %s: key name cannot be empty", dir.PathSigningKeys)
}
if uniqueKeyNames.Contains(key.Name) {
return fmt.Errorf("malformed %s: multiple keys with name '%s' found", dir.PathSigningKeys, key.Name)
}
uniqueKeyNames.Add(key.Name)
}
if config.Default != nil {
defaultKey := *config.Default
if len(defaultKey) == 0 {
return fmt.Errorf("malformed %s: default key name cannot be empty", dir.PathSigningKeys)
}
if !uniqueKeyNames.Contains(defaultKey) {
return fmt.Errorf("malformed %s: default key '%s' not found", dir.PathSigningKeys, defaultKey)
}
}
return nil
}

456
config/keys_test.go Normal file
View File

@ -0,0 +1,456 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"context"
"crypto/x509"
"encoding/pem"
"errors"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/notaryproject/notation-core-go/testhelper"
"github.com/notaryproject/notation-go/dir"
)
var sampleSigningKeysInfo = SigningKeys{
Default: Ptr("wabbit-networks"),
Keys: []KeySuite{
{
Name: "wabbit-networks",
X509KeyPair: &X509KeyPair{
KeyPath: "/home/demo/.config/notation/localkeys/wabbit-networks.key",
CertificatePath: "/home/demo/.config/notation/localkeys/wabbit-networks.crt",
},
},
{
Name: "import.acme-rockets",
X509KeyPair: &X509KeyPair{
KeyPath: "/home/demo/.config/notation/localkeys/import.acme-rockets.key",
CertificatePath: "/home/demo/.config/notation/localkeys/import.acme-rockets.crt",
},
},
{
Name: "external-key",
ExternalKey: &ExternalKey{
ID: "id1",
PluginName: "pluginX",
PluginConfig: map[string]string{
"key": "value",
},
},
},
},
}
func TestLoadSigningKeysInfo(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
dir.UserConfigDir = "./testdata/valid"
got, err := LoadSigningKeys()
if err != nil {
t.Errorf("LoadSigningKeysInfo() error = \"%v\"", err)
return
}
if !reflect.DeepEqual(sampleSigningKeysInfo.Default, got.Default) {
t.Fatal("signingKeysInfo test failed.")
}
if !reflect.DeepEqual(sampleSigningKeysInfo.Keys, got.Keys) {
t.Fatal("signingKeysInfo test failed.")
}
})
t.Run("DuplicateKeys", func(t *testing.T) {
expectedErr := "malformed signingkeys.json: multiple keys with name 'wabbit-networks' found"
dir.UserConfigDir = "./testdata/malformed-duplicate"
_, err := LoadSigningKeys()
if err == nil || err.Error() != expectedErr {
t.Errorf("LoadSigningKeysInfo() error expected = \"%v\" but found = \"%v\"", expectedErr, err)
}
})
t.Run("InvalidDefault", func(t *testing.T) {
expectedErr := "malformed signingkeys.json: default key 'missing-default' not found"
dir.UserConfigDir = "./testdata/malformed-invalid-default"
_, err := LoadSigningKeys()
if err == nil || err.Error() != expectedErr {
t.Errorf("LoadSigningKeysInfo() error expected = \"%v\" but found = \"%v\"", expectedErr, err)
}
})
}
func TestSaveSigningKeys(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
root := t.TempDir()
dir.UserConfigDir = root
sampleSigningKeysInfo.Save()
info, err := LoadSigningKeys()
if err != nil {
t.Fatal("Load signingkeys.json from temp dir failed.")
}
if !reflect.DeepEqual(sampleSigningKeysInfo.Default, info.Default) {
t.Fatal("Save signingkeys.json failed.")
}
if !reflect.DeepEqual(sampleSigningKeysInfo.Keys, info.Keys) {
t.Fatal("Save signingkeys.json failed.")
}
})
t.Run("ValidWithoutDefault", func(t *testing.T) {
root := t.TempDir()
dir.UserConfigDir = root
sampleSigningKeysInfoNoDefault := deepCopySigningKeys(sampleSigningKeysInfo)
sampleSigningKeysInfoNoDefault.Default = nil
sampleSigningKeysInfoNoDefault.Save()
info, err := LoadSigningKeys()
if err != nil {
t.Fatal("Load signingkeys.json from temp dir failed.")
}
if !reflect.DeepEqual(sampleSigningKeysInfoNoDefault.Default, info.Default) {
t.Fatal("Save signingkeys.json failed.")
}
if !reflect.DeepEqual(sampleSigningKeysInfoNoDefault.Keys, info.Keys) {
t.Fatal("Save signingkeys.json failed.")
}
})
t.Run("DuplicateKeys", func(t *testing.T) {
expectedErr := "malformed signingkeys.json: multiple keys with name 'import.acme-rockets' found"
dir.UserConfigDir = t.TempDir()
duplicateKeySignKeysInfo := deepCopySigningKeys(sampleSigningKeysInfo)
duplicateKeySignKeysInfo.Keys = append(duplicateKeySignKeysInfo.Keys, KeySuite{
Name: "import.acme-rockets",
X509KeyPair: &X509KeyPair{
KeyPath: "/keypath",
CertificatePath: "/CertificatePath",
},
})
err := duplicateKeySignKeysInfo.Save()
if err == nil || err.Error() != expectedErr {
t.Errorf("Save signingkeys.json failed, error expected = \"%v\" but found = \"%v\"", expectedErr, err)
}
})
t.Run("EmptyKeyName", func(t *testing.T) {
expectedErr := "malformed signingkeys.json: key name cannot be empty"
dir.UserConfigDir = t.TempDir()
emptyKeyNameSignKeysInfo := deepCopySigningKeys(sampleSigningKeysInfo)
emptyKeyNameSignKeysInfo.Keys[0].Name = ""
err := emptyKeyNameSignKeysInfo.Save()
if err == nil || err.Error() != expectedErr {
t.Errorf("Save signingkeys.json failed, error expected = \"%v\" but found = \"%v\"", expectedErr, err)
}
})
t.Run("InvalidDefault", func(t *testing.T) {
expectedErr := "malformed signingkeys.json: default key 'missing-default' not found"
dir.UserConfigDir = t.TempDir()
invalidDefaultSignKeysInfo := deepCopySigningKeys(sampleSigningKeysInfo)
invalidDefaultSignKeysInfo.Default = Ptr("missing-default")
err := invalidDefaultSignKeysInfo.Save()
if err == nil || err.Error() != expectedErr {
t.Errorf("Save signingkeys.json failed, error expected = \"%v\" but found = \"%v\"", expectedErr, err)
}
expectedErr = "malformed signingkeys.json: default key name cannot be empty"
invalidDefaultSignKeysInfo.Default = Ptr("")
err = invalidDefaultSignKeysInfo.Save()
if err == nil || err.Error() != expectedErr {
t.Errorf("Save signingkeys.json failed, error expected = \"%v\" but found = \"%v\"", expectedErr, err)
}
})
}
func TestAdd(t *testing.T) {
certPath, keyPath := createTempCertKey(t)
t.Run("WithDefault", func(t *testing.T) {
testSigningKeys := deepCopySigningKeys(sampleSigningKeysInfo)
expectedTestKeyName := "name1"
if err := testSigningKeys.Add(expectedTestKeyName, keyPath, certPath, true); err != nil {
t.Errorf("Add() failed with err= %v", err)
}
expectedSigningKeys := append(deepCopySigningKeys(sampleSigningKeysInfo).Keys, KeySuite{
Name: expectedTestKeyName,
X509KeyPair: &X509KeyPair{
KeyPath: keyPath,
CertificatePath: certPath,
},
})
if expectedTestKeyName != *testSigningKeys.Default {
t.Error("Add() failed, incorrect default key")
}
if !reflect.DeepEqual(testSigningKeys.Keys, expectedSigningKeys) {
t.Error("Add() failed, KeySuite mismatch")
}
})
t.Run("WithoutDefault", func(t *testing.T) {
dir.UserConfigDir = t.TempDir()
testSigningKeys := deepCopySigningKeys(sampleSigningKeysInfo)
expectedTestKeyName := "name2"
certPath, keyPath := createTempCertKey(t)
if err := testSigningKeys.Add(expectedTestKeyName, keyPath, certPath, false); err != nil {
t.Errorf("Add() failed with err= %v", err)
}
expectedSigningKeys := append(deepCopySigningKeys(sampleSigningKeysInfo).Keys, KeySuite{
Name: expectedTestKeyName,
X509KeyPair: &X509KeyPair{
KeyPath: keyPath,
CertificatePath: certPath,
},
})
if *sampleSigningKeysInfo.Default != *testSigningKeys.Default {
t.Error("Add() failed, default key changed")
}
if !reflect.DeepEqual(testSigningKeys.Keys, expectedSigningKeys) {
t.Error("Add() failed, KeySuite mismatch")
}
})
t.Run("InvalidCertKeyLocation", func(t *testing.T) {
err := sampleSigningKeysInfo.Add("name1", "invalid", "invalid", true)
if err == nil {
t.Error("expected Add() to fail for invalid cert and key location")
}
})
t.Run("InvalidName", func(t *testing.T) {
err := sampleSigningKeysInfo.Add("", "invalid", "invalid", true)
if err == nil {
t.Error("expected Add() to fail for empty key name")
}
})
t.Run("InvalidName", func(t *testing.T) {
err := sampleSigningKeysInfo.Add("", "invalid", "invalid", true)
if err == nil {
t.Error("expected Add() to fail for empty key name")
}
})
t.Run("DuplicateKey", func(t *testing.T) {
err := sampleSigningKeysInfo.Add(sampleSigningKeysInfo.Keys[0].Name, "invalid", "invalid", true)
if err == nil {
t.Error("expected Add() to fail for duplicate name")
}
})
}
func TestPluginAdd(t *testing.T) {
config := map[string]string{"key1": "value1"}
name := "name1"
id := "pluginId1"
pluginName := "pluginName1"
t.Run("InvalidCertKeyLocation", func(t *testing.T) {
err := sampleSigningKeysInfo.Add("name1", "invalid", "invalid", true)
if err == nil {
t.Error("expected AddPlugin() to fail for invalid cert and key location")
}
})
t.Run("InvalidName", func(t *testing.T) {
err := sampleSigningKeysInfo.AddPlugin(context.Background(), "", id, pluginName, config, true)
if err == nil {
t.Error("expected AddPlugin() to fail for empty key name")
}
})
t.Run("InvalidId", func(t *testing.T) {
err := sampleSigningKeysInfo.AddPlugin(context.Background(), name, "", pluginName, config, true)
if err == nil {
t.Error("expected AddPlugin() to fail for empty key name")
}
})
t.Run("InvalidPluginName", func(t *testing.T) {
err := sampleSigningKeysInfo.AddPlugin(context.Background(), name, id, "", config, true)
if err == nil {
t.Error("AddPlugin AddPlugin() to fail for empty plugin name")
}
})
}
func TestGet(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
key, err := sampleSigningKeysInfo.Get("external-key")
if err != nil {
t.Errorf("Get() failed with error= %v", err)
}
if !reflect.DeepEqual(key, sampleSigningKeysInfo.Keys[2]) {
t.Errorf("Get() returned %v but expected %v", key, sampleSigningKeysInfo.Keys[2])
}
})
t.Run("NonExistent", func(t *testing.T) {
_, err := sampleSigningKeysInfo.Get("nonExistent")
if err == nil {
t.Error("expected Get() to fail for nonExistent key name")
}
if !errors.Is(err, KeyNotFoundError{KeyName: "nonExistent"}) {
t.Error("expected Get() to return ErrorKeyNotFound")
}
})
t.Run("EmptyName", func(t *testing.T) {
_, err := sampleSigningKeysInfo.Get("")
if err == nil {
t.Error("expected Get() to fail for empty key name")
}
if !errors.Is(err, ErrKeyNameEmpty) {
t.Error("expected Get() to return ErrorKeyNameEmpty")
}
})
}
func TestGetDefault(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
key, err := sampleSigningKeysInfo.GetDefault()
if err != nil {
t.Errorf("GetDefault() failed with error= %v", err)
}
if !reflect.DeepEqual(key.Name, *sampleSigningKeysInfo.Default) {
t.Errorf("GetDefault() returned %s but expected %s", key.Name, *sampleSigningKeysInfo.Default)
}
})
t.Run("NoDefault", func(t *testing.T) {
testSigningKeysInfo := deepCopySigningKeys(sampleSigningKeysInfo)
testSigningKeysInfo.Default = nil
if _, err := testSigningKeysInfo.GetDefault(); err == nil {
t.Error("GetDefault Get() to fail there is no defualt key")
}
})
}
func TestUpdateDefault(t *testing.T) {
t.Run("Valid", func(t *testing.T) {
testSigningKeysInfo := deepCopySigningKeys(sampleSigningKeysInfo)
newDefault := sampleSigningKeysInfo.Keys[1].Name
err := testSigningKeysInfo.UpdateDefault(newDefault)
if err != nil {
t.Errorf("UpdateDefault() failed with error= %v", err)
}
if !reflect.DeepEqual(newDefault, *testSigningKeysInfo.Default) {
t.Errorf("UpdateDefault() didn't update default key")
}
})
t.Run("NonExistent", func(t *testing.T) {
err := sampleSigningKeysInfo.UpdateDefault("nonExistent")
if err == nil {
t.Error("expected Get() to fail for nonExistent key name")
}
if !errors.Is(err, KeyNotFoundError{KeyName: "nonExistent"}) {
t.Error("expected Get() to return ErrorKeyNotFound")
}
})
t.Run("EmptyName", func(t *testing.T) {
err := sampleSigningKeysInfo.UpdateDefault("")
if err == nil {
t.Error("expected Get() to fail for empty key name")
}
if !errors.Is(err, ErrKeyNameEmpty) {
t.Error("expected Get() to return ErrorKeyNameEmpty")
}
})
}
func TestRemove(t *testing.T) {
testKeyName := "wabbit-networks"
testSigningKeysInfo := deepCopySigningKeys(sampleSigningKeysInfo)
t.Run("Valid", func(t *testing.T) {
keys, err := testSigningKeysInfo.Remove(testKeyName)
if err != nil {
t.Errorf("testSigningKeysInfo() failed with error= %v", err)
}
if _, err := testSigningKeysInfo.Get(testKeyName); err == nil {
t.Error("Delete() filed to delete key")
}
if keys[0] != testKeyName {
t.Error("Delete() deleted key name mismatch")
}
})
t.Run("NonExistent", func(t *testing.T) {
_, err := testSigningKeysInfo.Remove("nonExistent")
if err == nil {
t.Error("expected Get() to fail for nonExistent key name")
}
if !errors.Is(err, KeyNotFoundError{KeyName: "nonExistent"}) {
t.Error("expected Get() to return ErrorKeyNotFound")
}
})
t.Run("EmptyName", func(t *testing.T) {
_, err := testSigningKeysInfo.Remove("")
if err == nil {
t.Error("expected Get() to fail for empty key name")
}
if !errors.Is(err, ErrKeyNameEmpty) {
t.Error("expected Get() to return ErrorKeyNameEmpty")
}
})
}
func deepCopySigningKeys(keys SigningKeys) SigningKeys {
cpyKeys := make([]KeySuite, len(sampleSigningKeysInfo.Keys))
copy(cpyKeys, keys.Keys)
cpyDefault := *keys.Default
cpySignKeys := keys
cpySignKeys.Default = &cpyDefault
cpySignKeys.Keys = cpyKeys
return cpySignKeys
}
func Ptr[T any](v T) *T {
return &v
}
func createTempCertKey(t *testing.T) (string, string) {
certTuple := testhelper.GetRSARootCertificate()
certPath := filepath.Join(t.TempDir(), "cert.tmp")
certData := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certTuple.Cert.Raw})
if err := os.WriteFile(certPath, certData, 0600); err != nil {
panic(err)
}
keyPath := filepath.Join(t.TempDir(), "key.tmp")
keyBytes, _ := x509.MarshalPKCS8PrivateKey(certTuple.PrivateKey)
keyData := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
if err := os.WriteFile(keyPath, keyData, 0600); err != nil {
panic(err)
}
return certPath, keyPath
}

View File

@ -0,0 +1,23 @@
{
"default": "wabbit-networks",
"keys": [
{
"name": "wabbit-networks",
"keyPath": "/home/demo/.config/notation/localkeys/wabbit-networks.key",
"certPath": "/home/demo/.config/notation/localkeys/wabbit-networks.crt"
},
{
"name": "wabbit-networks",
"keyPath": "/home/demo/.config/notation/localkeys/import.acme-rockets.key",
"certPath": "/home/demo/.config/notation/localkeys/import.acme-rockets.crt"
},
{
"name": "external-key",
"id": "id1",
"pluginName": "pluginX",
"pluginConfig": {
"key": "value"
}
}
]
}

View File

@ -0,0 +1,23 @@
{
"default": "missing-default",
"keys": [
{
"name": "wabbit-networks",
"keyPath": "/home/demo/.config/notation/localkeys/wabbit-networks.key",
"certPath": "/home/demo/.config/notation/localkeys/wabbit-networks.crt"
},
{
"name": "import.acme-rockets",
"keyPath": "/home/demo/.config/notation/localkeys/import.acme-rockets.key",
"certPath": "/home/demo/.config/notation/localkeys/import.acme-rockets.crt"
},
{
"name": "external-key",
"id": "id1",
"pluginName": "pluginX",
"pluginConfig": {
"key": "value"
}
}
]
}

6
config/testdata/valid/config.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"insecureRegistries": [
"registry.wabbit-networks.io"
],
"signatureFormat": "jws"
}

23
config/testdata/valid/signingkeys.json vendored Normal file
View File

@ -0,0 +1,23 @@
{
"default": "wabbit-networks",
"keys": [
{
"name": "wabbit-networks",
"keyPath": "/home/demo/.config/notation/localkeys/wabbit-networks.key",
"certPath": "/home/demo/.config/notation/localkeys/wabbit-networks.crt"
},
{
"name": "import.acme-rockets",
"keyPath": "/home/demo/.config/notation/localkeys/import.acme-rockets.key",
"certPath": "/home/demo/.config/notation/localkeys/import.acme-rockets.crt"
},
{
"name": "external-key",
"id": "id1",
"pluginName": "pluginX",
"pluginConfig": {
"key": "value"
}
}
]
}

View File

@ -1,31 +0,0 @@
package cryptoutil
import (
"crypto/x509"
"encoding/pem"
"os"
)
// ReadCertificateFile reads a certificate PEM file.
func ReadCertificateFile(path string) ([]*x509.Certificate, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return ParseCertificatePEM(data)
}
// ParseCertificatePEM parses a certificate PEM.
func ParseCertificatePEM(data []byte) ([]*x509.Certificate, error) {
var certs []*x509.Certificate
block, rest := pem.Decode(data)
for block != nil {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
certs = append(certs, cert)
block, rest = pem.Decode(rest)
}
return certs, nil
}

View File

@ -1,36 +0,0 @@
package cryptoutil
import (
"crypto"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os"
)
// ReadPrivateKeyFile reads a key PEM file as a signing key.
func ReadPrivateKeyFile(path string) (crypto.PrivateKey, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return ParsePrivateKeyPEM(data)
}
// ParsePrivateKeyPEM parses a PEM as a signing key.
func ParsePrivateKeyPEM(data []byte) (crypto.PrivateKey, error) {
block, _ := pem.Decode(data)
if block == nil {
return nil, errors.New("no PEM data found")
}
switch block.Type {
case "PRIVATE KEY":
return x509.ParsePKCS8PrivateKey(block.Bytes)
case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(block.Bytes)
case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(block.Bytes)
}
return nil, fmt.Errorf("unsupported PEM block type: %s", block.Type)
}

View File

@ -1,53 +0,0 @@
package jwsutil
import "encoding/json"
// Envelope contains a common payload signed by multiple signatures.
type Envelope struct {
Payload string `json:"payload,omitempty"`
Signatures []Signature `json:"signatures,omitempty"`
}
// Size returns the number of enclosed signatures.
func (e Envelope) Size() int {
return len(e.Signatures)
}
// Open opens the evelope and returns the first or default complete signature.
func (e Envelope) Open() CompleteSignature {
if len(e.Signatures) == 0 {
return CompleteSignature{
Payload: e.Payload,
}
}
return CompleteSignature{
Payload: e.Payload,
Signature: e.Signatures[0],
}
}
// UnmarshalJSON parses the JSON serialized JWS.
// Reference: RFC 7515 7.2 JWS JSON Serialization.
func (e *Envelope) UnmarshalJSON(data []byte) error {
var combined struct {
CompleteSignature
Signatures []Signature `json:"signatures"`
}
if err := json.Unmarshal(data, &combined); err != nil {
return ErrInvalidJSONSerialization
}
if len(combined.Signatures) == 0 {
*e = Envelope{
Payload: combined.Payload,
Signatures: []Signature{
combined.Signature,
},
}
} else {
*e = Envelope{
Payload: combined.Payload,
Signatures: combined.Signatures,
}
}
return nil
}

View File

@ -1,102 +0,0 @@
package jwsutil
import (
"encoding/json"
"reflect"
"testing"
)
func TestEnvelope_UnmarshalJSON(t *testing.T) {
tests := []struct {
name string
data string
want Envelope
}{
{
name: "General JWS JSON Serialization Syntax (multiple signatures)",
data: `{
"payload": "test payload",
"signatures": [
{
"protected": "protected foo",
"header": {"unprotected": "foo"},
"signature": "signature foo"
},
{
"protected": "protected bar",
"header": {"unprotected": "bar"},
"signature": "signature bar"
}
]
}`,
want: Envelope{
Payload: "test payload",
Signatures: []Signature{
{
Protected: "protected foo",
Unprotected: []byte(`{"unprotected": "foo"}`),
Signature: "signature foo",
},
{
Protected: "protected bar",
Unprotected: []byte(`{"unprotected": "bar"}`),
Signature: "signature bar",
},
},
},
},
{
name: "General JWS JSON Serialization Syntax (single signature)",
data: `{
"payload": "test payload",
"signatures": [
{
"protected": "protected foo",
"header": {"unprotected": "foo"},
"signature": "signature foo"
}
]
}`,
want: Envelope{
Payload: "test payload",
Signatures: []Signature{
{
Protected: "protected foo",
Unprotected: []byte(`{"unprotected": "foo"}`),
Signature: "signature foo",
},
},
},
},
{
name: "Flattened JWS JSON Serialization Syntax",
data: `{
"payload": "test payload",
"protected": "protected foo",
"header": {"unprotected": "foo"},
"signature": "signature foo"
}`,
want: Envelope{
Payload: "test payload",
Signatures: []Signature{
{
Protected: "protected foo",
Unprotected: []byte(`{"unprotected": "foo"}`),
Signature: "signature foo",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got Envelope
if err := json.Unmarshal([]byte(tt.data), &got); err != nil {
t.Fatalf("Envelope.UnmarshalJSON() error = %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Envelope.UnmarshalJSON() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -1,9 +0,0 @@
package jwsutil
import "errors"
// Common errors
var (
ErrInvalidCompactSerialization = errors.New("invalid compact serialization")
ErrInvalidJSONSerialization = errors.New("invalid JSON serialization")
)

View File

@ -1,56 +0,0 @@
// Package jwsutil provides serialization utilities for JWT libraries to comfort JWS.
// Reference: RFC 7515 JSON Web Signature (JWS).
package jwsutil
import (
"encoding/json"
"strings"
)
// Signature represents a detached signature.
type Signature struct {
Protected string `json:"protected,omitempty"`
Unprotected json.RawMessage `json:"header,omitempty"`
Signature string `json:"signature,omitempty"`
}
// CompleteSignature represents a clear signed signature.
// A CompleteSignature can be viewed as an envelope with a single signature in
// flattened JWS JSON serialization syntax.
// Reference: RFC 7515 7.2 JWS JSON Serialization.
type CompleteSignature struct {
Payload string `json:"payload,omitempty"`
Signature
}
// Parse parses the compact serialized JWS.
// Reference: RFC 7515 7.1 JWS Compact Serialization.
func ParseCompact(serialized string) (CompleteSignature, error) {
parts := strings.Split(serialized, ".")
if len(parts) != 3 {
return CompleteSignature{}, ErrInvalidCompactSerialization
}
return CompleteSignature{
Payload: parts[1],
Signature: Signature{
Protected: parts[0],
Signature: parts[2],
},
}, nil
}
// SerializeCompact serialize the signature in JWS Compact Serialization
// Reference: RFC 7515 7.1 JWS Compact Serialization.
func (s CompleteSignature) SerializeCompact() string {
return strings.Join([]string{s.Protected, s.Payload, s.Signature.Signature}, ".")
}
// Enclose packs the signature into an envelope.
func (s CompleteSignature) Enclose() Envelope {
return Envelope{
Payload: s.Payload,
Signatures: []Signature{
s.Signature,
},
}
}

View File

@ -1,78 +0,0 @@
package timestamp
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/url"
)
// maxBodyLength specifies the max content can be received from the possibly malicious
// remote server.
// The legnth of a regular TSA response with certificates is usually less than 10 KiB.
const maxBodyLength = 1 * 1024 * 1024 // 1 MiB
// httpTimestamper is a HTTP-based timestamper.
type httpTimestamper struct {
rt http.RoundTripper
endpoint string
}
// NewHTTPTimestamper creates a HTTP-based timestamper with the endpoint provided by the TSA.
// http.DefaultTransport is used if nil RoundTripper is passed.
func NewHTTPTimestamper(rt http.RoundTripper, endpoint string) (Timestamper, error) {
if rt == nil {
rt = http.DefaultTransport
}
if _, err := url.Parse(endpoint); err != nil {
return nil, err
}
return &httpTimestamper{
rt: rt,
endpoint: endpoint,
}, nil
}
// Timestamp sends the request to the remote TSA server for timestamping.
// Reference: RFC 3161 3.4 Time-Stamp Protocol via HTTP
func (ts *httpTimestamper) Timestamp(ctx context.Context, req *Request) (*Response, error) {
// prepare for http request
reqBytes, err := req.MarshalBinary()
if err != nil {
return nil, err
}
hReq, err := http.NewRequestWithContext(ctx, http.MethodPost, ts.endpoint, bytes.NewReader(reqBytes))
if err != nil {
return nil, err
}
hReq.Header.Set("Content-Type", "application/timestamp-query")
// send the request to the remote TSA server
hResp, err := ts.rt.RoundTrip(hReq)
if err != nil {
return nil, err
}
defer hResp.Body.Close()
// verify HTTP response
if hResp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status: %s", hResp.Status)
}
if contentType := hResp.Header.Get("Content-Type"); contentType != "application/timestamp-reply" {
return nil, fmt.Errorf("unexpected response content type: %s", contentType)
}
// read response
body := io.LimitReader(hResp.Body, maxBodyLength)
respBytes, err := io.ReadAll(body)
if err != nil {
return nil, err
}
var resp Response
if err := resp.UnmarshalBinary(respBytes); err != nil {
return nil, err
}
return &resp, nil
}

View File

@ -1,250 +0,0 @@
package timestamp
import (
"bytes"
"context"
"crypto"
"crypto/x509"
"encoding/asn1"
"io"
"net/http"
"net/http/httptest"
"os"
"reflect"
"testing"
"time"
"github.com/notaryproject/notation-go/internal/crypto/hashutil"
"github.com/notaryproject/notation-go/internal/crypto/pki"
)
var testRequest = []byte{
// Request
0x30, 0x37,
// Version
0x02, 0x01, 0x01,
// MessageImprint
0x30, 0x2f,
// MessageImprint.HashAlgorithm
0x30, 0x0b,
// MessageImprint.HashAlgorithm.Algorithm
0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
// MessageImprint.HashedMessage
0x04, 0x20,
0x83, 0x26, 0xf4, 0x70, 0x9d, 0x40, 0x1d, 0xfa, 0xbf, 0xa7, 0x83, 0x02, 0xfb, 0x1c, 0xde, 0xa0,
0xf1, 0x80, 0x48, 0xa4, 0x40, 0x40, 0xc2, 0x12, 0xbd, 0x8e, 0x28, 0xda, 0x6b, 0xc6, 0x51, 0xc7,
// CertReq
0x01, 0x01, 0xff,
}
func TestHTTPTimestampGranted(t *testing.T) {
// setup test server
testResp, err := os.ReadFile("testdata/granted.tsq")
if err != nil {
t.Fatal("failed to read test response:", err)
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
const wantContentType = "application/timestamp-query"
if got := r.Header.Get("Content-Type"); got != wantContentType {
t.Fatalf("TimeStampRequest.ContentType = %v, want %v", err, wantContentType)
}
if got, err := io.ReadAll(r.Body); err != nil {
t.Fatalf("TimeStampRequest.Body read error = %v", err)
} else if !bytes.Equal(got, testRequest) {
t.Fatalf("TimeStampRequest.Body = %v, want %v", got, testRequest)
}
// write reply
w.Header().Set("Content-Type", "application/timestamp-reply")
if _, err := w.Write(testResp); err != nil {
t.Error("failed to write response:", err)
}
}))
defer ts.Close()
// do timestamp
tsa, err := NewHTTPTimestamper(nil, ts.URL)
if err != nil {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
message := []byte("notation")
req, err := NewRequestFromBytes(message)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
}
ctx := context.Background()
resp, err := tsa.Timestamp(ctx, req)
if err != nil {
t.Fatalf("httpTimestamper.Timestamp() error = %v", err)
}
wantStatus := pki.StatusGranted
if got := resp.Status.Status; got != wantStatus {
t.Fatalf("Response.Status = %v, want %v", got, wantStatus)
}
// verify timestamp token
token, err := resp.SignedToken()
if err != nil {
t.Fatalf("Response.SignedToken() error = %v", err)
}
roots := x509.NewCertPool()
rootCABytes, err := os.ReadFile("testdata/GlobalSignRootCA.crt")
if err != nil {
t.Fatal("failed to read root CA certificate:", err)
}
if ok := roots.AppendCertsFromPEM(rootCABytes); !ok {
t.Fatal("failed to load root CA certificate")
}
opts := x509.VerifyOptions{
Roots: roots,
CurrentTime: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
}
certs, err := token.Verify(opts)
if err != nil {
t.Fatal("SignedToken.Verify() error =", err)
}
if got := len(certs); got != 1 {
t.Fatalf("SignedToken.Verify() len([]*x509.Certificate) = %v, want %v", got, 1)
}
certThumbprint, err := hashutil.ComputeHash(crypto.SHA256, certs[0].Raw)
if err != nil {
t.Fatal("failed to compute certificate thumbprint:", err)
}
wantCertThumbprint := []byte{
0x13, 0xd6, 0xe9, 0xc4, 0x20, 0xff, 0x6d, 0x4e, 0x27, 0x54, 0x72, 0x8c, 0x68, 0xe7, 0x78, 0x82,
0x65, 0x64, 0x67, 0xdb, 0x9a, 0x19, 0x0f, 0x81, 0x65, 0x97, 0xf6, 0x7f, 0xb6, 0xcc, 0xc6, 0xf9,
}
if !bytes.Equal(certThumbprint, wantCertThumbprint) {
t.Fatalf("SignedToken.Verify() = %v, want %v", certThumbprint, wantCertThumbprint)
}
info, err := token.Info()
if err != nil {
t.Fatal("SignedToken.Info() error =", err)
}
if err := info.Verify(message); err != nil {
t.Errorf("TSTInfo.Verify() error = %v", err)
}
timestamp, accuracy := info.Timestamp()
wantTimestamp := time.Date(2021, 9, 18, 11, 54, 34, 0, time.UTC)
if timestamp != wantTimestamp {
t.Errorf("TSTInfo.Timestamp() Timestamp = %v, want %v", timestamp, wantTimestamp)
}
wantAccuracy := time.Second
if accuracy != wantAccuracy {
t.Errorf("TSTInfo.Timestamp() Accuracy = %v, want %v", accuracy, wantAccuracy)
}
}
func TestHTTPTimestampRejection(t *testing.T) {
// setup test server
testResp, err := os.ReadFile("testdata/rejection.tsq")
if err != nil {
t.Fatal("failed to read test response:", err)
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
const wantContentType = "application/timestamp-query"
if got := r.Header.Get("Content-Type"); got != wantContentType {
t.Fatalf("TimeStampRequest.ContentType = %v, want %v", err, wantContentType)
}
if got, err := io.ReadAll(r.Body); err != nil {
t.Fatalf("TimeStampRequest.Body read error = %v", err)
} else if !bytes.Equal(got, testRequest) {
t.Fatalf("TimeStampRequest.Body = %v, want %v", got, testRequest)
}
// write reply
w.Header().Set("Content-Type", "application/timestamp-reply")
if _, err := w.Write(testResp); err != nil {
t.Error("failed to write response:", err)
}
}))
defer ts.Close()
// do timestamp
tsa, err := NewHTTPTimestamper(nil, ts.URL)
if err != nil {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
message := []byte("notation")
req, err := NewRequestFromBytes(message)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
}
ctx := context.Background()
resp, err := tsa.Timestamp(ctx, req)
if err != nil {
t.Fatalf("httpTimestamper.Timestamp() error = %v", err)
}
wantStatus := pki.StatusRejection
if got := resp.Status.Status; got != wantStatus {
t.Fatalf("Response.Status = %v, want %v", got, wantStatus)
}
wantStatusString := []string{"request contains unknown algorithm"}
if got := resp.Status.StatusString; !reflect.DeepEqual(got, wantStatusString) {
t.Fatalf("Response.StatusString = %v, want %v", got, wantStatusString)
}
wantFailInfo := asn1.BitString{
Bytes: []byte{0x80},
BitLength: 1,
}
if got := resp.Status.FailInfo; !reflect.DeepEqual(got, wantFailInfo) {
t.Fatalf("Response.FailInfo = %v, want %v", got, wantFailInfo)
}
}
func TestHTTPTimestampBadEndpoint(t *testing.T) {
// setup test server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// write reply
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if _, err := w.Write([]byte("{}")); err != nil {
t.Error("failed to write response:", err)
}
}))
defer ts.Close()
// do timestamp
tsa, err := NewHTTPTimestamper(nil, ts.URL)
if err != nil {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
req, err := NewRequestFromString("notation")
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
}
ctx := context.Background()
_, err = tsa.Timestamp(ctx, req)
if err == nil {
t.Fatalf("httpTimestamper.Timestamp() error = %v, wantErr %v", err, true)
}
}
func TestHTTPTimestampEndpointNotFound(t *testing.T) {
// setup test server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}))
defer ts.Close()
// do timestamp
tsa, err := NewHTTPTimestamper(nil, ts.URL)
if err != nil {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
req, err := NewRequestFromString("notation")
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
}
ctx := context.Background()
_, err = tsa.Timestamp(ctx, req)
if err == nil {
t.Fatalf("httpTimestamper.Timestamp() error = %v, wantErr %v", err, true)
}
}

View File

@ -1,99 +0,0 @@
package timestamp
import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"errors"
"math/big"
"github.com/notaryproject/notation-go/internal/crypto/oid"
digest "github.com/opencontainers/go-digest"
)
// MessageImprint contains the hash of the datum to be time-stamped.
// MessageImprint ::= SEQUENCE {
// hashAlgorithm AlgorithmIdentifier,
// hashedMessage OCTET STRING }
type MessageImprint struct {
HashAlgorithm pkix.AlgorithmIdentifier
HashedMessage []byte
}
// Request is a time-stamping request.
// TimeStampReq ::= SEQUENCE {
// version INTEGER { v1(1) },
// messageImprint MessageImprint,
// reqPolicy TSAPolicyID OPTIONAL,
// nonce INTEGER OPTIONAL,
// certReq BOOLEAN DEFAULT FALSE,
// extensions [0] IMPLICIT Extensions OPTIONAL }
type Request struct {
Version int // fixed to 1 as defined in RFC 3161 2.4.1 Request Format
MessageImprint MessageImprint
ReqPolicy asn1.ObjectIdentifier `asn1:"optional"`
Nonce *big.Int `asn1:"optional"`
CertReq bool `asn1:"optional,default:false"`
Extensions []pkix.Extension `asn1:"optional,tag:0"`
}
// NewRequest creates a request based on the given digest.
func NewRequest(contentDigest digest.Digest) (*Request, error) {
hashAlgorithm, err := getOIDFromDigestAlgorithm(contentDigest.Algorithm())
if err != nil {
return nil, err
}
hashedMessage, err := hex.DecodeString(contentDigest.Encoded())
if err != nil {
return nil, err
}
return &Request{
Version: 1,
MessageImprint: MessageImprint{
HashAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: hashAlgorithm,
},
HashedMessage: hashedMessage,
},
CertReq: true,
}, nil
}
// NewRequestFromBytes creates a request based on the given byte slice.
func NewRequestFromBytes(content []byte) (*Request, error) {
return NewRequest(digest.FromBytes(content))
}
// NewRequestFromString creates a request based on the given string.
func NewRequestFromString(content string) (*Request, error) {
return NewRequest(digest.FromString(content))
}
// MarshalBinary encodes the request to binary form.
// This method implements encoding.BinaryMarshaler
func (r *Request) MarshalBinary() ([]byte, error) {
if r == nil {
return nil, errors.New("nil request")
}
return asn1.Marshal(*r)
}
// UnmarshalBinary decodes the request from binary form.
// This method implements encoding.BinaryUnmarshaler
func (r *Request) UnmarshalBinary(data []byte) error {
_, err := asn1.Unmarshal(data, r)
return err
}
// getOIDFromDigestAlgorithm returns corresponding ASN.1 OID for the given digest algorithm.
func getOIDFromDigestAlgorithm(alg digest.Algorithm) (asn1.ObjectIdentifier, error) {
switch alg {
case digest.SHA256:
return oid.SHA256, nil
case digest.SHA384:
return oid.SHA384, nil
case digest.SHA512:
return oid.SHA512, nil
}
return nil, digest.ErrDigestUnsupported
}

View File

@ -1,44 +0,0 @@
package timestamp
import (
"encoding/asn1"
"errors"
"github.com/notaryproject/notation-go/internal/crypto/pki"
)
// Response is a time-stamping response.
// TimeStampResp ::= SEQUENCE {
// status PKIStatusInfo,
// timeStampToken TimeStampToken OPTIONAL }
type Response struct {
Status pki.StatusInfo
TimeStampToken asn1.RawValue `asn1:"optional"`
}
// MarshalBinary encodes the response to binary form.
// This method implements encoding.BinaryMarshaler
func (r *Response) MarshalBinary() ([]byte, error) {
if r == nil {
return nil, errors.New("nil response")
}
return asn1.Marshal(r)
}
// UnmarshalBinary decodes the response from binary form.
// This method implements encoding.BinaryUnmarshaler
func (r *Response) UnmarshalBinary(data []byte) error {
_, err := asn1.Unmarshal(data, r)
return err
}
// TokenBytes returns the bytes of the timestamp token.
func (r *Response) TokenBytes() []byte {
return r.TimeStampToken.FullBytes
}
// SignedToken returns the timestamp token with signatures.
// Callers should invoke Verify to verify the content before comsumption.
func (r *Response) SignedToken() (*SignedToken, error) {
return ParseSignedToken(r.TokenBytes())
}

View File

@ -1 +0,0 @@
0/0-0$ "request contains unknown algorithm

View File

@ -1,11 +0,0 @@
// Package timestamp generates timestamping requests to TSA servers,
// and fetches the responses according to RFC 3161.
package timestamp
import "context"
// Timestamper stamps the time.
type Timestamper interface {
// Timestamp stamps the time with the given request.
Timestamp(context.Context, *Request) (*Response, error)
}

View File

@ -1,269 +0,0 @@
// Package timestamptest provides utilities for timestamp testing
package timestamptest
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"math"
"math/big"
"time"
"github.com/notaryproject/notation-go/crypto/timestamp"
"github.com/notaryproject/notation-go/internal/crypto/cms"
"github.com/notaryproject/notation-go/internal/crypto/hashutil"
"github.com/notaryproject/notation-go/internal/crypto/oid"
"github.com/notaryproject/notation-go/internal/crypto/pki"
)
// responseRejection is a general response for request rejection.
var responseRejection = &timestamp.Response{
Status: pki.StatusInfo{
Status: pki.StatusRejection,
},
}
// TSA is a Timestamping Authority for testing purpose.
type TSA struct {
// key is the TSA signing key.
key *rsa.PrivateKey
// cert is the self-signed certificate by the TSA signing key.
cert *x509.Certificate
// NowFunc provides the current time. time.Now() is used if nil.
NowFunc func() time.Time
}
// NewTSA creates a TSA with random credentials.
func NewTSA() (*TSA, error) {
// generate key
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
// generate certificate
serialNumber, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
return nil, err
}
now := time.Now()
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "timestamp test",
},
NotBefore: now,
NotAfter: now.Add(365 * 24 * time.Hour), // 1 year
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping},
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, key.Public(), key)
if err != nil {
return nil, err
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, err
}
return &TSA{
key: key,
cert: cert,
}, nil
}
// Certificate returns the certificate used by the server.
func (tsa *TSA) Certificate() *x509.Certificate {
return tsa.cert
}
// Timestamp stamps the time with the given request.
func (tsa *TSA) Timestamp(_ context.Context, req *timestamp.Request) (*timestamp.Response, error) {
// validate request
if req.Version != 1 {
return responseRejection, nil
}
hash, ok := oid.ConvertToHash(req.MessageImprint.HashAlgorithm.Algorithm)
if !ok {
return responseRejection, nil
}
if hashedMessage := req.MessageImprint.HashedMessage; len(hashedMessage) != hash.Size() {
return responseRejection, nil
}
// generate token info
policy := asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 2} // time-stamp-policies
switch hash {
case crypto.SHA1:
policy = append(policy, 2)
case crypto.SHA256, crypto.SHA384, crypto.SHA512:
policy = append(policy, 3)
default:
return responseRejection, nil
}
infoBytes, err := tsa.generateTokenInfo(req, policy)
if err != nil {
return nil, err
}
// generate signed data
signed, err := tsa.generateSignedData(infoBytes, req.CertReq)
if err != nil {
return nil, err
}
content, err := convertToRawASN1(signed, "explicit,tag:0")
if err != nil {
return nil, err
}
// generate content info
contentInfo := cms.ContentInfo{
ContentType: oid.SignedData,
Content: content,
}
token, err := convertToRawASN1(contentInfo, "")
if err != nil {
return nil, err
}
// generate response
return &timestamp.Response{
Status: pki.StatusInfo{
Status: pki.StatusGranted,
},
TimeStampToken: token,
}, nil
}
// generateTokenInfo generate timestamp token info.
func (tsa *TSA) generateTokenInfo(req *timestamp.Request, policy asn1.ObjectIdentifier) ([]byte, error) {
serialNumber, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
return nil, err
}
nowFunc := tsa.NowFunc
if nowFunc == nil {
nowFunc = time.Now
}
info := timestamp.TSTInfo{
Version: 1,
Policy: policy,
MessageImprint: req.MessageImprint,
SerialNumber: serialNumber,
GenTime: nowFunc().UTC().Truncate(time.Second),
Accuracy: timestamp.Accuracy{
Seconds: 1,
},
}
return asn1.Marshal(info)
}
// generateSignedData generate signed data according to
func (tsa *TSA) generateSignedData(infoBytes []byte, requestCert bool) (cms.SignedData, error) {
var issuer asn1.RawValue
_, err := asn1.Unmarshal(tsa.cert.RawIssuer, &issuer)
if err != nil {
return cms.SignedData{}, err
}
contentType, err := convertToRawASN1([]interface{}{oid.TSTInfo}, "set")
if err != nil {
return cms.SignedData{}, err
}
infoDigest, err := hashutil.ComputeHash(crypto.SHA256, infoBytes)
if err != nil {
return cms.SignedData{}, err
}
messageDigest, err := convertToRawASN1([]interface{}{infoDigest}, "set")
if err != nil {
return cms.SignedData{}, err
}
signingTime, err := convertToRawASN1([]interface{}{time.Now().UTC()}, "set")
if err != nil {
return cms.SignedData{}, err
}
signed := cms.SignedData{
Version: 3,
DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{
{
Algorithm: oid.SHA256,
},
},
EncapsulatedContentInfo: cms.EncapsulatedContentInfo{
ContentType: oid.TSTInfo,
Content: infoBytes,
},
SignerInfos: []cms.SignerInfo{
{
Version: 1,
SignerIdentifier: cms.IssuerAndSerialNumber{
Issuer: issuer,
SerialNumber: tsa.cert.SerialNumber,
},
DigestAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oid.SHA256,
},
SignedAttributes: cms.Attributes{
{
Type: oid.ContentType,
Values: contentType,
},
{
Type: oid.MessageDigest,
Values: messageDigest,
},
{
Type: oid.SigningTime,
Values: signingTime,
},
},
SignatureAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: oid.SHA256WithRSA,
},
},
},
}
if requestCert {
certs, err := convertToRawASN1(tsa.cert.Raw, "tag:0")
if err != nil {
return cms.SignedData{}, err
}
signed.Certificates = certs
}
// sign data
signer := &signed.SignerInfos[0]
encodedAttributes, err := asn1.MarshalWithParams(signer.SignedAttributes, "set")
if err != nil {
return cms.SignedData{}, err
}
hashedAttributes, err := hashutil.ComputeHash(crypto.SHA256, encodedAttributes)
if err != nil {
return cms.SignedData{}, err
}
signer.Signature, err = rsa.SignPKCS1v15(rand.Reader, tsa.key, crypto.SHA256, hashedAttributes)
if err != nil {
return cms.SignedData{}, err
}
return signed, nil
}
// convertToRawASN1 convert any data ASN.1 data structure to asn1.RawValue.
func convertToRawASN1(val interface{}, params string) (asn1.RawValue, error) {
b, err := asn1.MarshalWithParams(val, params)
if err != nil {
return asn1.NullRawValue, err
}
var raw asn1.RawValue
_, err = asn1.UnmarshalWithParams(b, &raw, params)
if err != nil {
return asn1.NullRawValue, err
}
return raw, nil
}

View File

@ -1,95 +0,0 @@
package timestamptest
import (
"context"
"crypto/x509"
"testing"
"time"
"github.com/notaryproject/notation-go/crypto/timestamp"
"github.com/notaryproject/notation-go/internal/crypto/oid"
"github.com/notaryproject/notation-go/internal/crypto/pki"
)
func TestTSATimestampGranted(t *testing.T) {
// prepare TSA
now := time.Date(2021, 9, 18, 11, 54, 34, 0, time.UTC)
tsa, err := NewTSA()
if err != nil {
t.Fatalf("NewTSA() error = %v", err)
}
tsa.NowFunc = func() time.Time {
return now
}
// do timestamp
message := []byte("notation")
req, err := timestamp.NewRequestFromBytes(message)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
}
ctx := context.Background()
resp, err := tsa.Timestamp(ctx, req)
if err != nil {
t.Fatalf("TSA.Timestamp() error = %v", err)
}
wantStatus := pki.StatusGranted
if got := resp.Status.Status; got != wantStatus {
t.Fatalf("Response.Status = %v, want %v", got, wantStatus)
}
// verify timestamp token
token, err := resp.SignedToken()
if err != nil {
t.Fatalf("Response.SignedToken() error = %v", err)
}
roots := x509.NewCertPool()
roots.AddCert(tsa.Certificate())
opts := x509.VerifyOptions{
Roots: roots,
}
if _, err := token.Verify(opts); err != nil {
t.Fatal("SignedToken.Verify() error =", err)
}
info, err := token.Info()
if err != nil {
t.Fatal("SignedToken.Info() error =", err)
}
if err := info.Verify(message); err != nil {
t.Errorf("TSTInfo.Verify() error = %v", err)
}
timestamp, accuracy := info.Timestamp()
wantTimestamp := now
if timestamp != wantTimestamp {
t.Errorf("TSTInfo.Timestamp() Timestamp = %v, want %v", timestamp, wantTimestamp)
}
wantAccuracy := time.Second
if accuracy != wantAccuracy {
t.Errorf("TSTInfo.Timestamp() Accuracy = %v, want %v", accuracy, wantAccuracy)
}
}
func TestTSATimestampRejection(t *testing.T) {
// prepare TSA
tsa, err := NewTSA()
if err != nil {
t.Fatalf("NewTSA() error = %v", err)
}
// do timestamp
message := []byte("notation")
req, err := timestamp.NewRequestFromBytes(message)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
}
req.MessageImprint.HashAlgorithm.Algorithm = oid.SHA1WithRSA // set bad algorithm
ctx := context.Background()
resp, err := tsa.Timestamp(ctx, req)
if err != nil {
t.Fatalf("TSA.Timestamp() error = %v", err)
}
wantStatus := pki.StatusRejection
if got := resp.Status.Status; got != wantStatus {
t.Fatalf("Response.Status = %v, want %v", got, wantStatus)
}
}

View File

@ -1,132 +0,0 @@
package timestamp
import (
"bytes"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"math/big"
"time"
"github.com/notaryproject/notation-go/internal/crypto/cms"
"github.com/notaryproject/notation-go/internal/crypto/hashutil"
"github.com/notaryproject/notation-go/internal/crypto/oid"
asn1util "github.com/notaryproject/notation-go/internal/encoding/asn1"
)
// SignedToken is a parsed timestamp token with signatures.
type SignedToken cms.ParsedSignedData
// ParseSignedToken parses ASN.1 BER-encoded structure to SignedToken
// without verification.
// Callers should invoke Verify to verify the content before comsumption.
func ParseSignedToken(data []byte) (*SignedToken, error) {
data, err := asn1util.ConvertToDER(data)
if err != nil {
return nil, err
}
signed, err := cms.ParseSignedData(data)
if err != nil {
return nil, err
}
if !oid.TSTInfo.Equal(signed.ContentType) {
return nil, fmt.Errorf("unexpected content type: %v", signed.ContentType)
}
return (*SignedToken)(signed), nil
}
// Verify verifies the signed token as CMS SignedData.
// An empty list of KeyUsages in VerifyOptions implies ExtKeyUsageTimeStamping.
func (t *SignedToken) Verify(opts x509.VerifyOptions) ([]*x509.Certificate, error) {
if len(opts.KeyUsages) == 0 {
opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}
}
signed := (*cms.ParsedSignedData)(t)
certs, err := signed.Verify(opts)
if err != nil {
return nil, err
}
// RFC 3161 2.3: The corresponding certificate MUST contain only one instance of
// the extended key usage field extension.
verifiedCerts := make([]*x509.Certificate, 0, len(certs))
for _, cert := range certs {
if len(cert.ExtKeyUsage) == 1 && len(cert.UnknownExtKeyUsage) == 0 {
verifiedCerts = append(verifiedCerts, cert)
}
}
if len(verifiedCerts) == 0 {
return nil, errors.New("unexpected number of extended key usages")
}
return verifiedCerts, nil
}
// Info returns the timestamping information.
func (t *SignedToken) Info() (*TSTInfo, error) {
var info TSTInfo
if _, err := asn1.Unmarshal(t.Content, &info); err != nil {
return nil, err
}
return &info, nil
}
// Accuracy ::= SEQUENCE {
// seconds INTEGER OPTIONAL,
// millis [0] INTEGER (1..999) OPTIONAL,
// micros [1] INTEGER (1..999) OPTIONAL }
type Accuracy struct {
Seconds int `asn1:"optional"`
Milliseconds int `asn1:"optional,tag:0"`
Microseconds int `asn1:"optional,tag:1"`
}
// TSTInfo ::= SEQUENCE {
// version INTEGER { v1(1) },
// policy TSAPolicyId,
// messageImprint MessageImprint,
// serialNumber INTEGER,
// genTime GeneralizedTime,
// accuracy Accuracy OPTIONAL,
// ordering BOOLEAN DEFAULT FALSE,
// nonce INTEGER OPTIONAL,
// tsa [0] GeneralName OPTIONAL,
// extensions [1] IMPLICIT Extensions OPTIONAL }
type TSTInfo struct {
Version int // fixed to 1 as defined in RFC 3161 2.4.2 Response Format
Policy asn1.ObjectIdentifier
MessageImprint MessageImprint
SerialNumber *big.Int
GenTime time.Time `asn1:"generalized"`
Accuracy Accuracy `asn1:"optional"`
Ordering bool `asn1:"optional,default:false"`
Nonce *big.Int `asn1:"optional"`
TSA asn1.RawValue `asn1:"optional,tag:0"`
Extensions []pkix.Extension `asn1:"optional,tag:1"`
}
// Verify verifies the message against the timestamp token information.
func (tst *TSTInfo) Verify(message []byte) error {
hashAlg := tst.MessageImprint.HashAlgorithm.Algorithm
hash, ok := oid.ConvertToHash(hashAlg)
if !ok {
return fmt.Errorf("unrecognized hash algorithm: %v", hashAlg)
}
messageDigest, err := hashutil.ComputeHash(hash, message)
if err != nil {
return err
}
if !bytes.Equal(tst.MessageImprint.HashedMessage, messageDigest) {
return errors.New("mismatch message digest")
}
return nil
}
// Timestamp returns the timestamp by TSA and its accuracy.
func (tst *TSTInfo) Timestamp() (time.Time, time.Duration) {
accuracy := time.Duration(tst.Accuracy.Seconds)*time.Second +
time.Duration(tst.Accuracy.Milliseconds)*time.Millisecond +
time.Duration(tst.Accuracy.Microseconds)*time.Microsecond
return tst.GenTime, accuracy
}

67
dir/fs.go Normal file
View File

@ -0,0 +1,67 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dir
import (
"io/fs"
"os"
"path/filepath"
)
// SysFS is virtual file system interface that support fs.FS and SysPath method.
type SysFS interface {
fs.FS
// SysPath returns the real system path of the given path items in the SysFS.
SysPath(items ...string) (string, error)
}
type sysFS struct {
fs.FS
root string
}
// SysPath returns the real system path of the given name in the SysFS.
func (s sysFS) SysPath(items ...string) (string, error) {
pathItems := []string{s.root}
pathItems = append(pathItems, items...)
return filepath.Join(pathItems...), nil
}
// NewSysFS returns the SysFS for the given root directory.
//
// Support one root directory for rc.1, and may support union directories FS
// after rc.1.
func NewSysFS(root string) SysFS {
return sysFS{
FS: os.DirFS(root),
root: root}
}
// ConfigFS is the config SysFS
func ConfigFS() SysFS {
return NewSysFS(userConfigDirPath())
}
// PluginFS is the plugin SysFS
func PluginFS() SysFS {
return NewSysFS(filepath.Join(userLibexecDirPath(), PathPlugins))
}
// CacheFS is the cache SysFS.
//
// To get the root of crl file cache, use `CacheFS().SysFS(PathCRLCache)`.
func CacheFS() SysFS {
return NewSysFS(userCacheDirPath())
}

84
dir/fs_test.go Normal file
View File

@ -0,0 +1,84 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dir
import (
"bytes"
"path/filepath"
"testing"
)
func TestSysFS_SysPath(t *testing.T) {
wantPath := filepath.FromSlash("/path/notation/config.json")
fsys := NewSysFS("/path/notation")
path, err := fsys.SysPath(PathConfigFile)
if err != nil {
t.Fatalf("SysPath() failed. err = %v", err)
}
if path != wantPath {
t.Fatalf(`SysPath() failed. got: %q, want: %q`, path, wantPath)
}
}
func TestOsFs(t *testing.T) {
wantData := []byte("data")
fsys := NewSysFS("./testdata")
// read test file
path, err := fsys.Open("data.txt")
if err != nil {
t.Fatalf("Open() failed. err = %v", err)
}
data := make([]byte, 4)
_, err = path.Read(data)
if err != nil {
t.Fatalf("Read() failed. err = %v", err)
}
if !bytes.Equal(data, wantData) {
t.Fatalf("SysFS read failed. got data = %v, want %v", data, wantData)
}
}
func TestConfigFS(t *testing.T) {
configFS := ConfigFS()
path, err := configFS.SysPath(PathConfigFile)
if err != nil {
t.Fatalf("SysPath() failed. err = %v", err)
}
if path != filepath.Join(UserConfigDir, PathConfigFile) {
t.Fatalf(`SysPath() failed. got: %q, want: %q`, path, filepath.Join(UserConfigDir, PathConfigFile))
}
}
func TestPluginFS(t *testing.T) {
pluginFS := PluginFS()
path, err := pluginFS.SysPath("plugin")
if err != nil {
t.Fatalf("SysPath() failed. err = %v", err)
}
if path != filepath.Join(userLibexecDirPath(), PathPlugins, "plugin") {
t.Fatalf(`SysPath() failed. got: %q, want: %q`, path, filepath.Join(userLibexecDirPath(), PathPlugins, "plugin"))
}
}
func TestCRLFileCacheFS(t *testing.T) {
cacheFS := CacheFS()
path, err := cacheFS.SysPath(PathCRLCache)
if err != nil {
t.Fatalf("SysPath() failed. err = %v", err)
}
if path != filepath.Join(UserCacheDir, PathCRLCache) {
t.Fatalf(`SysPath() failed. got: %q, want: %q`, path, UserConfigDir)
}
}

153
dir/path.go Normal file
View File

@ -0,0 +1,153 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package dir implements Notation directory structure.
// [directory spec]: https://notaryproject.dev/docs/user-guides/how-to/directory-structure/
//
// Example:
//
// - Read config.json:
// file, err := dir.ConfigFS().Open(dir.PathConfigFile)
//
// - Get the path of config.json:
// path, err := dir.ConfigFS().SysPath(dir.PathConfigFile)
//
// - Read trustpolicy.json:
// file, err := dir.ConfigFS().Open(dir.PathTrustPolicy)
//
// - Get the path of trustpolicy.json:
// path, err := dir.ConfigFS().SysPath(dir.PathTrustPolicy)
//
// - Set custom configurations directory:
// dir.UserConfigDir = '/path/to/configurations/'
//
// Only user level directory is supported, and system level directory
// may be added later.
package dir
import (
"os"
"path"
"path/filepath"
)
var (
UserConfigDir string // Absolute path of user level {NOTATION_CONFIG}
UserLibexecDir string // Absolute path of user level {NOTATION_LIBEXEC}
UserCacheDir string // Absolute path of user level {NOTATION_CACHE}
)
const (
// notation is the directory name for notation configurations.
notation = "notation"
)
// The relative path to {NOTATION_CONFIG}
const (
// PathConfigFile is the config.json file relative path.
PathConfigFile = "config.json"
// PathSigningKeys is the signingkeys file relative path.
PathSigningKeys = "signingkeys.json"
// PathTrustPolicy is the OCI trust policy file relative path.
//
// Deprecated: PathTrustPolicy exists for historical compatibility and should not be used.
// To get OCI trust policy path, use PathOCITrustPolicy.
PathTrustPolicy = "trustpolicy.json"
// PathOCITrustPolicy is the OCI trust policy file relative path.
PathOCITrustPolicy = "trustpolicy.oci.json"
// PathBlobTrustPolicy is the Blob trust policy file relative path.
PathBlobTrustPolicy = "trustpolicy.blob.json"
// LocalKeysDir is the directory name for local key relative path.
LocalKeysDir = "localkeys"
// LocalCertificateExtension defines the extension of the certificate files.
LocalCertificateExtension = ".crt"
// LocalKeyExtension defines the extension of the key files.
LocalKeyExtension = ".key"
// TrustStoreDir is the directory name of trust store.
TrustStoreDir = "truststore"
)
// The relative path to {NOTATION_LIBEXEC}
const (
// PathPlugins is the plugins directory relative path.
PathPlugins = "plugins"
)
// The relative path to {NOTATION_CACHE}
const (
// PathCRLCache is the crl file cache directory relative path.
PathCRLCache = "crl"
)
// for unit tests
var (
userConfigDir = os.UserConfigDir
userCacheDir = os.UserCacheDir
)
// userConfigDirPath returns the user level {NOTATION_CONFIG} path.
func userConfigDirPath() string {
if UserConfigDir == "" {
userDir, err := userConfigDir()
if err != nil {
// fallback to current directory
UserConfigDir = "." + notation
return UserConfigDir
}
// set user config
UserConfigDir = filepath.Join(userDir, notation)
}
return UserConfigDir
}
// userLibexecDirPath returns the user level {NOTATION_LIBEXEC} path.
func userLibexecDirPath() string {
if UserLibexecDir == "" {
// set user libexec
UserLibexecDir = userConfigDirPath()
}
return UserLibexecDir
}
// userCacheDirPath returns the user level {NOTATION_CACHE} path.
func userCacheDirPath() string {
if UserCacheDir == "" {
userDir, err := userCacheDir()
if err != nil {
// fallback to current directory
UserCacheDir = filepath.Join("."+notation, "cache")
return UserCacheDir
}
// set user cache
UserCacheDir = filepath.Join(userDir, notation)
}
return UserCacheDir
}
// LocalKeyPath returns the local key and local cert relative paths.
func LocalKeyPath(name string) (keyPath, certPath string) {
basePath := path.Join(LocalKeysDir, name)
return basePath + LocalKeyExtension, basePath + LocalCertificateExtension
}
// X509TrustStoreDir returns the trust store relative path.
//
// items includes named-store and cert-file names.
// the directory follows the pattern of
// {NOTATION_CONFIG}/truststore/x509/{store-type}/{named-store}/{cert-file}
func X509TrustStoreDir(items ...string) string {
pathItems := []string{TrustStoreDir, "x509"}
pathItems = append(pathItems, items...)
return path.Join(pathItems...)
}

98
dir/path_test.go Normal file
View File

@ -0,0 +1,98 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dir
import (
"os"
"path/filepath"
"testing"
)
func mockUserPath() (string, error) {
return "/path/", nil
}
func setup() {
UserConfigDir = ""
UserLibexecDir = ""
UserCacheDir = ""
}
func Test_UserConfigDirPath(t *testing.T) {
userConfigDir = mockUserPath
setup()
got := userConfigDirPath()
if got != "/path/notation" {
t.Fatalf(`UserConfigDirPath() = %q, want "/path/notation"`, got)
}
}
func Test_NoHomeVariable(t *testing.T) {
t.Setenv("HOME", "")
t.Setenv("XDG_CONFIG_HOME", "")
t.Setenv("XDG_CACHE_HOME", "")
setup()
userConfigDir = os.UserConfigDir
got := userConfigDirPath()
if got != ".notation" {
t.Fatalf(`userConfigDirPath() = %q, want ".notation"`, got)
}
got = userCacheDirPath()
want := filepath.Join("."+notation, "cache")
if got != want {
t.Fatalf(`userCacheDirPath() = %q, want %q`, got, want)
}
}
func Test_UserLibexecDirPath(t *testing.T) {
userConfigDir = mockUserPath
setup()
got := userLibexecDirPath()
if got != "/path/notation" {
t.Fatalf(`UserConfigDirPath() = %q, want "/path/notation"`, got)
}
}
func Test_UserCacheDirPath(t *testing.T) {
userCacheDir = mockUserPath
setup()
got := userCacheDirPath()
if got != "/path/notation" {
t.Fatalf(`UserCacheDirPath() = %q, want "/path/notation"`, got)
}
}
func TestLocalKeyPath(t *testing.T) {
userConfigDir = mockUserPath
setup()
_ = userConfigDirPath()
_ = userLibexecDirPath()
gotKeyPath, gotCertPath := LocalKeyPath("web")
if gotKeyPath != "localkeys/web.key" {
t.Fatalf(`LocalKeyPath() gotKeyPath = %q, want "localkeys/web.key"`, gotKeyPath)
}
if gotCertPath != "localkeys/web.crt" {
t.Fatalf(`LocalKeyPath() gotCertPath = %q, want "localkeys/web.crt"`, gotCertPath)
}
}
func TestX509TrustStoreDir(t *testing.T) {
userConfigDir = mockUserPath
setup()
_ = userConfigDirPath()
_ = userLibexecDirPath()
if got := X509TrustStoreDir("ca", "web"); got != "truststore/x509/ca/web" {
t.Fatalf(`X509TrustStoreDir() = %q, want "truststore/x509/ca/web"`, got)
}
}

1
dir/testdata/data.txt vendored Normal file
View File

@ -0,0 +1 @@
data

130
errors.go
View File

@ -1,8 +1,128 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation
import "errors"
// ErrorPushSignatureFailed is used when failed to push signature to the
// target registry.
//
// Deprecated: Use PushSignatureFailedError instead.
type ErrorPushSignatureFailed = PushSignatureFailedError
// SignOptions errors
var (
ErrExpiryNotSpecified = errors.New("expiry not specified")
)
// PushSignatureFailedError is used when failed to push signature to the
// target registry.
type PushSignatureFailedError struct {
Msg string
}
func (e PushSignatureFailedError) Error() string {
if e.Msg != "" {
return "failed to push signature to registry with error: " + e.Msg
}
return "failed to push signature to registry"
}
// ErrorVerificationInconclusive is used when signature verification fails due
// to a runtime error (e.g. a network error)
//
// Deprecated: Use VerificationInconclusiveError instead.
type ErrorVerificationInconclusive = VerificationInconclusiveError
// VerificationInconclusiveError is used when signature verification fails due
// to a runtime error (e.g. a network error)
type VerificationInconclusiveError struct {
Msg string
}
func (e VerificationInconclusiveError) Error() string {
if e.Msg != "" {
return e.Msg
}
return "signature verification was inclusive due to an unexpected error"
}
// ErrorNoApplicableTrustPolicy is used when there is no trust policy that
// applies to the given artifact
//
// Deprecated: Use NoApplicableTrustPolicyError instead.
type ErrorNoApplicableTrustPolicy = NoApplicableTrustPolicyError
// NoApplicableTrustPolicyError is used when there is no trust policy that
// applies to the given artifact
type NoApplicableTrustPolicyError struct {
Msg string
}
func (e NoApplicableTrustPolicyError) Error() string {
if e.Msg != "" {
return e.Msg
}
return "there is no applicable trust policy for the given artifact"
}
// ErrorSignatureRetrievalFailed is used when notation is unable to retrieve the
// digital signature/s for the given artifact
//
// Deprecated: Use SignatureRetrievalFailedError instead.
type ErrorSignatureRetrievalFailed = SignatureRetrievalFailedError
// SignatureRetrievalFailedError is used when notation is unable to retrieve the
// digital signature/s for the given artifact
type SignatureRetrievalFailedError struct {
Msg string
}
func (e SignatureRetrievalFailedError) Error() string {
if e.Msg != "" {
return e.Msg
}
return "unable to retrieve the digital signature from the registry"
}
// ErrorVerificationFailed is used when it is determined that the digital
// signature/s is not valid for the given artifact
//
// Deprecated: Use VerificationFailedError instead.
type ErrorVerificationFailed = VerificationFailedError
// VerificationFailedError is used when it is determined that the digital
// signature/s is not valid for the given artifact
type VerificationFailedError struct {
Msg string
}
func (e VerificationFailedError) Error() string {
if e.Msg != "" {
return e.Msg
}
return "signature verification failed"
}
// ErrorUserMetadataVerificationFailed is used when the signature does not
// contain the user specified metadata
//
// Deprecated: Use UserMetadataVerificationFailedError instead.
type ErrorUserMetadataVerificationFailed = UserMetadataVerificationFailedError
// UserMetadataVerificationFailedError is used when the signature does not
// contain the user specified metadata
type UserMetadataVerificationFailedError struct {
Msg string
}
func (e UserMetadataVerificationFailedError) Error() string {
if e.Msg != "" {
return e.Msg
}
return "unable to find specified metadata in the signature"
}

170
errors_test.go Normal file
View File

@ -0,0 +1,170 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation
import "testing"
func TestErrorMessages(t *testing.T) {
tests := []struct {
name string
err error
want string
}{
{
name: "ErrorPushSignatureFailed with message",
err: ErrorPushSignatureFailed{Msg: "test message"},
want: "failed to push signature to registry with error: test message",
},
{
name: "ErrorPushSignatureFailed without message",
err: ErrorPushSignatureFailed{},
want: "failed to push signature to registry",
},
{
name: "ErrorVerificationInconclusive with message",
err: ErrorVerificationInconclusive{Msg: "test message"},
want: "test message",
},
{
name: "ErrorVerificationInconclusive without message",
err: ErrorVerificationInconclusive{},
want: "signature verification was inclusive due to an unexpected error",
},
{
name: "ErrorNoApplicableTrustPolicy with message",
err: ErrorNoApplicableTrustPolicy{Msg: "test message"},
want: "test message",
},
{
name: "ErrorNoApplicableTrustPolicy without message",
err: ErrorNoApplicableTrustPolicy{},
want: "there is no applicable trust policy for the given artifact",
},
{
name: "ErrorSignatureRetrievalFailed with message",
err: ErrorSignatureRetrievalFailed{Msg: "test message"},
want: "test message",
},
{
name: "ErrorSignatureRetrievalFailed without message",
err: ErrorSignatureRetrievalFailed{},
want: "unable to retrieve the digital signature from the registry",
},
{
name: "ErrorVerificationFailed with message",
err: ErrorVerificationFailed{Msg: "test message"},
want: "test message",
},
{
name: "ErrorVerificationFailed without message",
err: ErrorVerificationFailed{},
want: "signature verification failed",
},
{
name: "ErrorUserMetadataVerificationFailed with message",
err: ErrorUserMetadataVerificationFailed{Msg: "test message"},
want: "test message",
},
{
name: "ErrorUserMetadataVerificationFailed without message",
err: ErrorUserMetadataVerificationFailed{},
want: "unable to find specified metadata in the signature",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.err.Error(); got != tt.want {
t.Errorf("Error() = %v, want %v", got, tt.want)
}
})
}
}
func TestCustomErrorPrintsCorrectMessage(t *testing.T) {
tests := []struct {
name string
err error
want string
}{
{
name: "PushSignatureFailedError with message",
err: PushSignatureFailedError{Msg: "test message"},
want: "failed to push signature to registry with error: test message",
},
{
name: "PushSignatureFailedError without message",
err: PushSignatureFailedError{},
want: "failed to push signature to registry",
},
{
name: "VerificationInconclusiveError with message",
err: VerificationInconclusiveError{Msg: "test message"},
want: "test message",
},
{
name: "VerificationInconclusiveError without message",
err: VerificationInconclusiveError{},
want: "signature verification was inclusive due to an unexpected error",
},
{
name: "NoApplicableTrustPolicyError with message",
err: NoApplicableTrustPolicyError{Msg: "test message"},
want: "test message",
},
{
name: "NoApplicableTrustPolicyError without message",
err: NoApplicableTrustPolicyError{},
want: "there is no applicable trust policy for the given artifact",
},
{
name: "SignatureRetrievalFailedError with message",
err: SignatureRetrievalFailedError{Msg: "test message"},
want: "test message",
},
{
name: "SignatureRetrievalFailedError without message",
err: SignatureRetrievalFailedError{},
want: "unable to retrieve the digital signature from the registry",
},
{
name: "VerificationFailedError with message",
err: VerificationFailedError{Msg: "test message"},
want: "test message",
},
{
name: "VerificationFailedError without message",
err: VerificationFailedError{},
want: "signature verification failed",
},
{
name: "UserMetadataVerificationFailedError with message",
err: UserMetadataVerificationFailedError{Msg: "test message"},
want: "test message",
},
{
name: "UserMetadataVerificationFailedError without message",
err: UserMetadataVerificationFailedError{},
want: "unable to find specified metadata in the signature",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.err.Error(); got != tt.want {
t.Errorf("Error() = %v, want %v", got, tt.want)
}
})
}
}

94
example_localSign_test.go Normal file
View File

@ -0,0 +1,94 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"crypto/x509"
"fmt"
"github.com/notaryproject/notation-core-go/signature"
"github.com/notaryproject/notation-core-go/signature/cose"
"github.com/notaryproject/notation-core-go/testhelper"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/signer"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
var (
// exampleDesc is an example of the target manifest descriptor.
exampleDesc = ocispec.Descriptor{
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Digest: "sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c",
Size: 528,
}
// exampleCertTuple contains a RSA privateKey and a self-signed X509
// certificate generated for demo purpose ONLY.
exampleCertTuple = testhelper.GetRSASelfSignedSigningCertTuple("Notation Example self-signed")
exampleCerts = []*x509.Certificate{exampleCertTuple.Cert}
)
// ExampleLocalSign demonstrates how to use signer.Sign to sign an artifact
// at local (without using a registry.Repository).
func Example_localSign() {
// exampleSigner is a notation.Signer given key and X509 certificate chain.
// Users should replace `exampleCertTuple.PrivateKey` with their own private
// key and replace `exampleCerts` with the corresponding full certificate
// chain, following the Notary Project certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleSigner, err := signer.NewGenericSigner(exampleCertTuple.PrivateKey, exampleCerts)
if err != nil {
panic(err) // Handle error
}
// Both COSE ("application/cose") and JWS ("application/jose+json")
// signature mediaTypes are supported.
exampleSignatureMediaType := cose.MediaTypeEnvelope
// exampleSignOptions is an example of notation.SignerSignOptions.
exampleSignOptions := notation.SignerSignOptions{
SignatureMediaType: exampleSignatureMediaType,
SigningAgent: "example signing agent",
}
// local sign core process
// upon successful signing, signature envelope and signerInfo are returned.
// signatureEnvelope can be used in a verification process later on.
signatureEnvelope, signerInfo, err := exampleSigner.Sign(context.Background(), exampleDesc, exampleSignOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully signed")
// a peek of the signature envelope generated from Sign
sigBlob, err := signature.ParseEnvelope(exampleSignatureMediaType, signatureEnvelope)
if err != nil {
panic(err) // Handle error
}
sigContent, err := sigBlob.Content()
if err != nil {
panic(err) // Handle error
}
fmt.Println("signature Payload ContentType:", sigContent.Payload.ContentType)
fmt.Println("signature Payload Content:", string(sigContent.Payload.Content))
fmt.Println("signerInfo SigningAgent:", signerInfo.UnsignedAttributes.SigningAgent)
// Output:
// Successfully signed
// signature Payload ContentType: application/vnd.cncf.notary.payload.v1+json
// signature Payload Content: {"targetArtifact":{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c","size":528}}
// signerInfo SigningAgent: example signing agent
}

203
example_localVerify_test.go Normal file
View File

@ -0,0 +1,203 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"encoding/pem"
"errors"
"fmt"
"os"
"github.com/notaryproject/notation-core-go/signature/cose"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/dir"
"github.com/notaryproject/notation-go/verifier"
"github.com/notaryproject/notation-go/verifier/trustpolicy"
"github.com/notaryproject/notation-go/verifier/truststore"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// examplePolicyDocument is an example of a valid trust policy document.
// trust policy document should follow this spec:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-policy
var examplePolicyDocument = trustpolicy.OCIDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.OCITrustPolicy{
{
Name: "test-statement-name",
RegistryScopes: []string{"example/software"},
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: trustpolicy.LevelStrict.Name, Override: map[trustpolicy.ValidationType]trustpolicy.ValidationAction{trustpolicy.TypeRevocation: trustpolicy.ActionSkip}},
TrustStores: []string{"ca:valid-trust-store"},
TrustedIdentities: []string{"*"},
},
},
}
// ExampleLocalVerify demonstrates how to use verifier.Verify to verify a
// signature of the target artifact at local (without using a
// registry.Repository).
func Example_localVerify() {
// exampleArtifactReference is an example of the target artifact reference
exampleArtifactReference := "example/software@sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c"
// Both COSE ("application/cose") and JWS ("application/jose+json")
// signature mediaTypes are supported.
exampleSignatureMediaType := cose.MediaTypeEnvelope
// exampleTargetDescriptor is an example of the target manifest descriptor.
exampleTargetDescriptor := ocispec.Descriptor{
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Digest: "sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c",
Size: 528,
}
// exampleSignatureEnvelope is a valid signature envelope.
exampleSignatureEnvelope := generateExampleSignatureEnvelope()
// exampleVerifyOptions is an example of notation.VerifierVerifyOptions
exampleVerifyOptions := notation.VerifierVerifyOptions{
ArtifactReference: exampleArtifactReference,
SignatureMediaType: exampleSignatureMediaType,
}
// createTrustStore creates a trust store directory for demo purpose.
// Users could use the default trust store from Notary Project and
// add trusted certificates into it following the trust store spec:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-store
if err := createTrustStore(); err != nil {
panic(err) // Handle error
}
// exampleVerifier is an example of notation.Verifier given
// trust policy document and X509 trust store.
exampleVerifier, err := verifier.New(&examplePolicyDocument, truststore.NewX509TrustStore(dir.ConfigFS()), nil)
if err != nil {
panic(err) // Handle error
}
// local verify core process
// upon successful verification, the signature verification outcome is
// returned.
outcome, err := exampleVerifier.Verify(context.Background(), exampleTargetDescriptor, exampleSignatureEnvelope, exampleVerifyOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully verified")
// a peek of the payload inside the signature envelope
fmt.Println("payload ContentType:", outcome.EnvelopeContent.Payload.ContentType)
// Note, upon successful verification, payload.TargetArtifact from the
// signature envelope matches exactly with our exampleTargetDescriptor.
// (This check has been done for the user inside verifier.Verify.)
fmt.Println("payload Content:", string(outcome.EnvelopeContent.Payload.Content))
// Output:
// Successfully verified
// payload ContentType: application/vnd.cncf.notary.payload.v1+json
// payload Content: {"targetArtifact":{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c","size":528}}
}
func generateExampleSignatureEnvelope() []byte {
// exampleSignatureEnvelopePem is a valid signature envelope in COSE format
// wrapped by a PEM block.
// The signature envelope is generated in a previous Sign process.
// Users should replace it with their own signature envelope.
// Regarding how to generate such signature envelopes, users could refer to
// `example_localSign_test.go`.
exampleSignatureEnvelopePem := `-----BEGIN EXAMPLE SIGNATURE ENVELOPE-----
0oRYnqUBOCQCgXgcaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZQN4K2FwcGxp
Y2F0aW9uL3ZuZC5jbmNmLm5vdGFyeS5wYXlsb2FkLnYxK2pzb254GmlvLmNuY2Yu
bm90YXJ5LnNpZ25pbmdUaW1lwRpju22GeBxpby5jbmNmLm5vdGFyeS5zaWduaW5n
U2NoZW1la25vdGFyeS54NTA5ohghgVkDRDCCA0AwggIooAMCAQICAVEwDQYJKoZI
hvcNAQELBQAwTjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdT
ZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxDzANBgNVBAMTBmFscGluZTAgFw0wMDA4
MjkxMzUwMDBaGA8yMTIzMDgyOTEzNTAwMFowTjELMAkGA1UEBhMCVVMxCzAJBgNV
BAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxDzANBgNV
BAMTBmFscGluZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKHIN6hL
MjQwy3wfDhw+HYYvjNTTytQLMuTV/OHPoL2nGbqDy08JqTn5upz7exjzwfRu0usc
YRTW0cU2H2FIyvpGgo9/F4wUX+ZRnsG0iSlZMUPNv2sVO9HHkltyaWs62oHjfSVE
2fyX4uDH54qSE8HJjKIoo/Hhqx7JI8lcmWyXdFjDCkQ1lYSz1IjzFVrf+He2LWSL
c2nGkCrV5l4LEwk1qSKJbN4H7TWI60KDLFHpVHQ/LzgFjfSdvP0sgvrkofytSn8l
JW6rn5+HYvAxAcZ7T+cJ12GyS9Y7Y7FIBMQFY0MU9cyOfV9+pt7d2CqgkIdXLndN
i+aJzm2Os4+ezekCAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG
CCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAvAIwS8uxLEaYuqILgnRUm+p0R
o7xdezfm+pg297AEZwfE9acb8009usmvgMlkfo46HRMGzCEvtd5Vfvak9i8KpWzl
DWOdfT2thkUWO9iY3qMFiN4ipCmjM32VAe5UUxl3RLmQKS20zv9yVXEfX37tNDdV
GgD/WBhJUreCQyWAPTPnf0LaPh4iLBNCo/o3Z8CGKLzzzpa8iji3xW/69RhKu5+t
8RWc/N4sljWmXbCeTd2B8XTqZGwWwmpThAQyU40CqngGAS6ADTVNDgbJZqhwkgUx
J4W6iRzekCshdPUnDpeS8DNULE5dFGObIhiwH4/40n/Th/VS0zxzkvPzdCmueBtp
by5jbmNmLm5vdGFyeS5zaWduaW5nQWdlbnRuTm90YXRpb24vMS4wLjBYtXsidGFy
Z2V0QXJ0aWZhY3QiOnsibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tl
ci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsImRpZ2VzdCI6InNoYTI1
NjpjMGQ0ODhhODAwZTQxMjdjMzM0YWQyMGQ2MWQ3YmMyMWI0MDk3NTQwMzI3MjE3
ZGZhYjUyMjYyYWRjMDIzODBjIiwic2l6ZSI6NTI4fX1ZAQBUIYPA45B/iFylmloW
s/NpTVsheuedJb6nnXK0XR46BYs4AeCXVVYSRDK2Bq+tA9C7BXHoeCMcqnAHa8qs
ZR/fcMa9FrEPI6Pl8QVE/6QRMkT+Drn+9CSFxzHk6CU9S1vRsUVYNcibyejnuVEE
RPYaORrnfTc5wIs4XxeqprmrLimMMNn+u82Uadtc57tbHbY8Vh4XEKP++hBJJNvQ
E60X5aWKIS2RnOEc4n9T7LdN0bOL1OoM1lW4iTFMhzfcy/VmF8PrOStFS9LllX3J
69V0WwHbmD33cjtVBDCF44UXRWgLQGbE6yaaVmdxEUBGKqSUeHf8Gp7WoZ/YaFmz
xQr/
-----END EXAMPLE SIGNATURE ENVELOPE-----`
block, _ := pem.Decode([]byte(exampleSignatureEnvelopePem))
if block == nil {
panic(errors.New("invalid signature envelope pem block"))
}
// block.Bytes contains the binary of the signature envelope.
return block.Bytes
}
func createTrustStore() error {
// changing the path of the trust store for demo purpose.
// Users could keep the default value, i.e. os.UserConfigDir.
dir.UserConfigDir = "tmp"
// an example of a valid X509 self-signed certificate for demo purpose ONLY.
// (This self-signed cert is paired with the private key used to
// generate the `exampleSignatureEnvelopePem` above.)
// Users should replace `exampleX509Certificate` with their own trusted
// certificate and add to the trust store, following the
// Notary Project certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleX509Certificate := `-----BEGIN CERTIFICATE-----
MIIDQDCCAiigAwIBAgIBUTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEL
MAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEP
MA0GA1UEAxMGYWxwaW5lMCAXDTAwMDgyOTEzNTAwMFoYDzIxMjMwODI5MTM1MDAw
WjBOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUx
DzANBgNVBAoTBk5vdGFyeTEPMA0GA1UEAxMGYWxwaW5lMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAocg3qEsyNDDLfB8OHD4dhi+M1NPK1Asy5NX84c+g
vacZuoPLTwmpOfm6nPt7GPPB9G7S6xxhFNbRxTYfYUjK+kaCj38XjBRf5lGewbSJ
KVkxQ82/axU70ceSW3JpazrageN9JUTZ/Jfi4MfnipITwcmMoiij8eGrHskjyVyZ
bJd0WMMKRDWVhLPUiPMVWt/4d7YtZItzacaQKtXmXgsTCTWpIols3gftNYjrQoMs
UelUdD8vOAWN9J28/SyC+uSh/K1KfyUlbqufn4di8DEBxntP5wnXYbJL1jtjsUgE
xAVjQxT1zI59X36m3t3YKqCQh1cud02L5onObY6zj57N6QIDAQABoycwJTAOBgNV
HQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQAD
ggEBAC8AjBLy7EsRpi6oguCdFSb6nRGjvF17N+b6mDb3sARnB8T1pxvzTT26ya+A
yWR+jjodEwbMIS+13lV+9qT2LwqlbOUNY519Pa2GRRY72JjeowWI3iKkKaMzfZUB
7lRTGXdEuZApLbTO/3JVcR9ffu00N1UaAP9YGElSt4JDJYA9M+d/Qto+HiIsE0Kj
+jdnwIYovPPOlryKOLfFb/r1GEq7n63xFZz83iyWNaZdsJ5N3YHxdOpkbBbCalOE
BDJTjQKqeAYBLoANNU0OBslmqHCSBTEnhbqJHN6QKyF09ScOl5LwM1QsTl0UY5si
GLAfj/jSf9OH9VLTPHOS8/N0Ka4=
-----END CERTIFICATE-----`
// Adding the certificate into the trust store.
if err := os.MkdirAll("tmp/truststore/x509/ca/valid-trust-store", 0700); err != nil {
return err
}
return os.WriteFile("tmp/truststore/x509/ca/valid-trust-store/NotationLocalExample.pem", []byte(exampleX509Certificate), 0600)
}

View File

@ -0,0 +1,85 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"crypto/x509"
"fmt"
"oras.land/oras-go/v2/registry/remote"
"github.com/notaryproject/notation-core-go/signature/cose"
"github.com/notaryproject/notation-core-go/testhelper"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/registry"
"github.com/notaryproject/notation-go/signer"
)
// Both COSE ("application/cose") and JWS ("application/jose+json")
// signature mediaTypes are supported.
var exampleSignatureMediaType = cose.MediaTypeEnvelope
// ExampleRemoteSign demonstrates how to use notation.Sign to sign an artifact
// in the remote registry and push the signature to the remote.
func Example_remoteSign() {
// exampleArtifactReference is an example of the target artifact reference
var exampleArtifactReference = "localhost:5000/software@sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e"
// exampleCertTuple contains a RSA privateKey and a self-signed X509
// certificate generated for demo purpose ONLY.
exampleCertTuple := testhelper.GetRSASelfSignedSigningCertTuple("Notation Example self-signed")
exampleCerts := []*x509.Certificate{exampleCertTuple.Cert}
// exampleSigner is a notation.Signer given key and X509 certificate chain.
// Users should replace `exampleCertTuple.PrivateKey` with their own private
// key and replace `exampleCerts` with the corresponding full certificate
// chain, following the Notary Project certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleSigner, err := signer.NewGenericSigner(exampleCertTuple.PrivateKey, exampleCerts)
if err != nil {
panic(err) // Handle error
}
// exampleRepo is an example of registry.Repository.
remoteRepo, err := remote.NewRepository(exampleArtifactReference)
if err != nil {
panic(err) // Handle error
}
exampleRepo := registry.NewRepository(remoteRepo)
// exampleSignOptions is an example of notation.SignOptions.
exampleSignOptions := notation.SignOptions{
SignerSignOptions: notation.SignerSignOptions{
SignatureMediaType: exampleSignatureMediaType,
},
ArtifactReference: exampleArtifactReference,
}
// remote sign core process
// upon successful signing, descriptor of the sign content is returned and
// the generated signature is pushed into remote registry.
targetManifestDesc, sigManifestDesc, err := notation.SignOCI(context.Background(), exampleSigner, exampleRepo, exampleSignOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully signed")
fmt.Println("targetManifestDesc.MediaType:", targetManifestDesc.MediaType)
fmt.Println("targetManifestDesc.Digest:", targetManifestDesc.Digest)
fmt.Println("targetManifestDesc.Size:", targetManifestDesc.Size)
fmt.Println("sigManifestDesc.MediaType:", sigManifestDesc.MediaType)
fmt.Println("sigManifestDesc.Digest:", sigManifestDesc.Digest)
fmt.Println("sigManifestDesc.Size:", sigManifestDesc.Size)
}

View File

@ -0,0 +1,132 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"fmt"
"os"
_ "github.com/notaryproject/notation-core-go/signature/cose"
_ "github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/dir"
"github.com/notaryproject/notation-go/registry"
"github.com/notaryproject/notation-go/verifier"
"github.com/notaryproject/notation-go/verifier/trustpolicy"
"github.com/notaryproject/notation-go/verifier/truststore"
"oras.land/oras-go/v2/registry/remote"
)
// ExampleRemoteVerify demonstrates how to use notation.Verify to verify
// signatures of an artifact in the remote registry.
func Example_remoteVerify() {
// exampleArtifactReference is an example of the target artifact reference
exampleArtifactReference := "localhost:5000/software@sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e"
// examplePolicyDocument is an example of a valid trust policy document.
// trust policy document should follow this spec:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-policy
examplePolicyDocument := trustpolicy.OCIDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.OCITrustPolicy{
{
Name: "test-statement-name",
RegistryScopes: []string{"*"},
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: trustpolicy.LevelStrict.Name},
TrustStores: []string{"ca:valid-trust-store"},
TrustedIdentities: []string{"*"},
},
},
}
// generateTrustStore generates a trust store directory for demo purpose.
// Users should configure their own trust store and add trusted certificates
// into it following the trust store spec:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-store
if err := generateTrustStore(); err != nil {
panic(err) // Handle error
}
// exampleVerifier is an example of notation.Verifier given
// trust policy document and X509 trust store.
exampleVerifier, err := verifier.New(&examplePolicyDocument, truststore.NewX509TrustStore(dir.ConfigFS()), nil)
if err != nil {
panic(err) // Handle error
}
// exampleRepo is an example of registry.Repository.
remoteRepo, err := remote.NewRepository(exampleArtifactReference)
if err != nil {
panic(err) // Handle error
}
exampleRepo := registry.NewRepository(remoteRepo)
// exampleVerifyOptions is an example of notation.VerifyOptions.
exampleVerifyOptions := notation.VerifyOptions{
ArtifactReference: exampleArtifactReference,
MaxSignatureAttempts: 50,
}
// remote verify core process
// upon successful verification, the target manifest descriptor
// and signature verification outcome are returned.
targetDesc, _, err := notation.Verify(context.Background(), exampleVerifier, exampleRepo, exampleVerifyOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully verified")
fmt.Println("targetDesc MediaType:", targetDesc.MediaType)
fmt.Println("targetDesc Digest:", targetDesc.Digest)
fmt.Println("targetDesc Size:", targetDesc.Size)
}
func generateTrustStore() error {
// changing the path of the trust store for demo purpose.
// Users could keep the default value, i.e. os.UserConfigDir.
dir.UserConfigDir = "tmp"
// an example of a valid X509 self-signed certificate for demo purpose ONLY.
// Users should replace `exampleX509Certificate` with their own trusted
// certificate and add to the trust store, following the
// Notary Project certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleX509Certificate := `-----BEGIN CERTIFICATE-----
MIIDQDCCAiigAwIBAgIBUTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEL
MAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEP
MA0GA1UEAxMGYWxwaW5lMCAXDTAwMDgyOTEzNTAwMFoYDzIxMjMwODI5MTM1MDAw
WjBOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUx
DzANBgNVBAoTBk5vdGFyeTEPMA0GA1UEAxMGYWxwaW5lMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAocg3qEsyNDDLfB8OHD4dhi+M1NPK1Asy5NX84c+g
vacZuoPLTwmpOfm6nPt7GPPB9G7S6xxhFNbRxTYfYUjK+kaCj38XjBRf5lGewbSJ
KVkxQ82/axU70ceSW3JpazrageN9JUTZ/Jfi4MfnipITwcmMoiij8eGrHskjyVyZ
bJd0WMMKRDWVhLPUiPMVWt/4d7YtZItzacaQKtXmXgsTCTWpIols3gftNYjrQoMs
UelUdD8vOAWN9J28/SyC+uSh/K1KfyUlbqufn4di8DEBxntP5wnXYbJL1jtjsUgE
xAVjQxT1zI59X36m3t3YKqCQh1cud02L5onObY6zj57N6QIDAQABoycwJTAOBgNV
HQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQAD
ggEBAC8AjBLy7EsRpi6oguCdFSb6nRGjvF17N+b6mDb3sARnB8T1pxvzTT26ya+A
yWR+jjodEwbMIS+13lV+9qT2LwqlbOUNY519Pa2GRRY72JjeowWI3iKkKaMzfZUB
7lRTGXdEuZApLbTO/3JVcR9ffu00N1UaAP9YGElSt4JDJYA9M+d/Qto+HiIsE0Kj
+jdnwIYovPPOlryKOLfFb/r1GEq7n63xFZz83iyWNaZdsJ5N3YHxdOpkbBbCalOE
BDJTjQKqeAYBLoANNU0OBslmqHCSBTEnhbqJHN6QKyF09ScOl5LwM1QsTl0UY5si
GLAfj/jSf9OH9VLTPHOS8/N0Ka4=
-----END CERTIFICATE-----`
// Adding the certificate into the trust store.
if err := os.MkdirAll("tmp/truststore/x509/ca/valid-trust-store", 0700); err != nil {
return err
}
return os.WriteFile("tmp/truststore/x509/ca/valid-trust-store/NotationExample.pem", []byte(exampleX509Certificate), 0600)
}

88
example_signBlob_test.go Normal file
View File

@ -0,0 +1,88 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"fmt"
"strings"
"github.com/notaryproject/notation-core-go/signature"
"github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/signer"
)
// ExampleSignBlob demonstrates how to use [notation.SignBlob] to sign arbitrary
// data.
func Example_signBlob() {
// exampleSigner implements [notation.Signer] and [notation.BlobSigner].
// Given key and X509 certificate chain, it provides method to sign OCI
// artifacts or blobs.
// Users should replace `exampleCertTuple.PrivateKey` with their own private
// key and replace `exampleCerts` with the corresponding certificate chain,
// following the Notary Project certificate requirements:
// https://github.com/notaryproject/specifications/tree/9c81dc773508dedc5a81c02c8d805de04f65050b/specs/signature-specification.md#certificate-requirements
exampleSigner, err := signer.NewGenericSigner(exampleCertTuple.PrivateKey, exampleCerts)
if err != nil {
panic(err) // Handle error
}
// Both COSE ("application/cose") and JWS ("application/jose+json")
// signature mediaTypes are supported.
exampleSignatureMediaType := jws.MediaTypeEnvelope
exampleContentMediaType := "video/mp4"
// exampleSignOptions is an example of [notation.SignBlobOptions].
exampleSignOptions := notation.SignBlobOptions{
SignerSignOptions: notation.SignerSignOptions{
SignatureMediaType: exampleSignatureMediaType,
SigningAgent: "example signing agent",
},
ContentMediaType: exampleContentMediaType,
UserMetadata: map[string]string{"buildId": "101"},
}
// exampleReader reads the data that needs to be signed.
// This data can be in a file or in memory.
exampleReader := strings.NewReader("example blob")
// Upon successful signing, signature envelope and signerInfo are returned.
// signatureEnvelope can be used in a verification process later on.
signatureEnvelope, signerInfo, err := notation.SignBlob(context.Background(), exampleSigner, exampleReader, exampleSignOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully signed")
// a peek of the signature envelope generated
sigBlob, err := signature.ParseEnvelope(exampleSignatureMediaType, signatureEnvelope)
if err != nil {
panic(err) // Handle error
}
sigContent, err := sigBlob.Content()
if err != nil {
panic(err) // Handle error
}
fmt.Println("signature Payload ContentType:", sigContent.Payload.ContentType)
fmt.Println("signature Payload Content:", string(sigContent.Payload.Content))
fmt.Println("signerInfo SigningAgent:", signerInfo.UnsignedAttributes.SigningAgent)
// Output:
// Successfully signed
// signature Payload ContentType: application/vnd.cncf.notary.payload.v1+json
// signature Payload Content: {"targetArtifact":{"annotations":{"buildId":"101"},"digest":"sha384:b8ab24dafba5cf7e4c89c562f811cf10493d4203da982d3b1345f366ca863d9c2ed323dbd0fb7ff83a80302ceffa5a61","mediaType":"video/mp4","size":12}}
// signerInfo SigningAgent: example signing agent
}

View File

@ -0,0 +1,113 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"crypto/x509"
"encoding/pem"
"fmt"
"oras.land/oras-go/v2/registry/remote"
"github.com/notaryproject/notation-core-go/revocation"
"github.com/notaryproject/notation-core-go/revocation/purpose"
"github.com/notaryproject/notation-core-go/testhelper"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/registry"
"github.com/notaryproject/notation-go/signer"
"github.com/notaryproject/tspclient-go"
)
// Example_signWithTimestamp demonstrates how to use notation.Sign to sign an
// artifact with a RFC 3161 compliant timestamp countersignature and
// user trusted TSA root certificate
func Example_signWithTimestamp() {
// exampleArtifactReference is an example of the target artifact reference
var exampleArtifactReference = "localhost:5000/software@sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e"
// exampleCertTuple contains a RSA privateKey and a self-signed X509
// certificate generated for demo purpose ONLY.
exampleCertTuple := testhelper.GetRSASelfSignedSigningCertTuple("Notation Example self-signed")
exampleCerts := []*x509.Certificate{exampleCertTuple.Cert}
// exampleSigner is a notation.Signer given key and X509 certificate chain.
// Users should replace `exampleCertTuple.PrivateKey` with their own private
// key and replace `exampleCerts` with the corresponding full certificate
// chain, following the Notary Project certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleSigner, err := signer.NewGenericSigner(exampleCertTuple.PrivateKey, exampleCerts)
if err != nil {
panic(err) // Handle error
}
// exampleRepo is an example of registry.Repository.
remoteRepo, err := remote.NewRepository(exampleArtifactReference)
if err != nil {
panic(err) // Handle error
}
exampleRepo := registry.NewRepository(remoteRepo)
// replace exampleRFC3161TSAServer with your trusted TSA server URL.
exampleRFC3161TSAServer := "<TSA server URL>"
httpTimestamper, err := tspclient.NewHTTPTimestamper(nil, exampleRFC3161TSAServer)
if err != nil {
panic(err) // Handle error
}
// replace exampleTSARootCertPem with your trusted TSA root cert.
exampleTSARootCertPem := "<TSA root cert>"
block, _ := pem.Decode([]byte(exampleTSARootCertPem))
if block == nil {
panic("failed to parse tsa root certificate PEM")
}
tsaRootCert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
panic("failed to parse tsa root certificate: " + err.Error())
}
tsaRootCAs := x509.NewCertPool()
tsaRootCAs.AddCert(tsaRootCert)
// enable timestamping certificate chain revocation check
tsaRevocationValidator, err := revocation.NewWithOptions(revocation.Options{
CertChainPurpose: purpose.Timestamping,
})
if err != nil {
panic(err) // Handle error
}
// exampleSignOptions is an example of notation.SignOptions.
exampleSignOptions := notation.SignOptions{
SignerSignOptions: notation.SignerSignOptions{
SignatureMediaType: exampleSignatureMediaType,
Timestamper: httpTimestamper,
TSARootCAs: tsaRootCAs,
TSARevocationValidator: tsaRevocationValidator,
},
ArtifactReference: exampleArtifactReference,
}
targetManifestDesc, sigManifestDesc, err := notation.SignOCI(context.Background(), exampleSigner, exampleRepo, exampleSignOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully signed")
fmt.Println("targetManifestDesc.MediaType:", targetManifestDesc.MediaType)
fmt.Println("targetManifestDesc.Digest:", targetManifestDesc.Digest)
fmt.Println("targetManifestDesc.Size:", targetManifestDesc.Size)
fmt.Println("sigManifestDesc.MediaType:", sigManifestDesc.MediaType)
fmt.Println("sigManifestDesc.Digest:", sigManifestDesc.Digest)
fmt.Println("sigManifestDesc.Size:", sigManifestDesc.Size)
}

154
example_verifyBlob_test.go Normal file
View File

@ -0,0 +1,154 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"fmt"
"os"
"strings"
"github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/dir"
"github.com/notaryproject/notation-go/verifier"
"github.com/notaryproject/notation-go/verifier/trustpolicy"
"github.com/notaryproject/notation-go/verifier/truststore"
)
// exampleBlobPolicyDocument is an example of a valid blob trust policy document.
// blob trust policy document should follow this spec:
// https://github.com/notaryproject/specifications/tree/9c81dc773508dedc5a81c02c8d805de04f65050b/specs/trust-store-trust-policy.md#blob-trust-policy
var exampleBlobPolicyDocument = trustpolicy.BlobDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.BlobTrustPolicy{
{
Name: "test-statement-name",
SignatureVerification: trustpolicy.SignatureVerification{VerificationLevel: trustpolicy.LevelStrict.Name, Override: map[trustpolicy.ValidationType]trustpolicy.ValidationAction{trustpolicy.TypeRevocation: trustpolicy.ActionSkip}},
TrustStores: []string{"ca:valid-trust-store"},
TrustedIdentities: []string{"*"},
},
},
}
// ExampleVerifyBlob demonstrates how to use [notation.VerifyBlob] to verify a
// signature of an arbitrary blob.
func Example_verifyBlob() {
// Both COSE ("application/cose") and JWS ("application/jose+json")
// signature mediaTypes are supported.
exampleSignatureMediaType := jws.MediaTypeEnvelope
// exampleSignatureEnvelope is a valid signature envelope.
exampleSignatureEnvelope := getSignatureEnvelope()
// createTrustStoreForBlobVerify creates a trust store directory for demo purpose.
// Users could use the default trust store from Notary Project and add trusted
// certificates into it following the trust store spec:
// https://github.com/notaryproject/specifications/tree/9c81dc773508dedc5a81c02c8d805de04f65050b/specs/trust-store-trust-policy.md#trust-store
if err := createTrustStoreForBlobVerify(); err != nil {
panic(err) // Handle error
}
// exampleVerifier implements [notation.Verify] and [notation.VerifyBlob].
exampleVerifier, err := verifier.NewVerifierWithOptions(truststore.NewX509TrustStore(dir.ConfigFS()), verifier.VerifierOptions{
BlobTrustPolicy: &exampleBlobPolicyDocument,
})
if err != nil {
panic(err) // Handle error
}
// exampleReader reads the data that needs to be verified.
// This data can be in a file or in memory.
exampleReader := strings.NewReader("example blob")
// exampleVerifyOptions is an example of [notation.VerifyBlobOptions]
exampleVerifyOptions := notation.VerifyBlobOptions{
BlobVerifierVerifyOptions: notation.BlobVerifierVerifyOptions{
SignatureMediaType: exampleSignatureMediaType,
TrustPolicyName: "test-statement-name",
},
}
// upon successful verification, the signature verification outcome is
// returned.
_, outcome, err := notation.VerifyBlob(context.Background(), exampleVerifier, exampleReader, []byte(exampleSignatureEnvelope), exampleVerifyOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully verified")
// a peek of the payload inside the signature envelope
fmt.Println("payload ContentType:", outcome.EnvelopeContent.Payload.ContentType)
// Note, upon successful verification, payload.TargetArtifact from the
// signature envelope matches exactly with our exampleTargetDescriptor.
// (This check has been done for the user inside verifier.Verify.)
fmt.Println("payload Content:", string(outcome.EnvelopeContent.Payload.Content))
// Output:
// Successfully verified
// payload ContentType: application/vnd.cncf.notary.payload.v1+json
// payload Content: {"targetArtifact":{"digest":"sha384:b8ab24dafba5cf7e4c89c562f811cf10493d4203da982d3b1345f366ca863d9c2ed323dbd0fb7ff83a80302ceffa5a61","mediaType":"video/mp4","size":12}}
}
func createTrustStoreForBlobVerify() error {
// changing the path of the trust store for demo purpose.
// Users could keep the default value, i.e. os.UserConfigDir.
dir.UserConfigDir = "tmp"
// an example of a valid X509 self-signed certificate for demo purpose ONLY.
// (This self-signed cert is paired with the private key used to
// generate the `exampleSignatureEnvelopePem` above.)
// Users should replace `exampleX509Certificate` with their own trusted
// certificate and add to the trust store, following the
// Notary Project certificate requirements:
// https://github.com/notaryproject/specifications/tree/9c81dc773508dedc5a81c02c8d805de04f65050b/specs/signature-specification.md#certificate-requirements
exampleX509Certificate := `-----BEGIN CERTIFICATE-----
MIIEbDCCAtSgAwIBAgIBUzANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJVUzEL
MAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEl
MCMGA1UEAxMcTm90YXRpb24gRXhhbXBsZSBzZWxmLXNpZ25lZDAgFw0yNDA0MDQy
MTIwMjBaGA8yMTI0MDQwNDIxMjAyMFowZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
AldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxJTAjBgNVBAMT
HE5vdGF0aW9uIEV4YW1wbGUgc2VsZi1zaWduZWQwggGiMA0GCSqGSIb3DQEBAQUA
A4IBjwAwggGKAoIBgQDGIiN4yCjSVqFELZwxK/BMb8BokP587L8oPrZ1g8H7LudB
moLNDT7vF9xccbCfU3yNuOd0WaOgnENiCs81VHidyJsj1Oz3u+0Zn3ng7V+uZr6m
AIO74efA9ClMiY4i4HIt8IAZF57AL2mzDnCITgSWxikf030Il85MI42STvA+qYuz
ZEOp3XvKo8bDgQFvbtgK0HYYMfrka7VDmIWVo0rBMGm5btI8HOYQ0r9aqsrCxLAv
1AQeOQm+wbRcp4R5PIUJr+REGn7JCbOyXg/7qqHXKKmvV5yrGaraw8gZ5pqP/RHK
XUJIfvD0Vf2epJmsvC+6vXkSWtz+cA8J4GQx4J4SXL57hoYkC5qv39SOLzlWls3I
6fgeO+SZ0sceMd8NKlom/L5eOJBfB3bTQB83hq/3bRtjT7/qCMsL3VcndKkS+vGF
JPw5uTH+pmBgHrLr6tRoRRjwRFuZ0dO05AbdjCaxgVDtFI3wNbaXn/1VlRGySQIS
UNWxCrUsSzndeqwmjqsCAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
MAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBgQBdi0SaJAaeKBB0I+Fjcbmc
4zRvHE4GDSMSDnAK97nrZCZ9iwKuY4x6mv9lwQe2P3VXROoL9JmONNf0yaObOwQj
ILGnbe2rzYtUardz2gzh+6KNzJHspRvk1f06mp4496XQ3STMRSr8kno1svKQMy0Y
FRsGMKs4fWHavIAqNXg9ymrZvvXiatN2UiVtAA/jBFScZAWskeb2WHNzORi7H5Z1
mp5+IlNYQpzdIu/dvLVxzhh2UvkRdsQqsMgt/MOU84RncwUNZM4yI5EGPoaSJdsj
AGNd+UV6ur7QmVI2Q9EZNRlaDJtaoZmKns5j1SlmDXWKbdRmw42ORDudODj/pHA9
+u+ca9t3uLsbqO9yPm8m+6fyxffWS11QAH6O7EjydJWcEe5tYkPpL6kcaEyQKESm
5CDlsk+W3ElpaUu6tsnGKODvgdAN3m0noC+qxzCMqoCM4+M5V6OptR98MDl2FK0B
5+WF6YHBxf/uqDvFktUczjrIWuyfECywp05bpGAErGE=
-----END CERTIFICATE-----`
// Adding the certificate into the trust store.
if err := os.MkdirAll("tmp/truststore/x509/ca/valid-trust-store", 0700); err != nil {
return err
}
return os.WriteFile("tmp/truststore/x509/ca/valid-trust-store/NotationBlobExample.pem", []byte(exampleX509Certificate), 0600)
}
func getSignatureEnvelope() string {
return `{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEzODQ6YjhhYjI0ZGFmYmE1Y2Y3ZTRjODljNTYyZjgxMWNmMTA0OTNkNDIwM2RhOTgyZDNiMTM0NWYzNjZjYTg2M2Q5YzJlZDMyM2RiZDBmYjdmZjgzYTgwMzAyY2VmZmE1YTYxIiwibWVkaWFUeXBlIjoidmlkZW8vbXA0Iiwic2l6ZSI6MTJ9fQ","protected":"eyJhbGciOiJQUzM4NCIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI0LTA0LTA0VDE0OjIwOjIxLTA3OjAwIn0","header":{"x5c":["MIIEbDCCAtSgAwIBAgIBUzANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTElMCMGA1UEAxMcTm90YXRpb24gRXhhbXBsZSBzZWxmLXNpZ25lZDAgFw0yNDA0MDQyMTIwMjBaGA8yMTI0MDQwNDIxMjAyMFowZDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxJTAjBgNVBAMTHE5vdGF0aW9uIEV4YW1wbGUgc2VsZi1zaWduZWQwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDGIiN4yCjSVqFELZwxK/BMb8BokP587L8oPrZ1g8H7LudBmoLNDT7vF9xccbCfU3yNuOd0WaOgnENiCs81VHidyJsj1Oz3u+0Zn3ng7V+uZr6mAIO74efA9ClMiY4i4HIt8IAZF57AL2mzDnCITgSWxikf030Il85MI42STvA+qYuzZEOp3XvKo8bDgQFvbtgK0HYYMfrka7VDmIWVo0rBMGm5btI8HOYQ0r9aqsrCxLAv1AQeOQm+wbRcp4R5PIUJr+REGn7JCbOyXg/7qqHXKKmvV5yrGaraw8gZ5pqP/RHKXUJIfvD0Vf2epJmsvC+6vXkSWtz+cA8J4GQx4J4SXL57hoYkC5qv39SOLzlWls3I6fgeO+SZ0sceMd8NKlom/L5eOJBfB3bTQB83hq/3bRtjT7/qCMsL3VcndKkS+vGFJPw5uTH+pmBgHrLr6tRoRRjwRFuZ0dO05AbdjCaxgVDtFI3wNbaXn/1VlRGySQISUNWxCrUsSzndeqwmjqsCAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBgQBdi0SaJAaeKBB0I+Fjcbmc4zRvHE4GDSMSDnAK97nrZCZ9iwKuY4x6mv9lwQe2P3VXROoL9JmONNf0yaObOwQjILGnbe2rzYtUardz2gzh+6KNzJHspRvk1f06mp4496XQ3STMRSr8kno1svKQMy0YFRsGMKs4fWHavIAqNXg9ymrZvvXiatN2UiVtAA/jBFScZAWskeb2WHNzORi7H5Z1mp5+IlNYQpzdIu/dvLVxzhh2UvkRdsQqsMgt/MOU84RncwUNZM4yI5EGPoaSJdsjAGNd+UV6ur7QmVI2Q9EZNRlaDJtaoZmKns5j1SlmDXWKbdRmw42ORDudODj/pHA9+u+ca9t3uLsbqO9yPm8m+6fyxffWS11QAH6O7EjydJWcEe5tYkPpL6kcaEyQKESm5CDlsk+W3ElpaUu6tsnGKODvgdAN3m0noC+qxzCMqoCM4+M5V6OptR98MDl2FK0B5+WF6YHBxf/uqDvFktUczjrIWuyfECywp05bpGAErGE="],"io.cncf.notary.signingAgent":"example signing agent"},"signature":"liOjdgQ9BKuQTZGXRh3o6P8AMUIq_MKQReEcqA5h8M4RYs3DV_wXfaLCr2x_NRcwjTZsoO1_J77hmzkkk4L0IuFP8Qw0KKtmc83G0yFi4yYV5fwzrIbnhC2GRLuqLPnK-C4qYmv52ld3ebvo7XWwRHu30-VXePmTRFp6iG-eSAgkNgwhxSZ0ZmTFLG3ceNiX2bxpLHlXdPwA3aFKbd6nKrzo4CZ1ZyLNmAIaoA5-kmc0Hyt45trpxaaiWusI_pcTLw71YCqEAs32tEq3q6hRAgAZZN-Qvm9GyNp9EuaPiKjMbJFqtjome5ITxyNd-5t09dDCUgSe3t-iqv2Blm4E080AP1TYwUKLYklGniUP1dAtOau5G2juZLpl7tr4LQ99mycflnAmV7e79eEWXffvy5EAl77dW4_vM7lEemm08m2wddGuDOWXYb1j1r2_a5Xb92umHq6ZMhAp200A0pUkm9640x8z5jdudi_7KeezdqUK7ZMmSxHohiylyKD_20Cy"}`
}

View File

@ -0,0 +1,192 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notation_test
import (
"context"
"fmt"
"os"
_ "github.com/notaryproject/notation-core-go/signature/cose"
_ "github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/dir"
"github.com/notaryproject/notation-go/registry"
"github.com/notaryproject/notation-go/verifier"
"github.com/notaryproject/notation-go/verifier/trustpolicy"
"github.com/notaryproject/notation-go/verifier/truststore"
"oras.land/oras-go/v2/registry/remote"
)
// Example_verifyWithTimestamp demonstrates how to use notation.Verify to verify
// signature of an artifact including RFC 3161 compliant timestamp countersignature
func Example_verifyWithTimestamp() {
// exampleArtifactReference is an example of the target artifact reference
exampleArtifactReference := "localhost:5000/software@sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e"
// examplePolicyDocument is an example of a valid trust policy document with
// timestamping configurations.
// trust policy document should follow this spec:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-policy
examplePolicyDocument := trustpolicy.OCIDocument{
Version: "1.0",
TrustPolicies: []trustpolicy.OCITrustPolicy{
{
Name: "test-statement-name",
RegistryScopes: []string{"*"},
SignatureVerification: trustpolicy.SignatureVerification{
VerificationLevel: trustpolicy.LevelStrict.Name,
// verify timestamp countersignature only if the signing
// certificate chain has expired.
// Default: trustpolicy.OptionAlways
VerifyTimestamp: trustpolicy.OptionAfterCertExpiry,
},
// `tsa` trust store type MUST be configured to enable
// timestamp verification
TrustStores: []string{"ca:valid-trust-store", "tsa:valid-tsa"},
// TrustedIdentities only contains trusted identities of `ca`
// and `signingAuthority`
TrustedIdentities: []string{"*"},
},
},
}
// generateTrustStoreWithTimestamp generates a trust store directory for demo purpose.
// Users should configure their own trust store and add trusted certificates
// into it following the trust store spec:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/trust-store-trust-policy.md#trust-store
if err := generateTrustStoreWithTimestamp(); err != nil {
panic(err) // Handle error
}
// exampleVerifier is an example of notation.Verifier given
// trust policy document and X509 trust store.
exampleVerifier, err := verifier.New(&examplePolicyDocument, truststore.NewX509TrustStore(dir.ConfigFS()), nil)
if err != nil {
panic(err) // Handle error
}
// exampleRepo is an example of registry.Repository.
remoteRepo, err := remote.NewRepository(exampleArtifactReference)
if err != nil {
panic(err) // Handle error
}
exampleRepo := registry.NewRepository(remoteRepo)
// exampleVerifyOptions is an example of notation.VerifyOptions.
exampleVerifyOptions := notation.VerifyOptions{
ArtifactReference: exampleArtifactReference,
MaxSignatureAttempts: 50,
}
// remote verify core process
// upon successful verification, the target manifest descriptor
// and signature verification outcome are returned.
targetDesc, _, err := notation.Verify(context.Background(), exampleVerifier, exampleRepo, exampleVerifyOptions)
if err != nil {
panic(err) // Handle error
}
fmt.Println("Successfully verified")
fmt.Println("targetDesc MediaType:", targetDesc.MediaType)
fmt.Println("targetDesc Digest:", targetDesc.Digest)
fmt.Println("targetDesc Size:", targetDesc.Size)
}
func generateTrustStoreWithTimestamp() error {
// changing the path of the trust store for demo purpose.
// Users could keep the default value, i.e. os.UserConfigDir.
dir.UserConfigDir = "tmp"
// an example of a valid X509 self-signed certificate for demo purpose ONLY.
// Users should replace `exampleX509Certificate` with their own trusted
// certificate and add to the trust store, following the
// Notary Project certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleX509Certificate := `-----BEGIN CERTIFICATE-----
MIIDQDCCAiigAwIBAgIBUTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzEL
MAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEP
MA0GA1UEAxMGYWxwaW5lMCAXDTAwMDgyOTEzNTAwMFoYDzIxMjMwODI5MTM1MDAw
WjBOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUx
DzANBgNVBAoTBk5vdGFyeTEPMA0GA1UEAxMGYWxwaW5lMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAocg3qEsyNDDLfB8OHD4dhi+M1NPK1Asy5NX84c+g
vacZuoPLTwmpOfm6nPt7GPPB9G7S6xxhFNbRxTYfYUjK+kaCj38XjBRf5lGewbSJ
KVkxQ82/axU70ceSW3JpazrageN9JUTZ/Jfi4MfnipITwcmMoiij8eGrHskjyVyZ
bJd0WMMKRDWVhLPUiPMVWt/4d7YtZItzacaQKtXmXgsTCTWpIols3gftNYjrQoMs
UelUdD8vOAWN9J28/SyC+uSh/K1KfyUlbqufn4di8DEBxntP5wnXYbJL1jtjsUgE
xAVjQxT1zI59X36m3t3YKqCQh1cud02L5onObY6zj57N6QIDAQABoycwJTAOBgNV
HQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQAD
ggEBAC8AjBLy7EsRpi6oguCdFSb6nRGjvF17N+b6mDb3sARnB8T1pxvzTT26ya+A
yWR+jjodEwbMIS+13lV+9qT2LwqlbOUNY519Pa2GRRY72JjeowWI3iKkKaMzfZUB
7lRTGXdEuZApLbTO/3JVcR9ffu00N1UaAP9YGElSt4JDJYA9M+d/Qto+HiIsE0Kj
+jdnwIYovPPOlryKOLfFb/r1GEq7n63xFZz83iyWNaZdsJ5N3YHxdOpkbBbCalOE
BDJTjQKqeAYBLoANNU0OBslmqHCSBTEnhbqJHN6QKyF09ScOl5LwM1QsTl0UY5si
GLAfj/jSf9OH9VLTPHOS8/N0Ka4=
-----END CERTIFICATE-----`
// Adding the certificate into the trust store.
if err := os.MkdirAll("tmp/truststore/x509/ca/valid-trust-store", 0700); err != nil {
return err
}
if err := os.WriteFile("tmp/truststore/x509/ca/valid-trust-store/NotationExample.pem", []byte(exampleX509Certificate), 0600); err != nil {
return err
}
// an example of a valid TSA root certificate for demo purpose ONLY.
// Users should replace `exampleTSARootCertificate` with their own trusted
// TSA root certificate and add to the trust store, following the
// Notary Project certificate requirements:
// https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements
exampleTSARootCertificate := `-----BEGIN CERTIFICATE-----
MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
-----END CERTIFICATE-----`
// Adding the tsa root certificate into the trust store.
if err := os.MkdirAll("tmp/truststore/x509/tsa/valid-tsa", 0700); err != nil {
return err
}
return os.WriteFile("tmp/truststore/x509/tsa/valid-tsa/NotationTSAExample.pem", []byte(exampleTSARootCertificate), 0600)
}

23
go.mod
View File

@ -1,15 +1,26 @@
module github.com/notaryproject/notation-go
go 1.17
go 1.23.0
require (
github.com/go-ldap/ldap/v3 v3.4.3
github.com/golang-jwt/jwt/v4 v4.4.1
github.com/go-ldap/ldap/v3 v3.4.11
github.com/notaryproject/notation-core-go v1.3.0
github.com/notaryproject/notation-plugin-framework-go v1.0.0
github.com/notaryproject/tspclient-go v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1
github.com/veraison/go-cose v1.3.0
golang.org/x/crypto v0.39.0
golang.org/x/mod v0.25.0
oras.land/oras-go/v2 v2.6.0
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/sync v0.14.0 // indirect
)

75
go.sum
View File

@ -1,19 +1,60 @@
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ=
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.3 h1:JCKUtJPIcyOuG7ctGabLKMgIlKnGumD/iGjuWeEruDI=
github.com/go-ldap/ldap/v3 v3.4.3/go.mod h1:7LdHfVt6iIOESVEe3Bs4Jp2sHEKgDeduAhgM1/f9qmo=
github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/notaryproject/notation-core-go v1.3.0 h1:mWJaw1QBpBxpjLSiKOjzbZvB+xh2Abzk14FHWQ+9Kfs=
github.com/notaryproject/notation-core-go v1.3.0/go.mod h1:hzvEOit5lXfNATGNBT8UQRx2J6Fiw/dq/78TQL8aE64=
github.com/notaryproject/notation-plugin-framework-go v1.0.0 h1:6Qzr7DGXoCgXEQN+1gTZWuJAZvxh3p8Lryjn5FaLzi4=
github.com/notaryproject/notation-plugin-framework-go v1.0.0/go.mod h1:RqWSrTOtEASCrGOEffq0n8pSg2KOgKYiWqFWczRSics=
github.com/notaryproject/tspclient-go v1.0.0 h1:AwQ4x0gX8IHnyiZB1tggpn5NFqHpTEm1SDX8YNv4Dg4=
github.com/notaryproject/tspclient-go v1.0.0/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/veraison/go-cose v1.3.0 h1:2/H5w8kdSpQJyVtIhx8gmwPJ2uSz1PkyWFx0idbd7rk=
github.com/veraison/go-cose v1.3.0/go.mod h1:df09OV91aHoQWLmy1KsDdYiagtXgyAwAl8vFeFn1gMc=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc=
oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o=

39
internal/container/set.go Normal file
View File

@ -0,0 +1,39 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package set
// Set is a map as a set data structure.
type Set[T comparable] map[T]struct{}
// Add adds the element of type T into the Set.
func (s Set[T]) Add(elem T) {
s[elem] = struct{}{}
}
// Contains checks if element exists in the Set.
func (s Set[T]) Contains(elem T) bool {
_, ok := s[elem]
return ok
}
// New creates an empty Set for elements of type T.
func New[T comparable]() Set[T] {
return make(map[T]struct{})
}
// NewWithSize creates an empty Set of fixed size for elements of type T.
func NewWithSize[T comparable](size int) Set[T] {
return make(map[T]struct{}, size)
}

View File

@ -1,92 +0,0 @@
// Package cms verifies signatures in Cryptographic Message Syntax (CMS) / PKCS7
// defined in RFC 5652.
package cms
import (
"crypto/x509/pkix"
"encoding/asn1"
"math/big"
)
// ContentInfo ::= SEQUENCE {
// contentType ContentType,
// content [0] EXPLICIT ANY DEFINED BY contentType }
type ContentInfo struct {
ContentType asn1.ObjectIdentifier
Content asn1.RawValue `asn1:"explicit,tag:0"`
}
// SignedData ::= SEQUENCE {
// version CMSVersion,
// digestAlgorithms DigestAlgorithmIdentifiers,
// encapContentInfo EncapsulatedContentInfo,
// certificates [0] IMPLICIT CertificateSet OPTIONAL,
// crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
// signerInfos SignerInfos }
type SignedData struct {
Version int
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
EncapsulatedContentInfo EncapsulatedContentInfo
Certificates asn1.RawValue `asn1:"optional,tag:0"`
CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
SignerInfos []SignerInfo `asn1:"set"`
}
// EncapsulatedContentInfo ::= SEQUENCE {
// eContentType ContentType,
// eContent [0] EXPLICIT OCTET STRING OPTIONAL }
type EncapsulatedContentInfo struct {
ContentType asn1.ObjectIdentifier
Content []byte `asn1:"explicit,optional,tag:0"`
}
// SignerInfo ::= SEQUENCE {
// version CMSVersion,
// sid SignerIdentifier,
// digestAlgorithm DigestAlgorithmIdentifier,
// signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
// signatureAlgorithm SignatureAlgorithmIdentifier,
// signature SignatureValue,
// unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
// Only version 1 is supported. As defined in RFC 5652 5.3, SignerIdentifier
// is IssuerAndSerialNumber when version is 1.
type SignerInfo struct {
Version int
SignerIdentifier IssuerAndSerialNumber
DigestAlgorithm pkix.AlgorithmIdentifier
SignedAttributes Attributes `asn1:"optional,tag:0"`
SignatureAlgorithm pkix.AlgorithmIdentifier
Signature []byte
UnsignedAttributes Attributes `asn1:"optional,tag:1"`
}
// IssuerAndSerialNumber ::= SEQUENCE {
// issuer Name,
// serialNumber CertificateSerialNumber }
type IssuerAndSerialNumber struct {
Issuer asn1.RawValue
SerialNumber *big.Int
}
// Attribute ::= SEQUENCE {
// attrType OBJECT IDENTIFIER,
// attrValues SET OF AttributeValue }
type Attribute struct {
Type asn1.ObjectIdentifier
Values asn1.RawValue `asn1:"set"`
}
// Attribute ::= SET SIZE (1..MAX) OF Attribute
type Attributes []Attribute
// TryGet tries to find the attribute by the given identifier, parse and store
// the result in the value pointed to by out.
func (a Attributes) TryGet(identifier asn1.ObjectIdentifier, out interface{}) error {
for _, attribute := range a {
if identifier.Equal(attribute.Type) {
_, err := asn1.Unmarshal(attribute.Values.Bytes, out)
return err
}
}
return ErrAttributeNotFound
}

View File

@ -1,62 +0,0 @@
package cms
import "errors"
// ErrExpectSignedData is returned if wrong content is provided when signed
// data is expected.
var ErrExpectSignedData = errors.New("cms: signed data expected")
// ErrAttributeNotFound is returned if attribute is not found in a given set.
var ErrAttributeNotFound = errors.New("attribute not found")
// Verification errors
var (
ErrSignerNotFound = VerificationError{Message: "signer not found"}
ErrCertificateNotFound = VerificationError{Message: "certificate not found"}
)
// SyntaxError indicates that the ASN.1 data is invalid.
type SyntaxError struct {
Message string
Detail error
}
// Error returns error message.
func (e SyntaxError) Error() string {
msg := "cms: syntax error"
if e.Message != "" {
msg += ": " + e.Message
}
if e.Detail != nil {
msg += ": " + e.Detail.Error()
}
return msg
}
// Unwrap returns the internal error.
func (e SyntaxError) Unwrap() error {
return e.Detail
}
// VerificationError indicates verification failures.
type VerificationError struct {
Message string
Detail error
}
// Error returns error message.
func (e VerificationError) Error() string {
msg := "cms: verification failure"
if e.Message != "" {
msg += ": " + e.Message
}
if e.Detail != nil {
msg += ": " + e.Detail.Error()
}
return msg
}
// Unwrap returns the internal error.
func (e VerificationError) Unwrap() error {
return e.Detail
}

View File

@ -1,238 +0,0 @@
package cms
import (
"bytes"
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"time"
"github.com/notaryproject/notation-go/internal/crypto/hashutil"
"github.com/notaryproject/notation-go/internal/crypto/oid"
)
// ParsedSignedData is a parsed SignedData structure for golang friendly types.
type ParsedSignedData struct {
Content []byte
ContentType asn1.ObjectIdentifier
Certificates []*x509.Certificate
CRLs []pkix.CertificateList
Signers []SignerInfo
}
// ParseSignedData parses ASN.1 DER-encoded SignedData structure to golang friendly types.
func ParseSignedData(data []byte) (*ParsedSignedData, error) {
var contentInfo ContentInfo
if _, err := asn1.Unmarshal(data, &contentInfo); err != nil {
return nil, SyntaxError{Message: "invalid content info", Detail: err}
}
if !oid.SignedData.Equal(contentInfo.ContentType) {
return nil, ErrExpectSignedData
}
var signedData SignedData
if _, err := asn1.Unmarshal(contentInfo.Content.Bytes, &signedData); err != nil {
return nil, SyntaxError{Message: "invalid signed data", Detail: err}
}
certs, err := x509.ParseCertificates(signedData.Certificates.Bytes)
if err != nil {
return nil, SyntaxError{Message: "invalid signed data", Detail: err}
}
return &ParsedSignedData{
Content: signedData.EncapsulatedContentInfo.Content,
ContentType: signedData.EncapsulatedContentInfo.ContentType,
Certificates: certs,
CRLs: signedData.CRLs,
Signers: signedData.SignerInfos,
}, nil
}
// Verify attempts to verify the content in the parsed signed data against the signer
// information. The `Intermediates` in the verify options will be ignored and
// re-contrusted using the certificates in the parsed signed data.
// If more than one signature is present, the successful validation of any signature
// implies that the content in the parsed signed data is valid.
// On successful verification, the list of signing certificates that successfully
// verify is returned.
// If all signatures fail to verify, the last error is returned.
// References:
// - RFC 5652 5 Signed-data Content Type
// - RFC 5652 5.4 Message Digest Calculation Process
// - RFC 5652 5.6 Signature Verification Process
// WARNING: this function doesn't do any revocation checking.
func (d *ParsedSignedData) Verify(opts x509.VerifyOptions) ([]*x509.Certificate, error) {
if len(d.Signers) == 0 {
return nil, ErrSignerNotFound
}
if len(d.Certificates) == 0 {
return nil, ErrCertificateNotFound
}
intermediates := x509.NewCertPool()
for _, cert := range d.Certificates {
intermediates.AddCert(cert)
}
opts.Intermediates = intermediates
verifiedSignerMap := map[string]*x509.Certificate{}
var lastErr error
for _, signer := range d.Signers {
cert, err := d.verify(signer, opts)
if err != nil {
lastErr = err
continue
}
thumbprint, err := hashutil.ComputeHash(crypto.SHA256, cert.Raw)
if err != nil {
return nil, err
}
verifiedSignerMap[hex.EncodeToString(thumbprint)] = cert
}
if len(verifiedSignerMap) == 0 {
return nil, lastErr
}
verifiedSigners := make([]*x509.Certificate, 0, len(verifiedSignerMap))
for _, cert := range verifiedSignerMap {
verifiedSigners = append(verifiedSigners, cert)
}
return verifiedSigners, nil
}
// verify verifies the trust in a top-down manner.
// References:
// - RFC 5652 5.4 Message Digest Calculation Process
// - RFC 5652 5.6 Signature Verification Process
func (d *ParsedSignedData) verify(signer SignerInfo, opts x509.VerifyOptions) (*x509.Certificate, error) {
// find signer certificate
cert := d.getCertificate(signer.SignerIdentifier)
if cert == nil {
return nil, ErrCertificateNotFound
}
// verify signer certificate
if _, err := cert.Verify(opts); err != nil {
return cert, VerificationError{Detail: err}
}
// verify signature
return cert, d.verifySignature(signer, cert)
}
// verifySignature verifies the signature with a trusted certificate.
// References:
// - RFC 5652 5.4 Message Digest Calculation Process
// - RFC 5652 5.6 Signature Verification Process
func (d *ParsedSignedData) verifySignature(signer SignerInfo, cert *x509.Certificate) error {
// verify signature
algorithm := getSignatureAlgorithmFromOID(
signer.DigestAlgorithm.Algorithm,
signer.SignatureAlgorithm.Algorithm,
)
if algorithm == x509.UnknownSignatureAlgorithm {
return VerificationError{Message: "unknown signature algorithm"}
}
signed := d.Content
if len(signer.SignedAttributes) > 0 {
encoded, err := asn1.MarshalWithParams(signer.SignedAttributes, "set")
if err != nil {
return VerificationError{Message: "invalid signed attributes", Detail: err}
}
signed = encoded
}
if err := cert.CheckSignature(algorithm, signed, signer.Signature); err != nil {
return VerificationError{Detail: err}
}
// verify attributes if present
if len(signer.SignedAttributes) == 0 {
return nil
}
var contentType asn1.ObjectIdentifier
if err := signer.SignedAttributes.TryGet(oid.ContentType, &contentType); err != nil {
return VerificationError{Message: "invalid content type", Detail: err}
}
if !d.ContentType.Equal(contentType) {
return VerificationError{Message: "mismatch content type"}
}
var expectedDigest []byte
if err := signer.SignedAttributes.TryGet(oid.MessageDigest, &expectedDigest); err != nil {
return VerificationError{Message: "invalid message digest", Detail: err}
}
hash, ok := oid.ConvertToHash(signer.DigestAlgorithm.Algorithm)
if !ok {
return VerificationError{Message: "unsupported digest algorithm"}
}
actualDigest, err := hashutil.ComputeHash(hash, d.Content)
if err != nil {
return VerificationError{Message: "hash failure", Detail: err}
}
if !bytes.Equal(expectedDigest, actualDigest) {
return VerificationError{Message: "mismatch message digest"}
}
// sanity check on signing time
var signingTime time.Time
if err := signer.SignedAttributes.TryGet(oid.SigningTime, &signingTime); err != nil {
if err == ErrAttributeNotFound {
return nil
}
return VerificationError{Message: "invalid signing time", Detail: err}
}
if signingTime.Before(cert.NotBefore) || signingTime.After(cert.NotAfter) {
return VerificationError{Message: "signature signed when cert is inactive"}
}
return nil
}
// getCertificate finds the certificate by issuer name and issuer-specific
// serial number.
// Reference: RFC 5652 5 Signed-data Content Type
func (d *ParsedSignedData) getCertificate(ref IssuerAndSerialNumber) *x509.Certificate {
for _, cert := range d.Certificates {
if bytes.Equal(cert.RawIssuer, ref.Issuer.FullBytes) && cert.SerialNumber.Cmp(ref.SerialNumber) == 0 {
return cert
}
}
return nil
}
// getSignatureAlgorithmFromOID converts ASN.1 digest and signature algorithm identifiers
// to golang signature algorithms.
func getSignatureAlgorithmFromOID(digestAlg, sigAlg asn1.ObjectIdentifier) x509.SignatureAlgorithm {
switch {
case oid.RSA.Equal(sigAlg):
switch {
case oid.SHA1.Equal(digestAlg):
return x509.SHA1WithRSA
case oid.SHA256.Equal(digestAlg):
return x509.SHA256WithRSA
case oid.SHA384.Equal(digestAlg):
return x509.SHA384WithRSA
case oid.SHA512.Equal(digestAlg):
return x509.SHA512WithRSA
}
case oid.SHA1WithRSA.Equal(sigAlg):
return x509.SHA1WithRSA
case oid.SHA256WithRSA.Equal(sigAlg):
return x509.SHA256WithRSA
case oid.SHA384WithRSA.Equal(sigAlg):
return x509.SHA384WithRSA
case oid.SHA512WithRSA.Equal(sigAlg):
return x509.SHA512WithRSA
case oid.ECDSAWithSHA1.Equal(sigAlg):
return x509.ECDSAWithSHA1
case oid.ECDSAWithSHA256.Equal(sigAlg):
return x509.ECDSAWithSHA256
case oid.ECDSAWithSHA384.Equal(sigAlg):
return x509.ECDSAWithSHA384
case oid.ECDSAWithSHA512.Equal(sigAlg):
return x509.ECDSAWithSHA512
}
return x509.UnknownSignatureAlgorithm
}

View File

@ -1,95 +0,0 @@
package cms
import (
"crypto/x509"
"os"
"reflect"
"testing"
"time"
)
func TestVerifySignedData(t *testing.T) {
// parse signed data
sigBytes, err := os.ReadFile("testdata/TimeStampToken.p7s")
if err != nil {
t.Fatal("failed to read test signature:", err)
}
signed, err := ParseSignedData(sigBytes)
if err != nil {
t.Fatal("ParseSignedData() error =", err)
}
// basic check on parsed signed data
if got := len(signed.Certificates); got != 4 {
t.Fatalf("len(Certificates) = %v, want %v", got, 4)
}
if got := len(signed.Signers); got != 1 {
t.Fatalf("len(Signers) = %v, want %v", got, 1)
}
// verify with no root CAs and should fail
roots := x509.NewCertPool()
opts := x509.VerifyOptions{
Roots: roots,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping},
CurrentTime: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
}
if _, err := signed.Verify(opts); err == nil {
t.Errorf("ParseSignedData.Verify() error = %v, wantErr %v", err, true)
} else if vErr, ok := err.(VerificationError); !ok {
t.Errorf("ParseSignedData.Verify() error = %v, want VerificationError", err)
} else if _, ok := vErr.Detail.(x509.UnknownAuthorityError); !ok {
t.Errorf("ParseSignedData.Verify() VerificationError.Detail = %v, want UnknownAuthorityError", err)
}
// verify with proper root CA
rootCABytes, err := os.ReadFile("testdata/GlobalSignRootCA.crt")
if err != nil {
t.Fatal("failed to read root CA certificate:", err)
}
if ok := roots.AppendCertsFromPEM(rootCABytes); !ok {
t.Fatal("failed to load root CA certificate")
}
verifiedSigners, err := signed.Verify(opts)
if err != nil {
t.Fatal("ParseSignedData.Verify() error =", err)
}
if !reflect.DeepEqual(verifiedSigners, signed.Certificates[:1]) {
t.Fatalf("ParseSignedData.Verify() = %v, want %v", verifiedSigners, signed.Certificates[:1])
}
}
func TestVerifyCorruptedSignedData(t *testing.T) {
// parse signed data
sigBytes, err := os.ReadFile("testdata/TimeStampToken.p7s")
if err != nil {
t.Fatal("failed to read test signature:", err)
}
signed, err := ParseSignedData(sigBytes)
if err != nil {
t.Fatal("ParseSignedData() error =", err)
}
// corrupt the content
signed.Content = []byte("corrupted data")
// verify with no root CAs and should fail
roots := x509.NewCertPool()
rootCABytes, err := os.ReadFile("testdata/GlobalSignRootCA.crt")
if err != nil {
t.Fatal("failed to read root CA certificate:", err)
}
if ok := roots.AppendCertsFromPEM(rootCABytes); !ok {
t.Fatal("failed to load root CA certificate")
}
opts := x509.VerifyOptions{
Roots: roots,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping},
CurrentTime: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
}
if _, err := signed.Verify(opts); err == nil {
t.Errorf("ParseSignedData.Verify() error = %v, wantErr %v", err, true)
} else if _, ok := err.(VerificationError); !ok {
t.Errorf("ParseSignedData.Verify() error = %v, want VerificationError", err)
}
}

View File

@ -1,15 +0,0 @@
// Package hashutil provides utilities for hash.
package hashutil
import "crypto"
// ComputeHash computes the digest of the message with the given hash algorithm.
// Callers should check the availability of the hash algorithm before invoking.
func ComputeHash(hash crypto.Hash, message []byte) ([]byte, error) {
h := hash.New()
_, err := h.Write(message)
if err != nil {
return nil, err
}
return h.Sum(nil), nil
}

View File

@ -1,25 +0,0 @@
package oid
import (
"crypto"
"encoding/asn1"
)
// ConvertToHash converts ASN.1 digest algorithm identifier to golang crypto hash
// if it is available.
func ConvertToHash(alg asn1.ObjectIdentifier) (crypto.Hash, bool) {
var hash crypto.Hash
switch {
case SHA1.Equal(alg):
hash = crypto.SHA1
case SHA256.Equal(alg):
hash = crypto.SHA256
case SHA384.Equal(alg):
hash = crypto.SHA384
case SHA512.Equal(alg):
hash = crypto.SHA512
default:
return hash, false
}
return hash, hash.Available()
}

View File

@ -1,67 +0,0 @@
// Package oid collects object identifiers for crypto algorithms.
package oid
import "encoding/asn1"
// OIDs for hash algorithms
var (
// SHA1 (id-sha1) is defined in RFC 8017 B.1 Hash Functions
SHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
// SHA256 (id-sha256) is defined in RFC 8017 B.1 Hash Functions
SHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
// SHA384 (id-sha384) is defined in RFC 8017 B.1 Hash Functions
SHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
// SHA512 (id-sha512) is defined in RFC 8017 B.1 Hash Functions
SHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
)
// OIDs for signature algorithms
var (
// RSA is defined in RFC 8017 C ASN.1 Module
RSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
// SHA1WithRSA is defined in RFC 8017 C ASN.1 Module
SHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
// SHA256WithRSA is defined in RFC 8017 C ASN.1 Module
SHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
// SHA384WithRSA is defined in RFC 8017 C ASN.1 Module
SHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
// SHA512WithRSA is defined in RFC 8017 C ASN.1 Module
SHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
// ECDSAWithSHA1 is defined in ANSI X9.62
ECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
// ECDSAWithSHA256 is defined in RFC 5758 3.2 ECDSA Signature Algorithm
ECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
// ECDSAWithSHA384 is defined in RFC 5758 3.2 ECDSA Signature Algorithm
ECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
// ECDSAWithSHA512 is defined in RFC 5758 3.2 ECDSA Signature Algorithm
ECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
)
// OIDs defined in RFC 5652 Cryptographic Message Syntax (CMS)
var (
// SignedData (id-signedData) is defined in RFC 5652 5.1 SignedData Type
SignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
// ContentType (id-ct-contentType) is defined in RFC 5652 3 General Syntax
ContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
// MessageDigest (id-messageDigest) is defined in RFC 5652 11.2 Message Digest
MessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
// SigningTime (id-signingTime) is defined in RFC 5652 11.3 Signing Time
SigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
)
// TSTInfo (id-ct-TSTInfo) is defined in RFC 3161 2.4.2 Response Format
var TSTInfo = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 4}

View File

@ -1,50 +0,0 @@
// Package pki contains certificate management protocol structures
// defined in RFC 2510.
package pki
import "encoding/asn1"
// PKIStatus is defined in RFC 2510 3.2.3.
const (
StatusGranted = 0 // you got exactly what you asked for
StatusGrantedWithMods = 1 // you got something like what you asked for
StatusRejection = 2 // you don't get it, more information elsewhere in the message
StatusWaiting = 3 // the request body part has not yet been processed, expect to hear more later
StatusRevocationWarning = 4 // this message contains a warning that a revocation is imminent
StatusRevocationNotification = 5 // notification that a revocation has occurred
StatusKeyUpdateWarning = 6 // update already done for the oldCertId specified in the key update request message
)
// PKIFailureInfo is defined in RFC 2510 3.2.3 and RFC 3161 2.4.2.
const (
FailureInfoBadAlg = 0 // unrecognized or unsupported Algorithm Identifier
FailureInfoBadMessageCheck = 1 // integrity check failed (e.g., signature did not verify)
FailureInfoBadRequest = 2 // transaction not permitted or supported
FailureInfoBadTime = 3 // messageTime was not sufficiently close to the system time, as defined by local policy
FailureInfoBadCertID = 4 // no certificate could be found matching the provided criteria
FailureInfoBadDataFormat = 5 // the data submitted has the wrong format
FailureInfoWrongAuthority = 6 // the authority indicated in the request is different from the one creating the response token
FailureInfoIncorrectData = 7 // the requester's data is incorrect (used for notary services)
FailureInfoMissingTimeStamp = 8 // the timestamp is missing but should be there (by policy)
FailureInfoBadPOP = 9 // the proof-of-possession failed
FailureInfoTimeNotAvailable = 14 // the TSA's time source is not available
FailureInfoUnacceptedPolicy = 15 // the requested TSA policy is not supported by the TSA.
FailureInfoUnacceptedExtension = 16 // the requested extension is not supported by the TSA.
FailureInfoAddInfoNotAvailable = 17 // the additional information requested could not be understood or is not available
FailureInfoSystemFailure = 25 // the request cannot be handled due to system failure
)
// StatusInfo contains status codes and failure information for PKI messages.
// PKIStatusInfo ::= SEQUENCE {
// status PKIStatus,
// statusString PKIFreeText OPTIONAL,
// failInfo PKIFailureInfo OPTIONAL }
// PKIStatus ::= INTEGER
// PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
// PKIFailureInfo ::= BIT STRING
// Reference: RFC 2510 3.2.3 Status codes and Failure Information for PKI messages.
type StatusInfo struct {
Status int
StatusString []string `asn1:"optional,utf8"`
FailInfo asn1.BitString `asn1:"optional"`
}

View File

@ -1,54 +0,0 @@
// Package asn1 decodes BER-encoded ASN.1 data structures and encodes in DER.
// Note: DER is a subset of BER.
// Reference: http://luca.ntop.org/Teaching/Appunti/asn1.html
package asn1
import (
"bytes"
"encoding/asn1"
)
// Common errors
var (
ErrEarlyEOF = asn1.SyntaxError{Msg: "early EOF"}
ErrExpectConstructed = asn1.SyntaxError{Msg: "constructed value expected"}
ErrExpectPrimitive = asn1.SyntaxError{Msg: "primitive value expected"}
ErrUnsupportedLength = asn1.StructuralError{Msg: "length method not supported"}
)
// Value represents an ASN.1 value.
type Value interface {
// Encode encodes the value to the value writer in DER.
Encode(ValueWriter) error
// EncodedLen returns the length in bytes of the encoded data.
EncodedLen() int
}
// Decode decodes BER-encoded ASN.1 data structures.
func Decode(r ValueReader) (Value, error) {
peekIdentifier, err := r.ReadByte()
if err != nil {
return nil, err
}
if err = r.UnreadByte(); err != nil {
return nil, err
}
if isPrimitive(peekIdentifier) {
return DecodePrimitive(r)
}
return DecodeConstructed(r)
}
// ConvertToDER converts BER-encoded ASN.1 data structures to DER-encoded.
func ConvertToDER(ber []byte) ([]byte, error) {
v, err := Decode(bytes.NewReader(ber))
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(make([]byte, 0, v.EncodedLen()))
if err = v.Encode(buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@ -1,68 +0,0 @@
package asn1
import (
"encoding/asn1"
"reflect"
"testing"
)
func TestConvertToDER(t *testing.T) {
type data struct {
Type asn1.ObjectIdentifier
Value []byte
}
want := data{
Type: asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1},
Value: []byte{
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
},
}
ber := []byte{
// Constructed value
0x30,
// Constructed value length
0x2e,
// Type identifier
0x06,
// Type length
0x09,
// Type content
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
// Value identifier
0x04,
// Value length in BER
0x81, 0x20,
// Value content
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55,
}
der, err := ConvertToDER(ber)
if err != nil {
t.Errorf("ConvertToDER() error = %v", err)
return
}
var got data
rest, err := asn1.Unmarshal(der, &got)
if err != nil {
t.Errorf("Failed to decode converted data: %v", err)
return
}
if len(rest) > 0 {
t.Errorf("Unexpected rest data: %v", rest)
return
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got = %v, want %v", got, want)
}
}

View File

@ -1,116 +0,0 @@
package asn1
import "io"
// isPrimitive checks the primitive flag in the identifier.
// Returns true if the value is primitive.
func isPrimitive(identifier byte) bool {
return identifier&0x20 == 0
}
// encodedLengthSize gives the number of octets used for encoding the length.
func encodedLengthSize(length int) int {
if length < 0x80 {
return 1
}
lengthSize := 1
for ; length > 0; lengthSize++ {
length >>= 8
}
return lengthSize
}
// encodeLength encodes length octets in DER.
func encodeLength(w io.ByteWriter, length int) error {
// DER restriction: short form must be used for length less than 128
if length < 0x80 {
return w.WriteByte(byte(length))
}
// DER restriction: long form must be encoded in the minimum number of octets
lengthSize := encodedLengthSize(length)
err := w.WriteByte(0x80 | byte(lengthSize-1))
if err != nil {
return err
}
for i := lengthSize - 1; i > 0; i-- {
if err = w.WriteByte(byte(length >> (8 * (i - 1)))); err != nil {
return err
}
}
return nil
}
// decodeIdentifier decodes identifier octets.
func decodeIdentifier(r io.ByteReader) ([]byte, error) {
b, err := r.ReadByte()
if err != nil {
return nil, err
}
// low-tag-number form
identifier := []byte{b}
// high-tag-number form
if b&0x1f == 0x1f {
for {
b, err = r.ReadByte()
if err != nil {
if err == io.EOF {
return nil, ErrEarlyEOF
}
return nil, err
}
identifier = append(identifier, b)
if b&0x80 != 0 {
break
}
}
}
return identifier, nil
}
// decodeLength decodes length octets.
// Indefinite length is not supported
func decodeLength(r io.ByteReader) (int, error) {
b, err := r.ReadByte()
if err != nil {
if err == io.EOF {
return 0, ErrEarlyEOF
}
return 0, err
}
switch {
case b < 0x80:
// short form
return int(b), nil
case b == 0x80:
// Indefinite-length method is not supported.
return 0, ErrUnsupportedLength
}
// long form
n := int(b & 0x7f)
if n > 4 {
// length must fit the memory space of the int type.
return 0, ErrUnsupportedLength
}
var length int
for i := 0; i < n; i++ {
b, err = r.ReadByte()
if err != nil {
if err == io.EOF {
return 0, ErrEarlyEOF
}
return 0, err
}
length = (length << 8) | int(b)
}
if length < 0 {
// double check in case that length is over 31 bits.
return 0, ErrUnsupportedLength
}
return length, nil
}

View File

@ -1,172 +0,0 @@
package asn1
import (
"bytes"
"testing"
)
func Test_encodeLength(t *testing.T) {
tests := []struct {
name string
length int
want []byte
wantErr bool
}{
{
name: "zero length",
length: 0,
want: []byte{0x00},
},
{
name: "short form",
length: 42,
want: []byte{0x2a},
},
{
name: "short form in max",
length: 127,
want: []byte{0x7f},
},
{
name: "long form in min",
length: 128,
want: []byte{0x81, 0x80},
},
{
name: "long form",
length: 1234567890,
want: []byte{0x84, 0x49, 0x96, 0x02, 0xd2},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
buf := bytes.NewBuffer(nil)
if err := encodeLength(buf, tt.length); (err != nil) != tt.wantErr {
t.Errorf("encodeLength() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got := buf.Bytes(); !bytes.Equal(got, tt.want) {
t.Errorf("encoded length = %v, want %v", got, tt.want)
}
})
}
}
func Test_decodeIdentifier(t *testing.T) {
tests := []struct {
name string
encoded []byte
want []byte
wantErr bool
}{
{
name: "empty identifier",
wantErr: true,
},
{
name: "low-tag-number form",
encoded: []byte{0x0b},
want: []byte{0x0b},
},
{
name: "no extra read in low-tag-number form",
encoded: []byte{0x0b, 0x42},
want: []byte{0x0b},
},
{
name: "high-tag-number form",
encoded: []byte{0x1f, 0x17, 0xdf},
want: []byte{0x1f, 0x17, 0xdf}, // tag: 0x012345
},
{
name: "no extra read in high-tag-number form",
encoded: []byte{0x1f, 0x17, 0xdf, 0x42},
want: []byte{0x1f, 0x17, 0xdf}, // tag: 0x012345
},
{
name: "high-tag-number form (no termination)",
encoded: []byte{0x1f, 0x17, 0x5f},
wantErr: true,
},
{
name: "high-tag-number form (EOF)",
encoded: []byte{0x1f, 0x17},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := bytes.NewReader(tt.encoded)
got, err := decodeIdentifier(r)
if (err != nil) != tt.wantErr {
t.Errorf("decodeIdentifier() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !bytes.Equal(got, tt.want) {
t.Errorf("decodeIdentifier() = %v, want %v", got, tt.want)
}
})
}
}
func Test_decodeLength(t *testing.T) {
tests := []struct {
name string
encoded []byte
want int
wantErr bool
}{
{
name: "empty length",
wantErr: true,
},
{
name: "short form",
encoded: []byte{0x2a},
want: 42,
},
{
name: "no extra read in short form",
encoded: []byte{0x2a, 0x42},
want: 42,
},
{
name: "long form",
encoded: []byte{0x84, 0x49, 0x96, 0x02, 0xd2},
want: 1234567890,
},
{
name: "long form in BER",
encoded: []byte{0x81, 0x2a},
want: 42,
},
{
name: "no extra read in long form",
encoded: []byte{0x84, 0x49, 0x96, 0x02, 0xd2, 0x42},
want: 1234567890,
},
{
name: "long form (indefinite)",
encoded: []byte{0x80, 0x42, 0x00, 0x00},
wantErr: true,
},
{
name: "long form (EOF)",
encoded: []byte{0x84, 0x49, 0x96, 0x02},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := bytes.NewReader(tt.encoded)
got, err := decodeLength(r)
if (err != nil) != tt.wantErr {
t.Errorf("decodeLength() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("decodeLength() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -1,68 +0,0 @@
package asn1
import "io"
// ConstructedValue represents a value in constructed encoding.
type ConstructedValue struct {
identifier []byte
length int
members []Value
}
// Encode encodes the constructed value to the value writer in DER.
func (v ConstructedValue) Encode(w ValueWriter) error {
_, err := w.Write(v.identifier)
if err != nil {
return err
}
if err = encodeLength(w, v.length); err != nil {
return err
}
for _, value := range v.members {
if err = value.Encode(w); err != nil {
return err
}
}
return nil
}
// EncodedLen returns the length in bytes of the encoded data.
func (v ConstructedValue) EncodedLen() int {
return len(v.identifier) + encodedLengthSize(v.length) + v.length
}
// DecodeConstructed decodes a constructed value in BER.
func DecodeConstructed(r ValueReader) (Value, error) {
identifier, err := decodeIdentifier(r)
if err != nil {
return nil, err
}
if isPrimitive(identifier[0]) {
return nil, ErrExpectConstructed
}
expectedLength, err := decodeLength(r)
if err != nil {
return nil, err
}
var members []Value
encodedLength := 0
r = LimitValueReader(r, int64(expectedLength))
for {
value, err := Decode(r)
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
members = append(members, value)
encodedLength += value.EncodedLen()
}
return ConstructedValue{
identifier: identifier,
length: encodedLength,
members: members,
}, nil
}

View File

@ -1,51 +0,0 @@
package asn1
import "io"
// ValueReader is the interface for reading a value.
type ValueReader interface {
io.Reader
io.ByteScanner
}
// ValueWriter is the interface for writing a value.
type ValueWriter interface {
io.Writer
io.ByteWriter
}
// limitedValueReader limits the amount of data returned.
type limitedValueReader struct {
io.LimitedReader
S io.ByteScanner
}
// LimitValueReader returns a ValueReader, which limits the amount of data returned.
func LimitValueReader(r ValueReader, n int64) ValueReader {
return &limitedValueReader{
LimitedReader: io.LimitedReader{
R: r,
N: n,
},
S: r,
}
}
func (l *limitedValueReader) ReadByte() (c byte, err error) {
if l.N <= 0 {
return 0, io.EOF
}
c, err = l.S.ReadByte()
if err == nil {
l.N--
}
return
}
func (l *limitedValueReader) UnreadByte() (err error) {
err = l.S.UnreadByte()
if err == nil {
l.N++
}
return
}

View File

@ -1,55 +0,0 @@
package asn1
import "io"
// PrimitiveValue represents a value in primitive encoding.
type PrimitiveValue struct {
identifier []byte
content []byte
}
// Encode encodes the primitive value to the value writer in DER.
func (v PrimitiveValue) Encode(w ValueWriter) error {
_, err := w.Write(v.identifier)
if err != nil {
return err
}
if err = encodeLength(w, len(v.content)); err != nil {
return err
}
_, err = w.Write(v.content)
return err
}
// EncodedLen returns the length in bytes of the encoded data.
func (v PrimitiveValue) EncodedLen() int {
return len(v.identifier) + encodedLengthSize(len(v.content)) + len(v.content)
}
// DecodePrimitive decodes a primitive value in BER.
func DecodePrimitive(r ValueReader) (Value, error) {
identifier, err := decodeIdentifier(r)
if err != nil {
return nil, err
}
if !isPrimitive(identifier[0]) {
return nil, ErrExpectPrimitive
}
length, err := decodeLength(r)
if err != nil {
return nil, err
}
content := make([]byte, length)
_, err = io.ReadFull(r, content)
if err != nil {
if err == io.EOF {
return nil, ErrEarlyEOF
}
return nil, err
}
return PrimitiveValue{
identifier: identifier,
content: content,
}, nil
}

View File

@ -0,0 +1,68 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package envelope
import (
"errors"
"fmt"
"time"
"github.com/notaryproject/notation-core-go/signature"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// MediaTypePayloadV1 is the supported content type for signature's payload.
const (
MediaTypePayloadV1 = "application/vnd.cncf.notary.payload.v1+json"
AnnotationX509ChainThumbprint = "io.cncf.notary.x509chain.thumbprint#S256"
)
// Payload describes the content that gets signed.
type Payload struct {
TargetArtifact ocispec.Descriptor `json:"targetArtifact"`
}
// ValidatePayloadContentType validates signature payload's content type.
func ValidatePayloadContentType(payload *signature.Payload) error {
switch payload.ContentType {
case MediaTypePayloadV1:
return nil
default:
return fmt.Errorf("payload content type %q not supported", payload.ContentType)
}
}
// SanitizeTargetArtifact filters out unrelated ocispec.Descriptor fields based
// on notation spec (https://github.com/notaryproject/notaryproject/blob/main/specs/signature-specification.md#payload).
func SanitizeTargetArtifact(targetArtifact ocispec.Descriptor) ocispec.Descriptor {
return ocispec.Descriptor{
MediaType: targetArtifact.MediaType,
Digest: targetArtifact.Digest,
Size: targetArtifact.Size,
Annotations: targetArtifact.Annotations,
}
}
// SigningTime returns the signing time of a signature envelope blob
func SigningTime(signerInfo *signature.SignerInfo) (time.Time, error) {
// sanity check
if signerInfo == nil {
return time.Time{}, errors.New("failed to generate annotations: signerInfo cannot be nil")
}
signingTime := signerInfo.SignedAttributes.SigningTime
if signingTime.IsZero() {
return time.Time{}, errors.New("signing time is missing")
}
return signingTime.UTC(), nil
}

View File

@ -0,0 +1,141 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package envelope
import (
"errors"
"testing"
"time"
"github.com/notaryproject/notation-core-go/signature"
"github.com/notaryproject/notation-core-go/signature/cose"
"github.com/notaryproject/notation-core-go/signature/jws"
gcose "github.com/veraison/go-cose"
)
var (
validCoseSignatureEnvelope []byte
)
func init() {
msg := gcose.Sign1Message{
Headers: gcose.NewSign1Message().Headers,
Payload: []byte("valid"),
Signature: []byte("valid"),
}
validCoseSignatureEnvelope, _ = msg.MarshalCBOR()
}
const invalidMediaType = "invalid"
func checkErrorEqual(expected, got error) bool {
if expected == nil && got == nil {
return true
}
if expected != nil && got != nil {
return expected.Error() == got.Error()
}
return false
}
func TestValidateEnvelopeMediaType(t *testing.T) {
tests := []struct {
name string
mediaType string
expectedErr error
}{
{
name: "jws signature media type",
mediaType: jws.MediaTypeEnvelope,
expectedErr: nil,
},
{
name: "cose signature media type",
mediaType: cose.MediaTypeEnvelope,
expectedErr: nil,
},
{
name: "invalid media type",
mediaType: invalidMediaType,
expectedErr: errors.New("invalid envelope media type"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := validateEnvelopeMediaType(tt.mediaType); !checkErrorEqual(tt.expectedErr, err) {
t.Fatalf("expected validate envelope media type err: %v, got: %v", tt.expectedErr, err)
}
})
}
}
func TestValidatePayloadContentType(t *testing.T) {
payload := &signature.Payload{
ContentType: MediaTypePayloadV1,
}
err := ValidatePayloadContentType(payload)
if !isErrEqual(nil, err) {
t.Fatalf("ValidatePayloadContentType() expects error: %v, but got: %v.", nil, err)
}
payload = &signature.Payload{
ContentType: "invalid",
}
err = ValidatePayloadContentType(payload)
expect := errors.New("payload content type \"invalid\" not supported")
if !isErrEqual(expect, err) {
t.Fatalf("ValidatePayloadContentType() expects error: %v, but got: %v.", expect, err)
}
}
func TestSigningTime(t *testing.T) {
testTime, err := time.Parse(time.RFC3339, "2023-03-14T04:45:22Z")
if err != nil {
t.Fatal("failed to generate time")
}
signerInfo := signature.SignerInfo{
SignedAttributes: signature.SignedAttributes{
SigningTime: testTime,
},
}
signingTime, err := SigningTime(&signerInfo)
if err != nil {
t.Fatalf("failed to get signing time from signature: %v", err)
}
expectedSigningTime := "2023-03-14T04:45:22Z"
if signingTime.Format(time.RFC3339) != expectedSigningTime {
t.Fatalf("expected signing time: %q, got: %q", expectedSigningTime, signingTime.Format(time.RFC3339))
}
}
func isErrEqual(wanted, got error) bool {
if wanted == nil && got == nil {
return true
}
if wanted != nil && got != nil {
return wanted.Error() == got.Error()
}
return false
}
// validateEnvelopeMediaType validetes envelope media type is supported by
// notation-core-go.
func validateEnvelopeMediaType(mediaType string) error {
for _, types := range signature.RegisteredEnvelopeTypes() {
if mediaType == types {
return nil
}
}
return errors.New("invalid envelope media type")
}

153
internal/file/file.go Normal file
View File

@ -0,0 +1,153 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package file
import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"regexp"
"strings"
)
const (
// tempFileNamePrefix is the prefix of the temporary file
tempFileNamePrefix = "notation-*"
)
// ErrNotRegularFile is returned when the file is not an regular file.
var ErrNotRegularFile = errors.New("not regular file")
// ErrNotDirectory is returned when the path is not a directory.
var ErrNotDirectory = errors.New("not directory")
// IsValidFileName checks if a file name is cross-platform compatible
func IsValidFileName(fileName string) bool {
return regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`).MatchString(fileName)
}
// CopyToDir copies the src file to dst dir. All parent directories are created
// with permissions 0755.
//
// Source file's read and execute permissions are preserved for everyone.
// Write permission is preserved for owner. Group and others cannot write.
// Existing file will be overwritten.
func CopyToDir(src, dst string) error {
sourceFileInfo, err := os.Stat(src)
if err != nil {
return err
}
if !sourceFileInfo.Mode().IsRegular() {
return ErrNotRegularFile
}
source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
dstFile := filepath.Join(dst, filepath.Base(src))
destination, err := os.Create(dstFile)
if err != nil {
return err
}
defer destination.Close()
err = destination.Chmod(sourceFileInfo.Mode() & os.FileMode(0755))
if err != nil {
return err
}
_, err = io.Copy(destination, source)
return err
}
// CopyDirToDir copies contents in src dir to dst dir. Only regular files are
// copied. Existing files will be overwritten.
func CopyDirToDir(src, dst string) error {
fi, err := os.Stat(src)
if err != nil {
return err
}
if !fi.Mode().IsDir() {
return ErrNotDirectory
}
return filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
// skip sub-directories
if d.IsDir() && d.Name() != filepath.Base(path) {
return fs.SkipDir
}
info, err := d.Info()
if err != nil {
return err
}
// only copy regular files
if info.Mode().IsRegular() {
return CopyToDir(path, dst)
}
return nil
})
}
// TrimFileExtension returns the file name without extension.
//
// For example,
//
// when input is xyz.exe, output is xyz
//
// when input is xyz.tar.gz, output is xyz.tar
func TrimFileExtension(fileName string) string {
return strings.TrimSuffix(fileName, filepath.Ext(fileName))
}
// WriteFile writes content to a temporary file and moves it to path.
// If path already exists and is a file, WriteFile overwrites it.
//
// Parameters:
// - tempDir is the directory to create the temporary file. It should be
// in the same mount point as path. If tempDir is empty, the default
// directory for temporary files is used.
// - path is the destination file path.
// - content is the content to write.
func WriteFile(tempDir, path string, content []byte) (writeErr error) {
tempFile, err := os.CreateTemp(tempDir, tempFileNamePrefix)
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
defer func() {
// remove the temp file in case of error
if writeErr != nil {
tempFile.Close()
os.Remove(tempFile.Name())
}
}()
if _, err := tempFile.Write(content); err != nil {
return fmt.Errorf("failed to write content to temp file: %w", err)
}
// close before moving
if err := tempFile.Close(); err != nil {
return fmt.Errorf("failed to close temp file: %w", err)
}
// rename is atomic on UNIX-like platforms
return os.Rename(tempFile.Name(), path)
}

214
internal/file/file_test.go Normal file
View File

@ -0,0 +1,214 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package file
import (
"bytes"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)
func TestCopyToDir(t *testing.T) {
t.Run("copy file", func(t *testing.T) {
tempDir := t.TempDir()
data := []byte("data")
filename := filepath.Join(tempDir, "a", "file.txt")
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(tempDir, filename, data); err != nil {
t.Fatal(err)
}
destDir := filepath.Join(tempDir, "b")
if err := CopyToDir(filename, destDir); err != nil {
t.Fatal(err)
}
})
t.Run("source directory permission error", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
tempDir := t.TempDir()
destDir := t.TempDir()
data := []byte("data")
filename := filepath.Join(tempDir, "a", "file.txt")
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(tempDir, filename, data); err != nil {
t.Fatal(err)
}
if err := os.Chmod(tempDir, 0000); err != nil {
t.Fatal(err)
}
defer os.Chmod(tempDir, 0700)
if err := CopyToDir(filename, destDir); err == nil {
t.Fatal("should have error")
}
})
t.Run("not a regular file", func(t *testing.T) {
tempDir := t.TempDir()
destDir := t.TempDir()
if err := CopyToDir(tempDir, destDir); err == nil {
t.Fatal("should have error")
}
})
t.Run("source file permission error", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
tempDir := t.TempDir()
destDir := t.TempDir()
data := []byte("data")
// prepare file
filename := filepath.Join(tempDir, "a", "file.txt")
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(tempDir, filename, data); err != nil {
t.Fatal(err)
}
// forbid reading
if err := os.Chmod(filename, 0000); err != nil {
t.Fatal(err)
}
defer os.Chmod(filename, 0600)
if err := CopyToDir(filename, destDir); err == nil {
t.Fatal("should have error")
}
})
t.Run("dest directory permission error", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
tempDir := t.TempDir()
destTempDir := t.TempDir()
data := []byte("data")
// prepare file
filename := filepath.Join(tempDir, "a", "file.txt")
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(tempDir, filename, data); err != nil {
t.Fatal(err)
}
// forbid dest directory operation
if err := os.Chmod(destTempDir, 0000); err != nil {
t.Fatal(err)
}
defer os.Chmod(destTempDir, 0700)
if err := CopyToDir(filename, filepath.Join(destTempDir, "a")); err == nil {
t.Fatal("should have error")
}
})
t.Run("dest directory permission error 2", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
tempDir := t.TempDir()
destTempDir := t.TempDir()
data := []byte("data")
// prepare file
filename := filepath.Join(tempDir, "a", "file.txt")
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(tempDir, filename, data); err != nil {
t.Fatal(err)
}
// forbid writing to destTempDir
if err := os.Chmod(destTempDir, 0000); err != nil {
t.Fatal(err)
}
defer os.Chmod(destTempDir, 0700)
if err := CopyToDir(filename, destTempDir); err == nil {
t.Fatal("should have error")
}
})
t.Run("copy file and check content", func(t *testing.T) {
tempDir := t.TempDir()
data := []byte("data")
filename := filepath.Join(tempDir, "a", "file.txt")
if err := os.MkdirAll(filepath.Dir(filename), 0700); err != nil {
t.Fatal(err)
}
if err := WriteFile(tempDir, filename, data); err != nil {
t.Fatal(err)
}
destDir := filepath.Join(tempDir, "b")
if err := CopyToDir(filename, destDir); err != nil {
t.Fatal(err)
}
validFileContent(t, filepath.Join(destDir, "file.txt"), data)
})
}
func TestFileNameWithoutExtension(t *testing.T) {
input := "testfile.tar.gz"
expectedOutput := "testfile.tar"
actualOutput := TrimFileExtension(input)
if actualOutput != expectedOutput {
t.Errorf("expected '%s', but got '%s'", expectedOutput, actualOutput)
}
}
func TestWriteFile(t *testing.T) {
tempDir := t.TempDir()
content := []byte("test WriteFile")
t.Run("permission denied", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
err := os.Chmod(tempDir, 0)
if err != nil {
t.Fatal(err)
}
err = WriteFile(tempDir, filepath.Join(tempDir, "testFile"), content)
if err == nil || !strings.Contains(err.Error(), "permission denied") {
t.Fatalf("expected permission denied error, but got %s", err)
}
err = os.Chmod(tempDir, 0700)
if err != nil {
t.Fatal(err)
}
})
}
func validFileContent(t *testing.T, filename string, content []byte) {
b, err := os.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(content, b) {
t.Fatal("file content is not correct")
}
}

View File

@ -0,0 +1,53 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package io provides a LimitWriter that writes to an underlying writer up to
// a limit.
package io
import (
"errors"
"io"
)
// ErrLimitExceeded is returned when the write limit is exceeded.
var ErrLimitExceeded = errors.New("write limit exceeded")
// LimitedWriter is a writer that writes to an underlying writer up to a limit.
type LimitedWriter struct {
W io.Writer // underlying writer
N int64 // remaining bytes
}
// LimitWriter returns a new LimitWriter that writes to w.
//
// parameters:
// w: the writer to write to
// limit: the maximum number of bytes to write
func LimitWriter(w io.Writer, limit int64) *LimitedWriter {
return &LimitedWriter{W: w, N: limit}
}
// Write writes p to the underlying writer up to the limit.
func (l *LimitedWriter) Write(p []byte) (int, error) {
if l.N <= 0 {
return 0, ErrLimitExceeded
}
if int64(len(p)) > l.N {
p = p[:l.N]
}
n, err := l.W.Write(p)
l.N -= int64(n)
return n, err
}

View File

@ -0,0 +1,67 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package io
import (
"bytes"
"errors"
"testing"
)
func TestLimitWriter(t *testing.T) {
limit := int64(10)
tests := []struct {
input string
expected string
written int
}{
{"hello", "hello", 5},
{" world", " world", 6},
{"!", "!", 1},
{"1234567891011", "1234567891", 10},
}
for _, tt := range tests {
var buf bytes.Buffer
lw := LimitWriter(&buf, limit)
n, err := lw.Write([]byte(tt.input))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if n != tt.written {
t.Errorf("expected %d bytes written, got %d", tt.written, n)
}
if buf.String() != tt.expected {
t.Errorf("expected buffer %q, got %q", tt.expected, buf.String())
}
}
}
func TestLimitWriterFailed(t *testing.T) {
limit := int64(10)
longString := "1234567891011"
var buf bytes.Buffer
lw := LimitWriter(&buf, limit)
_, err := lw.Write([]byte(longString))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
_, err = lw.Write([]byte(longString))
expectedErr := errors.New("write limit exceeded")
if err.Error() != expectedErr.Error() {
t.Errorf("expected error %v, got %v", expectedErr, err)
}
}

View File

@ -0,0 +1,48 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mockfs
import (
"io/fs"
"path/filepath"
"github.com/notaryproject/notation-go/dir"
)
type sysFSMock struct {
fs.FS
root string
}
// SysPath returns the system path of the FS.
func (s sysFSMock) SysPath(items ...string) (string, error) {
pathItems := []string{s.root}
pathItems = append(pathItems, items...)
return filepath.Join(pathItems...), nil
}
// NewSysFSMock returns a SysFS mock of the given FS.
func NewSysFSMock(fsys fs.FS) dir.SysFS {
return sysFSMock{
FS: fsys,
root: ""}
}
// NewSysFSWithRootMock returns a SysFS mock of the given fs and
// a root directory
func NewSysFSWithRootMock(fsys fs.FS, root string) dir.SysFS {
return sysFSMock{
FS: fsys,
root: root}
}

228
internal/mock/mocks.go Normal file
View File

@ -0,0 +1,228 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mock
import (
"context"
_ "embed"
"github.com/notaryproject/notation-core-go/signature"
"github.com/notaryproject/notation-plugin-framework-go/plugin"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
//go:embed testdata/ca_valid_sig_env.json
var MockCaValidSigEnv []byte
//go:embed testdata/ca_invalid_sig_env.json
var MockCaInvalidSigEnv []byte
//go:embed testdata/sa_valid_sig_env.json
var MockSaValidSigEnv []byte
//go:embed testdata/ca_plugin_sig_env.json
var MockCaPluginSigEnv []byte // extended attributes are "SomeKey":"SomeValue", "io.cncf.notary.verificationPlugin":"plugin-name"
//go:embed testdata/sa_invalid_sig_env.json
var MockSaInvalidSigEnv []byte
//go:embed testdata/ca_expired_sig_env.json
var MockCaExpiredSigEnv []byte
//go:embed testdata/sa_expired_sig_env.json
var MockSaExpiredSigEnv []byte
//go:embed testdata/sa_plugin_sig_env.json
var MockSaPluginSigEnv []byte // extended attributes are "SomeKey":"SomeValue", "io.cncf.notary.verificationPlugin":"plugin-name"
//go:embed testdata/sig_env_with_metadata.json
var MockSigEnvWithMetadata []byte
//go:embed testdata/ca_incompatible_pluginver_sig_env_1.0.9.json
var MockCaIncompatiblePluginVerSigEnv_1_0_9 []byte
//go:embed testdata/ca_incompatible_pluginver_sig_env_1.0.1.json
var MockCaIncompatiblePluginVerSigEnv_1_0_1 []byte
//go:embed testdata/ca_incompatible_pluginver_sig_env_1.2.3.json
var MockCaIncompatiblePluginVerSigEnv_1_2_3 []byte
//go:embed testdata/ca_incompatible_pluginver_sig_env_1.1.0-alpha.json
var MockCaIncompatiblePluginVerSigEnv_1_1_0_alpha []byte
//go:embed testdata/ca_compatible_pluginver_sig_env_0.0.9.json
var MockCaCompatiblePluginVerSigEnv_0_0_9 []byte
//go:embed testdata/ca_compatible_pluginver_sig_env_1.0.0-alpha.json
var MockCaCompatiblePluginVerSigEnv_1_0_0_alpha []byte
//go:embed testdata/ca_compatible_pluginver_sig_env_1.0.0-alpha.beta.json
var MockCaCompatiblePluginVerSigEnv_1_0_0_alpha_beta []byte
//go:embed testdata/ca_compatible_pluginver_sig_env_1.0.0.json
var MockCaCompatiblePluginVerSigEnv_1_0_0 []byte
var (
SampleArtifactUri = "registry.acme-rockets.io/software/net-monitor@sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e"
SampleDigest = digest.Digest("sha256:60043cf45eaebc4c0867fea485a039b598f52fd09fd5b07b0b2d2f88fad9d74e")
ZeroDigest = digest.Digest("sha256:0000000000000000000000000000000000000000000000000000000000000000")
Annotations = map[string]string{"key": "value"}
ImageDescriptor = ocispec.Descriptor{
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Digest: SampleDigest,
Size: 528,
Annotations: Annotations,
}
SigManfiestDescriptor = ocispec.Descriptor{
MediaType: "application/vnd.oci.image.manifest.v1+json",
Digest: SampleDigest,
Size: 300,
Annotations: Annotations,
}
TestImageDescriptor = ocispec.Descriptor{
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Digest: digest.Digest("sha256:fe7e9333395060c2f5e63cf36a38fba10176f183b4163a5794e081a480abba5f"),
Size: 942,
Annotations: nil,
}
JwsSigEnvDescriptor = ocispec.Descriptor{
MediaType: "application/jose+json",
Digest: SampleDigest,
Size: 100,
Annotations: Annotations,
}
PluginExtendedCriticalAttribute = signature.Attribute{
Key: "SomeKey",
Critical: true,
Value: "SomeValue",
}
MetadataSigEnvDescriptor = ocispec.Descriptor{
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Digest: digest.Digest("sha256:5a07385af4e6b6af81b0ebfd435aedccdfa3507f0609c658209e1aba57159b2b"),
Size: 942,
Annotations: map[string]string{"io.wabbit-networks.buildId": "123", "io.wabbit-networks.buildTime": "1672944615"},
}
)
type Repository struct {
ResolveResponse ocispec.Descriptor
ResolveError error
ListSignaturesResponse []ocispec.Descriptor
ListSignaturesError error
FetchSignatureBlobResponse []byte
FetchSignatureBlobError error
MissMatchDigest bool
ExceededNumOfSignatures bool
PushSignatureError error
}
func NewRepository() Repository {
return Repository{
ResolveResponse: ImageDescriptor,
ListSignaturesResponse: []ocispec.Descriptor{SigManfiestDescriptor},
FetchSignatureBlobResponse: MockCaValidSigEnv,
}
}
func (t Repository) Resolve(ctx context.Context, reference string) (ocispec.Descriptor, error) {
if t.MissMatchDigest {
return ocispec.Descriptor{
MediaType: "application/vnd.docker.distribution.manifest.v2+json",
Digest: ZeroDigest,
Size: 528,
Annotations: Annotations,
}, nil
}
return t.ResolveResponse, t.ResolveError
}
func (t Repository) ListSignatures(ctx context.Context, desc ocispec.Descriptor, fn func(signatureManifests []ocispec.Descriptor) error) error {
if t.ExceededNumOfSignatures {
t.ListSignaturesResponse = []ocispec.Descriptor{SigManfiestDescriptor, SigManfiestDescriptor}
}
err := fn(t.ListSignaturesResponse)
if err != nil {
return err
}
return t.ListSignaturesError
}
func (t Repository) FetchSignatureBlob(ctx context.Context, desc ocispec.Descriptor) ([]byte, ocispec.Descriptor, error) {
return t.FetchSignatureBlobResponse, JwsSigEnvDescriptor, t.FetchSignatureBlobError
}
func (t Repository) PushSignature(ctx context.Context, mediaType string, blob []byte, subject ocispec.Descriptor, annotations map[string]string) (blobDesc, manifestDesc ocispec.Descriptor, err error) {
if t.PushSignatureError != nil {
return ocispec.Descriptor{}, ocispec.Descriptor{}, t.PushSignatureError
}
return ocispec.Descriptor{}, ocispec.Descriptor{}, nil
}
type PluginMock struct {
Metadata plugin.GetMetadataResponse
ExecuteResponse interface{}
ExecuteError error
}
func (p *PluginMock) GetMetadata(ctx context.Context, req *plugin.GetMetadataRequest) (*plugin.GetMetadataResponse, error) {
return &p.Metadata, nil
}
func (p *PluginMock) VerifySignature(ctx context.Context, req *plugin.VerifySignatureRequest) (*plugin.VerifySignatureResponse, error) {
if resp, ok := p.ExecuteResponse.(*plugin.VerifySignatureResponse); ok {
return resp, nil
}
return nil, p.ExecuteError
}
func (p *PluginMock) DescribeKey(ctx context.Context, req *plugin.DescribeKeyRequest) (*plugin.DescribeKeyResponse, error) {
panic("not implemented") // TODO: Implement
}
func (p *PluginMock) GenerateSignature(ctx context.Context, req *plugin.GenerateSignatureRequest) (*plugin.GenerateSignatureResponse, error) {
panic("not implemented") // TODO: Implement
}
func (p *PluginMock) GenerateEnvelope(ctx context.Context, req *plugin.GenerateEnvelopeRequest) (*plugin.GenerateEnvelopeResponse, error) {
panic("not implemented") // TODO: Implement
}
type PluginManager struct {
PluginCapabilities []plugin.Capability
GetPluginError error
PluginRunnerLoadError error
PluginRunnerExecuteResponse interface{}
PluginRunnerExecuteError error
}
func (pm PluginManager) Get(ctx context.Context, name string) (plugin.Plugin, error) {
return &PluginMock{
Metadata: plugin.GetMetadataResponse{
Name: "plugin-name",
Description: "for mocking in unit tests",
Version: "1.0.0",
URL: ".",
SupportedContractVersions: []string{"1.0"},
Capabilities: pm.PluginCapabilities,
},
ExecuteResponse: pm.PluginRunnerExecuteResponse,
ExecuteError: pm.PluginRunnerExecuteError,
}, pm.GetPluginError
}
func (pm PluginManager) List(ctx context.Context) ([]string, error) {
panic("not implemented")
}

View File

@ -0,0 +1,47 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ocilayout
import (
"context"
"os"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/content/oci"
)
// Copy creates a temporary OCI layout for testing
// and returns the path to the layout.
func Copy(sourcePath, destPath, tag string) error {
ctx := context.Background()
srcStore, err := oci.NewFromFS(ctx, os.DirFS(sourcePath))
if err != nil {
return err
}
// create a dest store for store the generated oci layout.
destStore, err := oci.New(destPath)
if err != nil {
return err
}
// copy data
_, err = oras.ExtendedCopy(ctx, srcStore, tag, destStore, "", oras.DefaultExtendedCopyOptions)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,64 @@
// Copyright The Notary Project Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ocilayout
import (
"os"
"runtime"
"testing"
)
func TestCopy(t *testing.T) {
t.Run("empty oci layout", func(t *testing.T) {
err := Copy("", "", "v2")
if err == nil {
t.Errorf("expected error, got nil")
}
})
t.Run("invalid target path permission", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("skipping test on Windows")
}
tempDir := t.TempDir()
// change the permission of the tempDir to make it invalid
if err := os.Chmod(tempDir, 0); err != nil {
t.Fatalf("failed to change the permission of the tempDir: %v", err)
}
err := Copy("../../testdata/oci-layout", tempDir, "v2")
if err == nil {
t.Errorf("expected error, got nil")
}
if err := os.Chmod(tempDir, 0755); err != nil {
t.Fatalf("failed to change the permission of the tempDir: %v", err)
}
})
t.Run("copy failed", func(t *testing.T) {
tempDir := t.TempDir()
err := Copy("../../testdata/oci-layout", tempDir, "v3")
if err == nil {
t.Errorf("expected error, got nil")
}
})
t.Run("copy success", func(t *testing.T) {
tempDir := t.TempDir()
err := Copy("../../testdata/oci-layout", tempDir, "v2")
if err != nil {
t.Errorf("expected nil, got %v", err)
}
})
}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQwMDoyMTozNi0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjAuMC45In0","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"VXWolnwKhCSYn1x1_0CUpvUxEihiuKCJ9Ae2Lm--gkW_tfbBlTzkq4TciXM4u4V9MBtbDczQ8k748tmEb7qbzPPT6CEPGMBX8WN7kDStqXGILmpIE5M7Z1nYVIYkgQPk_w6FyC291bluQQGu0yqNrAO3pT1Ym5DoHAyRHLROdDRChntI4Qrz5DGrjBsiibo_GAOxw1jY1ENvo5dlSTAgnZm9jkfbY0gsYTXuNGYk2atS0H1W_MVRdgDSI9gbQ6amLUf-qy_gcbl5UT8Pa5fWb_1KZPtAqoh4hA5PW4UKkxFE0Wz2pUAs9RpYI-xpw1B6KGtgiI9MuTYDFMmTXHBRig"}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQxMzowMzoyMy0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjEuMC4wLWFscGhhLmJldGEifQ","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"iJthtqbz0O5nFuo5Z9nRddEjyZp3RG-KOY6SSB3sc8AgDBdT5Fjp9yltIoqTl-BLZhrGOAFeO0T_1JVsPbZZMxzJq4fb3gPaIPItrendkpit1m2RaB8fK1D_I6Vqu1_rGiYaxDcNpaqn1T_isxr4MVRekcLSNQnG3iMdJ0k-Attf8JdCXE0EWKyLBStMVAfo0J39ShFcwyIMvO0vm2_TRDVbcKovpY0vFrfyE2pFIChnJECmivImdKmBMIW78vEtN6qBrKskI3HzA9N1XjxGY4GOAu30iqtNRanO65nZGng0lqpJd15bAwUaqj-KD_BAZIUT9T2qCf2COF9GKvc3NQ"}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQxMjo1NzowNC0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjEuMC4wLWFscGhhIn0","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"xZqE2HZye4qPmG688z875mHySGV_MoWOb99wChu-hInU8-CyxMesxzVCo_boG3Oae6tj6MKwdJ-Dj2cKbI3S4aX2l6t5IRFLB5z4DuIsDhmKZj9iN5LjtP8ua5_fni9dBk4e9c9TAsMq1hjXyNEen2rC1dzP_bcNYnoOs1yRWpO4JAcsslMYeqUIKKf39kzlOxOKIsJ8YhZoNeRc3HnAu4hlX2XpXwArovvMZtg1Akp6qCjVQcQQUTb_M0JeytmR8R5tdr_ZYqh-rCWbIe5tNU4u9jCP8xvlXPdSjpHgpmPsEnNd4u4gnLFxuYAq5l3UkdGDLXUsGrTx_Bi_LoFHUQ"}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQxMzowNTo1OC0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjEuMC4wIn0","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"JG7Gk8HwJbkUheyX9eRoBDPezynCmMetYATNsW0U4ERBiagKO-DxRMN4lqHxcVFf7HXVRCWPf3A6aIYo6Vox0fHNFDWyX7g4qcD0wy8mSIgt9FsN5EBFqkgUxfC2o_5OrlUEsbaN8vU3tH4jNoTjWEcT6cNVNv7gltzkTQDQFdgl7DC-Bf12p9HJsSQQlJqdS-BhDYp-ou7dwgd3jeomureLC6kOhaU3ssmSsn69cdCt9cZgZ9U9-5knjyicGUDaCpPHWpz3_R8JgyLq3L8nzEetPBHRShwMPUwV42F_9_C2-gXR7ZVaU3ENshViL0p0T70U4VElOb7IxqAMWRmIlw"}

View File

@ -0,0 +1,12 @@
{
"payload": "eyJ0YXJnZXRBcnRpZmFjdCI6eyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiZGlnZXN0Ijoic2hhMjU2OjYwMDQzY2Y0NWVhZWJjNGMwODY3ZmVhNDg1YTAzOWI1OThmNTJmZDA5ZmQ1YjA3YjBiMmQyZjg4ZmFkOWQ3NGUiLCJzaXplIjo1Mjh9fQ",
"protected": "eyJhbGciOiJQUzM4NCIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LmV4cGlyeSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3RhcnkuZXhwaXJ5IjoiMjAyMi0wNy0yOVQyMzo1OTowMFoiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nU2NoZW1lIjoibm90YXJ5Lng1MDkiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nVGltZSI6IjIwMjItMDctMjlUMDA6MDA6MDBaIn0",
"header": {
"x5c": [
"MIIEWDCCAsCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMTAwOTA3MDAwMFoYDzIxMjIwODA2MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwE8YkFUAA0R7aUkRYxHKYoVbFPx9xhuNovLKDy72/7X0+j4XdGP4C0aAX2KLfgy9OR1RIUwtpMyI7k7ZFRd+ljcMW/FgbirfhkY/8axjamOYMBO0Qg+w93oaI6HA1gvZ/WZem4PHu68LlZhLQ2BrQwCz/F/3Ft0IZ2S1aF6N6vajx2le8xTI5hQS+UZFPQGrBUqrjcYc6GkL8XqL+rLGZaKGfh3c7bF9cEbA1H2Tm6MDFnfoFemerbP3v19JoUH+EtOnvYmNZWEU51RaLsNGkC3E/unXAnIfXrNxHDcbehyfa5y3AT10Shiron6O4Bc9S0MvwtXyLT6qein3Nh0VKBFUMSdthu5ZrSR28T9wDWHMXngpa115VjHOQDY3gDPwfzZ0xitN3NpMnivxculGUCkEQpst957tqQNJpS/zipI5Mtej0YOAhVKGQMjDIJekZ2DXDNd1X3xfahrR5VEQF0gnRFhA3vhycDqFj4E6Hoc5y3SxnFqrhX3w2wyFt/xRAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAYEAAdONCAJxdB7H0uFDw6H+8Z5MtoRdJe6ZhlM2O5WMzkC1DLSyrF7arPnUMTeSyNS2Fx1BU38n5R1wvdgSfWtjm7o2ZyR8JQ+AngPklUCTNeL18kxNNXpmjDuMvsRlfHcr5hherjiQ49jWlpFqGRrNtZQWiVEI0r9Qz8DtZTw3GYF4MSuotA6wuUjolI1V2oMn/gdt8FFo0XUTDyiA12qpZzkUHY1rg3zJxKq3pIk04E7k6rFakHyZL91ipV2UeSbNq9vwLL7cglfPJ8+J+9AKvIPDstDF5k0ivUCYH5fIFZBGoceLiNfHSMcqA/qWfErqLBWAkACRUNyCWpAEv3DfDRbTHId0n6QQwOXj5d9YnDrmOLvQcn/sa+ZBfFMK7RdG9uVwMRyo+sRUnxo+v2lcvYwWymL7ONQqVWZbTJCxuG90Unxa3cQHZiKB5mgKweMft+vp6C3IQFhFfP8j1kvRTJq8ZqSEBADppUuBZJ1KWalwauK0AE4jpHlE0KsYDXiP",
"MIIEizCCAvOgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMDkwOTA3MDAwMFoYDzIxMjIwOTA1MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxxAZ8VZegqBUctz3BkwhObZKnW+KsN5/N1/u2vPLmEzHDj6xgd8Hn0JoughDaxeQCV66NC2obqPnPp4+68G/qZnxkXVXdFyqVodu4FgPUjiqcJjft7bh45BVgLFpOqSqDQ3ko30B7gdGfIIkoBj/8gz3tHnmIvl3MywtOhDeGnlLNzBY52wVmhPIdKOaW/7WkMrXKFCkLkNICGnIpWuyBtC+7RfM8hG6eRW1KCm5xrkRmn5ptonjxix/JTGj4me/NMkwdVkz6wcCSAJnqTgHi2oqk73qqNu0LHsEMFBF8IGqmVkn2MOHkFamPBokzQ6HXXfvR4nbcWQZCUgRinPTVg9CF0B6XSCEMCSH5kveZxTQtAFRB6NosbzuU5jDmJgpbDfauev7Eg/6bZzphcugRkVuwulymzsake5Jbvs9Kyw3CNPYH2G3Kli1FNhfc46ugXHbIfXgNQcou3xabcu+r6cFRqqK6NmV9ouMQRj8Ri95Gp2BUlpTEFhcvMb9d4nXAgMBAAGjWjBYMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBS5FZjt9UsEPkcKrStrnjSpTq4kDTANBgkqhkiG9w0BAQsFAAOCAYEAKtxfv12LzM85bxOMp5++pIDa6eMcBaurYbAM2yC9B6LuHf0JGeFdNqt4Fw38Ajooj2vWMWBrARVEZRVqTC5+ZSN2meGBXBXlT4n8FdEdmv+05iwVYdmDFp8FKeoOZZZF23u+r2OrazJo1ufWmoSI2P0lEfZQQFQElltWu3QH+OLOWXJmB7KbLKyheelGK5XhtAYYapRdW4sKJ398ybpv5C1oALCcTwoSmvH8wW5J4/gjmhKICYh2goMauf0lesdxj+0His7E8blOWrUmfOB5dp73XawLKcd/UxHN8zAPC08LDL9NMcihn3ZHKi7/dtkiV2iSaDPD1ChSGdqfXIysYqOhYoktgAfBZ43CWnqQhgB8NezRKdOStYC3P2AGJW18irxxTRp2CO+gnXEcyhyr+cvyf0j8MkRSaHLXzjIrECu8BUitB6sKughdN13fs5t5SIiO6foeFdvIpZFFKO8s+4oTOSDCos2WFoC+8TZS6r583OtFLmywl1HRgQkobGgw"
],
"io.cncf.notary.SigningAgent": "Notation/1.0.0"
},
"signature": "RZtqCD4KGh5_CD8wjG69TJIzzB4Cr-cxQhKTvZJYsRVIJyl3s5D0215GhBrggomVk9-LGD2FdWd2VfuaLd4bmhW3rSV3ltmAext7DNQFg2xtMeYSeCL2U_ygN2j4bc80RDaX8w_zOTVOmuhW6i2jgwRjWXdDaJeYTbZA2syA5R38tYYewVcZJ6U057Wsflt5yPWJCdxZLuTago5CkbLASL8HHnmlUkDvKKB1Y9SNDOQ3AmGP4-XJykcX_MfPo5RGRvZE-zHUJOEKj3ryfC0UTUT7V1ISTagqOt7zOa1BEzgQ-1GQk1MbaPPZWkiOZX4RqMXMV3hVqtDuZxlpT25KzZPm1USwWwJkycv7YB69fc2aoHJAPo-39uEV9fdAz_03whnrQSpfJbmHHTXMJkWKrZ5ozU-8zlEttWyL5D85zAouSMVXWm22zMrDW-XxST9QoeV4b1_BedW1PwJDbeU6P1hhobnQh3jHmSueVl_WZ5_g8_iVepSmSBcR1e4WpoPi"
}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQxMjo0OTowMi0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjEuMC4xIn0","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"tb2xdd03j1ATBoG1K-9QmCjNeTnK-LKLHdZS44NJ0G5MfrzSFv56w3_FDqnS1jki8FTmGVUMdPAOciTuyoP_nREMBMr9QYn-qOAHisVrvxAcqmWEL-4Uoa_VIzmPvq-_wJKw9L_oZ2m-b9dx93tl2t2z0gxQaAgtVWJP6ap47lKlri6IFeFIXDq6jpdC9sy3q_wifnxFaZ9LM3892Pp7aMLvnT_TdTPxT1AHSq6ZOvddPbStvSUVICXZLmsglFym2c8RzatxulrnGlZ1fKKS0gR7W96-L1JsqIV5KeBMXq8vFnG-rK4fsqa0FeBBkmOOV6ZKKIruvfm7Z-SJ-nJLZw"}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQwMDo0NzoxOC0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjEuMC45In0","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"zdA6RgbS3-QUdrhJuhAz-4wi55PZjy0CezOyTpg0UP1zxRfigVefPne86GEhGmiC-m-QlJC6bWSYFdkF3EoBL1CpGo46zUeaGKhQXM0Db1I8VKhJE20o1T83yXm-_ZVgDEe3_LUhu_KYs-jvkfJu_DGl6DJdBp_lkEpc9Br3tYUvgkxtF2LlvSUNYuc4oILnidj2sYFO5o7IBKdDoBVlQ3Z29s2Z6NUzy48ab9mxZCq0T9-uGj8636GJ3yJ78086GI_lt-0_mXdJ592WguWb3WBogCz9NvLm-byPIC7cP4RpHRqJQRsvYp6txgsrDqy2T1I0BEsf-Fp1FSxBdWMwXA"}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQxMjo0MDozNC0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjEuMS4wLWFscGhhIn0","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"In4X5VH7wiFuGRaop36jSxFMvgAmbnZ7Pwhl1iqhSFItCGpwCCq7Sb9fWn79fiRyxI9F6JuJSTnTtHnjmZXfShAe5KRlSUktPwRcGg6LAMG9YTvd1JayNdjAGyPvZw7PGqeKF_syNgSrw-UzLsR0YXqck639affiVlKRTMNeZla2iXb8gRa8LGGiGoizKMrwV3Ywf3QilWy4CR5NK9TUj-OmdpaBfmE3T--LDpaOt7fjzhCFMXDGq27I_7NfzhrIJ_LpS7f2R5dG6eVRIgmSOVKEkCVM0n38lJ0H1E2uwwYmhns5wzDWJeBVEem8ycFrQkEvsGHWJ1Ru9YYNXhfr9Q"}

View File

@ -0,0 +1 @@
{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6ZmU3ZTkzMzMzOTUwNjBjMmY1ZTYzY2YzNmEzOGZiYTEwMTc2ZjE4M2I0MTYzYTU3OTRlMDgxYTQ4MGFiYmE1ZiIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuZGlzdHJpYnV0aW9uLm1hbmlmZXN0LnYyK2pzb24iLCJzaXplIjo5NDJ9fQ","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbk1pblZlcnNpb24iLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iXSwiY3R5IjoiYXBwbGljYXRpb24vdm5kLmNuY2Yubm90YXJ5LnBheWxvYWQudjEranNvbiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMy0wMS0xOVQxMjo1NDowMS0wODowMCIsImlvLmNuY2Yubm90YXJ5LnZlcmlmaWNhdGlvblBsdWdpbiI6ImlvLmNuY2Yubm90YXJ5LnBsdWdpbi51bml0dGVzdC5tb2NrIiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luTWluVmVyc2lvbiI6IjEuMi4zIn0","header":{"x5c":["MIIDVjCCAj6gAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMB4XDTIzMDExOTA4MTkwN1oXDTMzMDExOTA4MTkwN1owWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZOb3RhcnkxGzAZBgNVBAMTEndhYmJpdC1uZXR3b3Jrcy5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANHhlP+SiY7hsGlf2mADOzJW/J9siqMkiQvSOx0OSM2yxetfVQL/abi4iqCXM6wkSxviBeNwIoYEs4thMA8NGEbnKoXktyh9vmiLB1FW7HHr4QLwjgLzgWJKIQTy1JmDBecXZh56d0f3w3Yj1IDTvkIScXCNI+5v/08GUQKhyBwv7Fq9MYpo2lfXSI7V33BKKddXIxPGVWwKGvPE0sg2VV7WM84ZZLdDKz2mq0PtPTHrSwg3hlK/mjn+blg3gsYQ4h9/7Z6nNaF9X0SdyESl841ZWrtMhAOFpIzLbz9ete8NRd3bYCRBIr5gscHWTf6lyUgy4xzsSwMHPsGLM4A+Z00CAwEAAaMnMCUwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMA0GCSqGSIb3DQEBCwUAA4IBAQAbN0Eru56uTQSC28ZTf8D7VyCkYrrWLYiJMYdOKBzzKV9mKaM0OGF2uyWwDaPxp9KTdLXmBp9EFq5SXXArFA+nRS7KinDAe2O7A/9Std2XjKi927rkA2cj239d5lRsjWXqJXf9vAMV9a2FjUM/in2Eevlq7bvjFE3l26VXCKtOs9ErmfxrL+6ETRKSVYOOG/rSHFv/SB2MlqDg5QsXC9lZjzL5/X/ioe2qZKhp6X5DPpad1q1Q4ItKdTN+2EXyMyoHn1BJKNba7CUUvXf03EJebT/Im+qozfEksJeZJUSlSujANUPoCpsEYGWWQx5G+ViG05Sqs+6ppKrut+P+DVPo"],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"sB7vQl3zpK1JBjKa0gwj_s0Rbboo2kb4x81MNIbUINAc2ocvfRqyxMtlJYdgmx78GledCm4j8BfXr7_sV0_WkKI6Af6n_5rYMQ0a3EOI79-uzkRqrKBJsh4BsuQuweBql-W5-ofnwAhpNUmowHUcJlh0PmpOeYPQcj0TFMCZuqwMSKi4KLj4H5ENnmIWyR4rDoNueZkenbfh-eYR47PDb8KHyGTX86m-8IY-gNlAYRm_62MRecsGjg97EQ5niFGaRxlg7jf-1RVg3jLKXYlLRs41dnsMo1QGuTW7nPEUZKpXMZIyPnqPmS91icq0v1Obcx-r4aOqTKrPTeIPCYMoSQ"}

View File

@ -0,0 +1,12 @@
{
"payload": "eyJ0YXJnZXRBcnRpZmFjdCI6eyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiZGlnZXN0Ijoic2hhMjU2OjYwMDQzY2Y0NWVhZWJjNGMwODY3ZmVhNDg1YTAzOWI1OThmNTJmZDA5ZmQ1YjA3YjBiMmQyZjg4ZmFkOWQ3NGUiLCJzaXplIjo1Mjh9fQ=",
"protected": "eyJhbGciOiJQUzM4NCIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LmV4cGlyeSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3RhcnkuZXhwaXJ5IjoiMjEyMC0xMS0wOVQwNzowMDowMFoiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nU2NoZW1lIjoibm90YXJ5Lng1MDkiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nVGltZSI6IjIwMjAtMTEtMDlUMDc6MDA6MDBaIn0",
"header": {
"x5c": [
"MIIEWDCCAsCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMTAwOTA3MDAwMFoYDzIxMjIwODA2MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwE8YkFUAA0R7aUkRYxHKYoVbFPx9xhuNovLKDy72/7X0+j4XdGP4C0aAX2KLfgy9OR1RIUwtpMyI7k7ZFRd+ljcMW/FgbirfhkY/8axjamOYMBO0Qg+w93oaI6HA1gvZ/WZem4PHu68LlZhLQ2BrQwCz/F/3Ft0IZ2S1aF6N6vajx2le8xTI5hQS+UZFPQGrBUqrjcYc6GkL8XqL+rLGZaKGfh3c7bF9cEbA1H2Tm6MDFnfoFemerbP3v19JoUH+EtOnvYmNZWEU51RaLsNGkC3E/unXAnIfXrNxHDcbehyfa5y3AT10Shiron6O4Bc9S0MvwtXyLT6qein3Nh0VKBFUMSdthu5ZrSR28T9wDWHMXngpa115VjHOQDY3gDPwfzZ0xitN3NpMnivxculGUCkEQpst957tqQNJpS/zipI5Mtej0YOAhVKGQMjDIJekZ2DXDNd1X3xfahrR5VEQF0gnRFhA3vhycDqFj4E6Hoc5y3SxnFqrhX3w2wyFt/xRAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAYEAAdONCAJxdB7H0uFDw6H+8Z5MtoRdJe6ZhlM2O5WMzkC1DLSyrF7arPnUMTeSyNS2Fx1BU38n5R1wvdgSfWtjm7o2ZyR8JQ+AngPklUCTNeL18kxNNXpmjDuMvsRlfHcr5hherjiQ49jWlpFqGRrNtZQWiVEI0r9Qz8DtZTw3GYF4MSuotA6wuUjolI1V2oMn/gdt8FFo0XUTDyiA12qpZzkUHY1rg3zJxKq3pIk04E7k6rFakHyZL91ipV2UeSbNq9vwLL7cglfPJ8+J+9AKvIPDstDF5k0ivUCYH5fIFZBGoceLiNfHSMcqA/qWfErqLBWAkACRUNyCWpAEv3DfDRbTHId0n6QQwOXj5d9YnDrmOLvQcn/sa+ZBfFMK7RdG9uVwMRyo+sRUnxo+v2lcvYwWymL7ONQqVWZbTJCxuG90Unxa3cQHZiKB5mgKweMft+vp6C3IQFhFfP8j1kvRTJq8ZqSEBADppUuBZJ1KWalwauK0AE4jpHlE0KsYDXiP",
"MIIEizCCAvOgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMDkwOTA3MDAwMFoYDzIxMjIwOTA1MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxxAZ8VZegqBUctz3BkwhObZKnW+KsN5/N1/u2vPLmEzHDj6xgd8Hn0JoughDaxeQCV66NC2obqPnPp4+68G/qZnxkXVXdFyqVodu4FgPUjiqcJjft7bh45BVgLFpOqSqDQ3ko30B7gdGfIIkoBj/8gz3tHnmIvl3MywtOhDeGnlLNzBY52wVmhPIdKOaW/7WkMrXKFCkLkNICGnIpWuyBtC+7RfM8hG6eRW1KCm5xrkRmn5ptonjxix/JTGj4me/NMkwdVkz6wcCSAJnqTgHi2oqk73qqNu0LHsEMFBF8IGqmVkn2MOHkFamPBokzQ6HXXfvR4nbcWQZCUgRinPTVg9CF0B6XSCEMCSH5kveZxTQtAFRB6NosbzuU5jDmJgpbDfauev7Eg/6bZzphcugRkVuwulymzsake5Jbvs9Kyw3CNPYH2G3Kli1FNhfc46ugXHbIfXgNQcou3xabcu+r6cFRqqK6NmV9ouMQRj8Ri95Gp2BUlpTEFhcvMb9d4nXAgMBAAGjWjBYMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBS5FZjt9UsEPkcKrStrnjSpTq4kDTANBgkqhkiG9w0BAQsFAAOCAYEAKtxfv12LzM85bxOMp5++pIDa6eMcBaurYbAM2yC9B6LuHf0JGeFdNqt4Fw38Ajooj2vWMWBrARVEZRVqTC5+ZSN2meGBXBXlT4n8FdEdmv+05iwVYdmDFp8FKeoOZZZF23u+r2OrazJo1ufWmoSI2P0lEfZQQFQElltWu3QH+OLOWXJmB7KbLKyheelGK5XhtAYYapRdW4sKJ398ybpv5C1oALCcTwoSmvH8wW5J4/gjmhKICYh2goMauf0lesdxj+0His7E8blOWrUmfOB5dp73XawLKcd/UxHN8zAPC08LDL9NMcihn3ZHKi7/dtkiV2iSaDPD1ChSGdqfXIysYqOhYoktgAfBZ43CWnqQhgB8NezRKdOStYC3P2AGJW18irxxTRp2CO+gnXEcyhyr+cvyf0j8MkRSaHLXzjIrECu8BUitB6sKughdN13fs5t5SIiO6foeFdvIpZFFKO8s+4oTOSDCos2WFoC+8TZS6r583OtFLmywl1HRgQkobGgw"
],
"io.cncf.notary.SigningAgent": "Notation/1.0.0"
},
"signature": "ZvsxyaSqDzS7mY_jKpnq2XtBcmyWmSE461BHL6q2pAx_-Rxr8Fvs2oIfZdSG2o3qugPDjzZDMhKdYdnrW1AIEkVIG_QUmeyGj28PVXxsC5NKpXwrPUMOzrXSFLHIvBNZ2q87wRYInsgCPtv5ZPv0IgA2sAW6y7NlVM2D0vJax55ITsJO5aEaEUlAdi_H7-TCD48DHuFpnJdNkVB_hZkwYfxuqIKU2C__Z2hLLHxaS2LzuzhqOnYlbqn4e225uZt9odXq3qmZ_44Vx3DYL_-ZuV0S9jEk7NW8-dO0T0MeQn6VXDyfT1rjc6IVPnLxAnELFyLn121GYulYC8V2D1_MLcv8sDHY23rHb3-R-WCLMDSfaIvReY89vQfxcfpdCRC0F3N2CcnrgsrUC6Fplm5Uy45Gn9--b7x5cdSzOzQsefCH1GpixW7YyNs1xZQ17WqdYyWD2EBrB5vqVFzkzDYnQ4H-p9G3AzM4HTrjWqHX-0cYHlpmTS4AjVxn0UV80Jn9"
}

View File

@ -0,0 +1,12 @@
{
"payload": "eyJ0YXJnZXRBcnRpZmFjdCI6eyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiZGlnZXN0Ijoic2hhMjU2OjYwMDQzY2Y0NWVhZWJjNGMwODY3ZmVhNDg1YTAzOWI1OThmNTJmZDA5ZmQ1YjA3YjBiMmQyZjg4ZmFkOWQ3NGUiLCJzaXplIjo1Mjh9fQ",
"protected": "eyJTb21lS2V5IjoiU29tZVZhbHVlIiwiYWxnIjoiUFMzODQiLCJjcml0IjpbImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiLCJTb21lS2V5IiwiaW8uY25jZi5ub3RhcnkuZXhwaXJ5IiwiaW8uY25jZi5ub3RhcnkudmVyaWZpY2F0aW9uUGx1Z2luIl0sImN0eSI6ImFwcGxpY2F0aW9uL3ZuZC5jbmNmLm5vdGFyeS5wYXlsb2FkLnYxK2pzb24iLCJpby5jbmNmLm5vdGFyeS5leHBpcnkiOiIyMTIwLTExLTA5VDA3OjAwOjAwWiIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdTY2hlbWUiOiJub3RhcnkueDUwOSIsImlvLmNuY2Yubm90YXJ5LnNpZ25pbmdUaW1lIjoiMjAyMC0xMS0wOVQwNzowMDowMFoiLCJpby5jbmNmLm5vdGFyeS52ZXJpZmljYXRpb25QbHVnaW4iOiJwbHVnaW4tbmFtZSJ9",
"header": {
"x5c": [
"MIIEWDCCAsCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMTAwOTA3MDAwMFoYDzIxMjIwODA2MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwE8YkFUAA0R7aUkRYxHKYoVbFPx9xhuNovLKDy72/7X0+j4XdGP4C0aAX2KLfgy9OR1RIUwtpMyI7k7ZFRd+ljcMW/FgbirfhkY/8axjamOYMBO0Qg+w93oaI6HA1gvZ/WZem4PHu68LlZhLQ2BrQwCz/F/3Ft0IZ2S1aF6N6vajx2le8xTI5hQS+UZFPQGrBUqrjcYc6GkL8XqL+rLGZaKGfh3c7bF9cEbA1H2Tm6MDFnfoFemerbP3v19JoUH+EtOnvYmNZWEU51RaLsNGkC3E/unXAnIfXrNxHDcbehyfa5y3AT10Shiron6O4Bc9S0MvwtXyLT6qein3Nh0VKBFUMSdthu5ZrSR28T9wDWHMXngpa115VjHOQDY3gDPwfzZ0xitN3NpMnivxculGUCkEQpst957tqQNJpS/zipI5Mtej0YOAhVKGQMjDIJekZ2DXDNd1X3xfahrR5VEQF0gnRFhA3vhycDqFj4E6Hoc5y3SxnFqrhX3w2wyFt/xRAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAYEAAdONCAJxdB7H0uFDw6H+8Z5MtoRdJe6ZhlM2O5WMzkC1DLSyrF7arPnUMTeSyNS2Fx1BU38n5R1wvdgSfWtjm7o2ZyR8JQ+AngPklUCTNeL18kxNNXpmjDuMvsRlfHcr5hherjiQ49jWlpFqGRrNtZQWiVEI0r9Qz8DtZTw3GYF4MSuotA6wuUjolI1V2oMn/gdt8FFo0XUTDyiA12qpZzkUHY1rg3zJxKq3pIk04E7k6rFakHyZL91ipV2UeSbNq9vwLL7cglfPJ8+J+9AKvIPDstDF5k0ivUCYH5fIFZBGoceLiNfHSMcqA/qWfErqLBWAkACRUNyCWpAEv3DfDRbTHId0n6QQwOXj5d9YnDrmOLvQcn/sa+ZBfFMK7RdG9uVwMRyo+sRUnxo+v2lcvYwWymL7ONQqVWZbTJCxuG90Unxa3cQHZiKB5mgKweMft+vp6C3IQFhFfP8j1kvRTJq8ZqSEBADppUuBZJ1KWalwauK0AE4jpHlE0KsYDXiP",
"MIIEizCCAvOgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMDkwOTA3MDAwMFoYDzIxMjIwOTA1MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxxAZ8VZegqBUctz3BkwhObZKnW+KsN5/N1/u2vPLmEzHDj6xgd8Hn0JoughDaxeQCV66NC2obqPnPp4+68G/qZnxkXVXdFyqVodu4FgPUjiqcJjft7bh45BVgLFpOqSqDQ3ko30B7gdGfIIkoBj/8gz3tHnmIvl3MywtOhDeGnlLNzBY52wVmhPIdKOaW/7WkMrXKFCkLkNICGnIpWuyBtC+7RfM8hG6eRW1KCm5xrkRmn5ptonjxix/JTGj4me/NMkwdVkz6wcCSAJnqTgHi2oqk73qqNu0LHsEMFBF8IGqmVkn2MOHkFamPBokzQ6HXXfvR4nbcWQZCUgRinPTVg9CF0B6XSCEMCSH5kveZxTQtAFRB6NosbzuU5jDmJgpbDfauev7Eg/6bZzphcugRkVuwulymzsake5Jbvs9Kyw3CNPYH2G3Kli1FNhfc46ugXHbIfXgNQcou3xabcu+r6cFRqqK6NmV9ouMQRj8Ri95Gp2BUlpTEFhcvMb9d4nXAgMBAAGjWjBYMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBS5FZjt9UsEPkcKrStrnjSpTq4kDTANBgkqhkiG9w0BAQsFAAOCAYEAKtxfv12LzM85bxOMp5++pIDa6eMcBaurYbAM2yC9B6LuHf0JGeFdNqt4Fw38Ajooj2vWMWBrARVEZRVqTC5+ZSN2meGBXBXlT4n8FdEdmv+05iwVYdmDFp8FKeoOZZZF23u+r2OrazJo1ufWmoSI2P0lEfZQQFQElltWu3QH+OLOWXJmB7KbLKyheelGK5XhtAYYapRdW4sKJ398ybpv5C1oALCcTwoSmvH8wW5J4/gjmhKICYh2goMauf0lesdxj+0His7E8blOWrUmfOB5dp73XawLKcd/UxHN8zAPC08LDL9NMcihn3ZHKi7/dtkiV2iSaDPD1ChSGdqfXIysYqOhYoktgAfBZ43CWnqQhgB8NezRKdOStYC3P2AGJW18irxxTRp2CO+gnXEcyhyr+cvyf0j8MkRSaHLXzjIrECu8BUitB6sKughdN13fs5t5SIiO6foeFdvIpZFFKO8s+4oTOSDCos2WFoC+8TZS6r583OtFLmywl1HRgQkobGgw"
],
"io.cncf.notary.SigningAgent": "Notation/1.0.0"
},
"signature": "cyB34qtMss9N1E_2XAQ_71c6j1fOcamenm7YrYsXn562XOhFgJKUjmDYWkz9mmdLN-GqQNKA8MhAfKt2ipXxsWldrb3a-6AZ-y4jIkY5XIY_s7Sndz58DPtez0X4kAehvKiyUtDVPbqIJQ5Hwgj8tC_f0Yva6pdrSD7xwenxwiCZmxM6N_LV9d1oYSDQi9890XRrFK4M1YRlOZquJ19HrhADLVJXS-ZfqcTE_tceoU2Hq82pqd2MnazAtJiWZm0cxwt-OsGlgGrkvHoNcMYS8K6BSBvL-vVtOuSpca89QrLsTCnKnmvUlw3wrWTDf83qhPyfw-2ASrE2V57vunpxSNyoA_70fNgOuhWUZZUTi9eXxutp0GCcGTem7MzZRBJVOVdw9OgR3pClGiRxP3BE2Atn3EUXs2HgQHEiE1KZvVHFeObB6asMqfbAMMNDgZCsZi7Yah7NaYg1NH9YwrJgAtNFW0p2trxiQ6uqICD2m54yGtRmvw_O9kt5HnUaBQJX"
}

View File

@ -0,0 +1,12 @@
{
"payload": "eyJ0YXJnZXRBcnRpZmFjdCI6eyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiZGlnZXN0Ijoic2hhMjU2OjYwMDQzY2Y0NWVhZWJjNGMwODY3ZmVhNDg1YTAzOWI1OThmNTJmZDA5ZmQ1YjA3YjBiMmQyZjg4ZmFkOWQ3NGUiLCJzaXplIjo1Mjh9fQ",
"protected": "eyJhbGciOiJQUzM4NCIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSIsImlvLmNuY2Yubm90YXJ5LmV4cGlyeSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3RhcnkuZXhwaXJ5IjoiMjEyMC0xMS0wOVQwNzowMDowMFoiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nU2NoZW1lIjoibm90YXJ5Lng1MDkiLCJpby5jbmNmLm5vdGFyeS5zaWduaW5nVGltZSI6IjIwMjAtMTEtMDlUMDc6MDA6MDBaIn0",
"header": {
"x5c": [
"MIIEWDCCAsCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMTAwOTA3MDAwMFoYDzIxMjIwODA2MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAwE8YkFUAA0R7aUkRYxHKYoVbFPx9xhuNovLKDy72/7X0+j4XdGP4C0aAX2KLfgy9OR1RIUwtpMyI7k7ZFRd+ljcMW/FgbirfhkY/8axjamOYMBO0Qg+w93oaI6HA1gvZ/WZem4PHu68LlZhLQ2BrQwCz/F/3Ft0IZ2S1aF6N6vajx2le8xTI5hQS+UZFPQGrBUqrjcYc6GkL8XqL+rLGZaKGfh3c7bF9cEbA1H2Tm6MDFnfoFemerbP3v19JoUH+EtOnvYmNZWEU51RaLsNGkC3E/unXAnIfXrNxHDcbehyfa5y3AT10Shiron6O4Bc9S0MvwtXyLT6qein3Nh0VKBFUMSdthu5ZrSR28T9wDWHMXngpa115VjHOQDY3gDPwfzZ0xitN3NpMnivxculGUCkEQpst957tqQNJpS/zipI5Mtej0YOAhVKGQMjDIJekZ2DXDNd1X3xfahrR5VEQF0gnRFhA3vhycDqFj4E6Hoc5y3SxnFqrhX3w2wyFt/xRAgMBAAGjJzAlMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAYEAAdONCAJxdB7H0uFDw6H+8Z5MtoRdJe6ZhlM2O5WMzkC1DLSyrF7arPnUMTeSyNS2Fx1BU38n5R1wvdgSfWtjm7o2ZyR8JQ+AngPklUCTNeL18kxNNXpmjDuMvsRlfHcr5hherjiQ49jWlpFqGRrNtZQWiVEI0r9Qz8DtZTw3GYF4MSuotA6wuUjolI1V2oMn/gdt8FFo0XUTDyiA12qpZzkUHY1rg3zJxKq3pIk04E7k6rFakHyZL91ipV2UeSbNq9vwLL7cglfPJ8+J+9AKvIPDstDF5k0ivUCYH5fIFZBGoceLiNfHSMcqA/qWfErqLBWAkACRUNyCWpAEv3DfDRbTHId0n6QQwOXj5d9YnDrmOLvQcn/sa+ZBfFMK7RdG9uVwMRyo+sRUnxo+v2lcvYwWymL7ONQqVWZbTJCxuG90Unxa3cQHZiKB5mgKweMft+vp6C3IQFhFfP8j1kvRTJq8ZqSEBADppUuBZJ1KWalwauK0AE4jpHlE0KsYDXiP",
"MIIEizCCAvOgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MCAXDTIwMDkwOTA3MDAwMFoYDzIxMjIwOTA1MjAzODQ1WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSTm90YXRpb24gVGVzdCBSb290MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxxAZ8VZegqBUctz3BkwhObZKnW+KsN5/N1/u2vPLmEzHDj6xgd8Hn0JoughDaxeQCV66NC2obqPnPp4+68G/qZnxkXVXdFyqVodu4FgPUjiqcJjft7bh45BVgLFpOqSqDQ3ko30B7gdGfIIkoBj/8gz3tHnmIvl3MywtOhDeGnlLNzBY52wVmhPIdKOaW/7WkMrXKFCkLkNICGnIpWuyBtC+7RfM8hG6eRW1KCm5xrkRmn5ptonjxix/JTGj4me/NMkwdVkz6wcCSAJnqTgHi2oqk73qqNu0LHsEMFBF8IGqmVkn2MOHkFamPBokzQ6HXXfvR4nbcWQZCUgRinPTVg9CF0B6XSCEMCSH5kveZxTQtAFRB6NosbzuU5jDmJgpbDfauev7Eg/6bZzphcugRkVuwulymzsake5Jbvs9Kyw3CNPYH2G3Kli1FNhfc46ugXHbIfXgNQcou3xabcu+r6cFRqqK6NmV9ouMQRj8Ri95Gp2BUlpTEFhcvMb9d4nXAgMBAAGjWjBYMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAKBggrBgEFBQcDAzASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBS5FZjt9UsEPkcKrStrnjSpTq4kDTANBgkqhkiG9w0BAQsFAAOCAYEAKtxfv12LzM85bxOMp5++pIDa6eMcBaurYbAM2yC9B6LuHf0JGeFdNqt4Fw38Ajooj2vWMWBrARVEZRVqTC5+ZSN2meGBXBXlT4n8FdEdmv+05iwVYdmDFp8FKeoOZZZF23u+r2OrazJo1ufWmoSI2P0lEfZQQFQElltWu3QH+OLOWXJmB7KbLKyheelGK5XhtAYYapRdW4sKJ398ybpv5C1oALCcTwoSmvH8wW5J4/gjmhKICYh2goMauf0lesdxj+0His7E8blOWrUmfOB5dp73XawLKcd/UxHN8zAPC08LDL9NMcihn3ZHKi7/dtkiV2iSaDPD1ChSGdqfXIysYqOhYoktgAfBZ43CWnqQhgB8NezRKdOStYC3P2AGJW18irxxTRp2CO+gnXEcyhyr+cvyf0j8MkRSaHLXzjIrECu8BUitB6sKughdN13fs5t5SIiO6foeFdvIpZFFKO8s+4oTOSDCos2WFoC+8TZS6r583OtFLmywl1HRgQkobGgw"
],
"io.cncf.notary.SigningAgent": "Notation/1.0.0"
},
"signature": "ZvsxyaSqDzS7mY_jKpnq2XtBcmyWmSE461BHL6q2pAx_-Rxr8Fvs2oIfZdSG2o3qugPDjzZDMhKdYdnrW1AIEkVIG_QUmeyGj28PVXxsC5NKpXwrPUMOzrXSFLHIvBNZ2q87wRYInsgCPtv5ZPv0IgA2sAW6y7NlVM2D0vJax55ITsJO5aEaEUlAdi_H7-TCD48DHuFpnJdNkVB_hZkwYfxuqIKU2C__Z2hLLHxaS2LzuzhqOnYlbqn4e225uZt9odXq3qmZ_44Vx3DYL_-ZuV0S9jEk7NW8-dO0T0MeQn6VXDyfT1rjc6IVPnLxAnELFyLn121GYulYC8V2D1_MLcv8sDHY23rHb3-R-WCLMDSfaIvReY89vQfxcfpdCRC0F3N2CcnrgsrUC6Fplm5Uy45Gn9--b7x5cdSzOzQsefCH1GpixW7YyNs1xZQ17WqdYyWD2EBrB5vqVFzkzDYnQ4H-p9G3AzM4HTrjWqHX-0cYHlpmTS4AjVxn0UV80Jn9"
}

Some files were not shown because too many files have changed in this diff Show More