Skip to content

Commit 3351c95

Browse files
authored
backport: timestamping cert chain revocation check during signing from main to release-1.3 branch (#1121)
Signed-off-by: Patrick Zheng <[email protected]>
1 parent fae91b7 commit 3351c95

File tree

7 files changed

+141
-39
lines changed

7 files changed

+141
-39
lines changed

cmd/notation/sign.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ import (
2323
"strings"
2424
"time"
2525

26+
"github.com/notaryproject/notation-core-go/revocation/purpose"
2627
corex509 "github.com/notaryproject/notation-core-go/x509"
2728
"github.com/notaryproject/notation-go"
2829
"github.com/notaryproject/notation-go/log"
2930
"github.com/notaryproject/notation/cmd/notation/internal/experimental"
3031
"github.com/notaryproject/notation/internal/cmd"
3132
"github.com/notaryproject/notation/internal/envelope"
3233
"github.com/notaryproject/notation/internal/httputil"
34+
clirev "github.com/notaryproject/notation/internal/revocation"
3335
nx509 "github.com/notaryproject/notation/internal/x509"
3436
"github.com/notaryproject/tspclient-go"
3537
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -251,6 +253,11 @@ func prepareSigningOpts(ctx context.Context, opts *signOpts) (notation.SignOptio
251253
rootCAs := x509.NewCertPool()
252254
rootCAs.AddCert(tsaRootCert)
253255
signOpts.TSARootCAs = rootCAs
256+
tsaRevocationValidator, err := clirev.NewRevocationValidator(ctx, purpose.Timestamping)
257+
if err != nil {
258+
return notation.SignOptions{}, fmt.Errorf("failed to create timestamping revocation validator: %w", err)
259+
}
260+
signOpts.TSARevocationValidator = tsaRevocationValidator
254261
}
255262
return signOpts, nil
256263
}

cmd/notation/verify.go

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,22 @@ import (
1818
"errors"
1919
"fmt"
2020
"io/fs"
21-
"net/http"
2221
"os"
2322
"reflect"
24-
"time"
2523

26-
"github.com/notaryproject/notation-core-go/revocation"
2724
"github.com/notaryproject/notation-core-go/revocation/purpose"
2825
"github.com/notaryproject/notation-go"
2926
"github.com/notaryproject/notation-go/dir"
3027
"github.com/notaryproject/notation-go/plugin"
3128
"github.com/notaryproject/notation-go/verifier"
32-
"github.com/notaryproject/notation-go/verifier/crl"
3329
"github.com/notaryproject/notation-go/verifier/trustpolicy"
3430
"github.com/notaryproject/notation-go/verifier/truststore"
3531
"github.com/notaryproject/notation/cmd/notation/internal/experimental"
3632
"github.com/notaryproject/notation/internal/cmd"
37-
"github.com/notaryproject/notation/internal/httputil"
3833
"github.com/notaryproject/notation/internal/ioutil"
3934
"github.com/spf13/cobra"
4035

41-
corecrl "github.com/notaryproject/notation-core-go/revocation/crl"
42-
clicrl "github.com/notaryproject/notation/internal/crl"
36+
clirev "github.com/notaryproject/notation/internal/revocation"
4337
)
4438

4539
type verifyOpts struct {
@@ -234,39 +228,11 @@ func printMetadataIfPresent(outcome *notation.VerificationOutcome) {
234228

235229
func getVerifier(ctx context.Context) (notation.Verifier, error) {
236230
// revocation check
237-
ocspHttpClient := httputil.NewClient(ctx, &http.Client{Timeout: 2 * time.Second})
238-
crlFetcher, err := corecrl.NewHTTPFetcher(httputil.NewClient(ctx, &http.Client{Timeout: 5 * time.Second}))
231+
revocationCodeSigningValidator, err := clirev.NewRevocationValidator(ctx, purpose.CodeSigning)
239232
if err != nil {
240233
return nil, err
241234
}
242-
crlFetcher.DiscardCacheError = true // discard crl cache error
243-
cacheRoot, err := dir.CacheFS().SysPath(dir.PathCRLCache)
244-
if err != nil {
245-
return nil, err
246-
}
247-
fileCache, err := crl.NewFileCache(cacheRoot)
248-
if err != nil {
249-
// discard NewFileCache error as cache errors are not critical
250-
fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
251-
} else {
252-
crlFetcher.Cache = &clicrl.CacheWithLog{
253-
Cache: fileCache,
254-
DiscardCacheError: crlFetcher.DiscardCacheError,
255-
}
256-
}
257-
revocationCodeSigningValidator, err := revocation.NewWithOptions(revocation.Options{
258-
OCSPHTTPClient: ocspHttpClient,
259-
CRLFetcher: crlFetcher,
260-
CertChainPurpose: purpose.CodeSigning,
261-
})
262-
if err != nil {
263-
return nil, err
264-
}
265-
revocationTimestampingValidator, err := revocation.NewWithOptions(revocation.Options{
266-
OCSPHTTPClient: ocspHttpClient,
267-
CRLFetcher: crlFetcher,
268-
CertChainPurpose: purpose.Timestamping,
269-
})
235+
revocationTimestampingValidator, err := clirev.NewRevocationValidator(ctx, purpose.Timestamping)
270236
if err != nil {
271237
return nil, err
272238
}
File renamed without changes.
File renamed without changes.

internal/revocation/revocation.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright The Notary Project Authors.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package revocation
15+
16+
import (
17+
"context"
18+
"fmt"
19+
"net/http"
20+
"os"
21+
"time"
22+
23+
"github.com/notaryproject/notation-core-go/revocation"
24+
corecrl "github.com/notaryproject/notation-core-go/revocation/crl"
25+
"github.com/notaryproject/notation-core-go/revocation/purpose"
26+
"github.com/notaryproject/notation-go/dir"
27+
"github.com/notaryproject/notation-go/verifier/crl"
28+
"github.com/notaryproject/notation/internal/httputil"
29+
clicrl "github.com/notaryproject/notation/internal/revocation/crl"
30+
)
31+
32+
// NewRevocationValidator returns a revocation.Validator given the certificate
33+
// purpose
34+
func NewRevocationValidator(ctx context.Context, purpose purpose.Purpose) (revocation.Validator, error) {
35+
// err is always nil
36+
crlFetcher, _ := corecrl.NewHTTPFetcher(httputil.NewClient(ctx, &http.Client{Timeout: 5 * time.Second}))
37+
crlFetcher.DiscardCacheError = true // discard crl cache error
38+
cacheRoot, _ := dir.CacheFS().SysPath(dir.PathCRLCache) // err is always nil
39+
fileCache, err := crl.NewFileCache(cacheRoot)
40+
if err != nil {
41+
// discard NewFileCache error as cache errors are not critical
42+
fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
43+
} else {
44+
crlFetcher.Cache = &clicrl.CacheWithLog{
45+
Cache: fileCache,
46+
DiscardCacheError: crlFetcher.DiscardCacheError,
47+
}
48+
}
49+
return revocation.NewWithOptions(revocation.Options{
50+
OCSPHTTPClient: httputil.NewClient(ctx, &http.Client{Timeout: 2 * time.Second}),
51+
CRLFetcher: crlFetcher,
52+
CertChainPurpose: purpose,
53+
})
54+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright The Notary Project Authors.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package revocation
15+
16+
import (
17+
"context"
18+
"net/http"
19+
"os"
20+
"runtime"
21+
"testing"
22+
"time"
23+
24+
corecrl "github.com/notaryproject/notation-core-go/revocation/crl"
25+
"github.com/notaryproject/notation-core-go/revocation/purpose"
26+
"github.com/notaryproject/notation-go/dir"
27+
"github.com/notaryproject/notation/internal/httputil"
28+
)
29+
30+
func TestNewRevocationValidator(t *testing.T) {
31+
defer func(oldCacheDir string) {
32+
dir.UserCacheDir = oldCacheDir
33+
}(dir.UserCacheDir)
34+
35+
t.Run("Success", func(t *testing.T) {
36+
if runtime.GOOS == "windows" {
37+
t.Skip("skipping test on Windows")
38+
}
39+
if _, err := NewRevocationValidator(context.Background(), purpose.Timestamping); err != nil {
40+
t.Fatal(err)
41+
}
42+
})
43+
44+
tempRoot := t.TempDir()
45+
t.Run("Success but without permission to create cache directory", func(t *testing.T) {
46+
if runtime.GOOS == "windows" {
47+
t.Skip("skipping test on Windows")
48+
}
49+
dir.UserCacheDir = tempRoot
50+
if err := os.Chmod(tempRoot, 0); err != nil {
51+
t.Fatal(err)
52+
}
53+
defer func() {
54+
// restore permission
55+
if err := os.Chmod(tempRoot, 0755); err != nil {
56+
t.Fatalf("failed to change permission: %v", err)
57+
}
58+
}()
59+
if _, err := NewRevocationValidator(context.Background(), purpose.Timestamping); err != nil {
60+
t.Fatal(err)
61+
}
62+
})
63+
}
64+
65+
func TestNilError(t *testing.T) {
66+
_, err := corecrl.NewHTTPFetcher(httputil.NewClient(context.Background(), &http.Client{Timeout: 5 * time.Second}))
67+
if err != nil {
68+
t.Fatal(err)
69+
}
70+
71+
_, err = dir.CacheFS().SysPath(dir.PathCRLCache)
72+
if err != nil {
73+
t.Fatal(err)
74+
}
75+
}

test/e2e/suite/command/verify.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ var _ = Describe("notation verify", func() {
229229
notation.Exec("sign", "--timestamp-url", "http://timestamp.digicert.com", "--timestamp-root-cert", filepath.Join(NotationE2EConfigPath, "timestamp", "DigiCertTSARootSHA384.cer"), artifact.ReferenceWithDigest()).
230230
MatchKeyWords(SignSuccessfully)
231231

232-
notation.Exec("verify", artifact.ReferenceWithDigest(), "-v").
232+
notation.Exec("verify", artifact.ReferenceWithDigest(), "-d").
233233
MatchKeyWords(VerifySuccessfully).
234234
MatchErrKeyWords("Timestamp verification disabled")
235235
})
@@ -251,7 +251,7 @@ var _ = Describe("notation verify", func() {
251251
notation.Exec("sign", "--timestamp-url", "http://timestamp.digicert.com", "--timestamp-root-cert", filepath.Join(NotationE2EConfigPath, "timestamp", "DigiCertTSARootSHA384.cer"), artifact.ReferenceWithDigest()).
252252
MatchKeyWords(SignSuccessfully)
253253

254-
notation.Exec("verify", artifact.ReferenceWithDigest(), "-v").
254+
notation.Exec("verify", artifact.ReferenceWithDigest(), "-d").
255255
MatchKeyWords(VerifySuccessfully).
256256
MatchErrKeyWords("Timestamp verification disabled: verifyTimestamp is set to \"afterCertExpiry\" and signing cert chain unexpired")
257257
})

0 commit comments

Comments
 (0)