-
Notifications
You must be signed in to change notification settings - Fork 241
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add issuance chain service * Remove IsStorageBackendEnabled methods * Remove IssuanceChainStorageBackend and its deps * Add ChainHash function comment * Add issuance chain service tests * Remove context from NewIssuanceChainService * Unexport `ChainHash` in IssuanceChainService * Set issuance chain service to be unexported * Set `newIssuanceChainService` to unexported function * Convert `issuanceChainService.chainHash` to static `issuanceChainHash` method * Add go routine for issuance chain cache set * Fix race condition for concurrent read/write on fake storage and cache
- Loading branch information
Showing
2 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// 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 ctfe | ||
|
||
import ( | ||
"context" | ||
"crypto/sha256" | ||
|
||
"github.com/google/certificate-transparency-go/trillian/ctfe/cache" | ||
"github.com/google/certificate-transparency-go/trillian/ctfe/storage" | ||
"k8s.io/klog/v2" | ||
) | ||
|
||
type issuanceChainService struct { | ||
storage storage.IssuanceChainStorage | ||
cache cache.IssuanceChainCache | ||
} | ||
|
||
func newIssuanceChainService(s storage.IssuanceChainStorage, c cache.IssuanceChainCache) *issuanceChainService { | ||
service := &issuanceChainService{ | ||
storage: s, | ||
cache: c, | ||
} | ||
|
||
return service | ||
} | ||
|
||
// GetByHash returns the issuance chain with hash as the input. | ||
func (s *issuanceChainService) GetByHash(ctx context.Context, hash []byte) ([]byte, error) { | ||
// Return if found in cache. | ||
chain, err := s.cache.Get(ctx, hash) | ||
if chain != nil || err != nil { | ||
return chain, err | ||
} | ||
|
||
// Find in storage if cache miss. | ||
chain, err = s.storage.FindByKey(ctx, hash) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// If there is any error from cache set, do not return the error because | ||
// the chain is still available for read. | ||
go func(ctx context.Context, hash, chain []byte) { | ||
if err := s.cache.Set(ctx, hash, chain); err != nil { | ||
klog.Errorf("failed to set hash and chain into cache: %v", err) | ||
} | ||
}(ctx, hash, chain) | ||
|
||
return chain, nil | ||
} | ||
|
||
// Add adds the issuance chain into the storage and cache and returns the hash | ||
// of the chain. | ||
func (s *issuanceChainService) Add(ctx context.Context, chain []byte) ([]byte, error) { | ||
hash := issuanceChainHash(chain) | ||
|
||
if err := s.storage.Add(ctx, hash, chain); err != nil { | ||
return nil, err | ||
} | ||
|
||
// If there is any error from cache set, do not return the error because | ||
// the chain is already stored. | ||
go func(ctx context.Context, hash, chain []byte) { | ||
if err := s.cache.Set(ctx, hash, chain); err != nil { | ||
klog.Errorf("failed to set hash and chain into cache: %v", err) | ||
} | ||
}(ctx, hash, chain) | ||
|
||
return hash, nil | ||
} | ||
|
||
// issuanceChainHash returns the SHA-256 hash of the chain. | ||
func issuanceChainHash(chain []byte) []byte { | ||
checksum := sha256.Sum256(chain) | ||
return checksum[:] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// 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 ctfe | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"crypto/sha256" | ||
"os" | ||
"sync" | ||
"testing" | ||
) | ||
|
||
func TestIssuanceChainServiceAddAndGet(t *testing.T) { | ||
tests := []struct { | ||
chain []byte | ||
}{ | ||
{readTestData(t, "leaf00.chain")}, | ||
{readTestData(t, "leaf01.chain")}, | ||
{readTestData(t, "leaf02.chain")}, | ||
{nil}, | ||
} | ||
|
||
ctx := context.Background() | ||
storage := &fakeIssuanceChainStorage{} | ||
cache := &fakeIssuanceChainCache{} | ||
issuanceChainService := newIssuanceChainService(storage, cache) | ||
|
||
for _, test := range tests { | ||
hash, err := issuanceChainService.Add(ctx, test.chain) | ||
if err != nil { | ||
t.Errorf("IssuanceChainService.Add(): %v", err) | ||
} | ||
|
||
got, err := issuanceChainService.GetByHash(ctx, hash) | ||
if err != nil { | ||
t.Errorf("IssuanceChainService.GetByHash(): %v", err) | ||
} | ||
|
||
if !bytes.Equal(got, test.chain) { | ||
t.Errorf("GetByHash = %v, want %v", got, test.chain) | ||
} | ||
} | ||
} | ||
|
||
func TestIssuanceChainHashLen(t *testing.T) { | ||
want := sha256.Size | ||
tests := []struct { | ||
chain []byte | ||
}{ | ||
{readTestData(t, "leaf00.chain")}, | ||
{readTestData(t, "leaf01.chain")}, | ||
{readTestData(t, "leaf02.chain")}, | ||
{nil}, | ||
} | ||
|
||
for _, test := range tests { | ||
got := len(issuanceChainHash(test.chain)) | ||
if got != want { | ||
t.Errorf("len(issuanceChainHash(%v)) = %d, want %d", test.chain, got, want) | ||
} | ||
} | ||
} | ||
|
||
func readTestData(t *testing.T, filename string) []byte { | ||
t.Helper() | ||
|
||
data, err := os.ReadFile("../testdata/" + filename) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
return data | ||
} | ||
|
||
type fakeIssuanceChainStorage struct { | ||
chains sync.Map | ||
} | ||
|
||
func (s *fakeIssuanceChainStorage) FindByKey(_ context.Context, key []byte) ([]byte, error) { | ||
val, _ := s.chains.Load(string(key)) | ||
chain, _ := val.([]byte) | ||
return chain, nil | ||
} | ||
|
||
func (s *fakeIssuanceChainStorage) Add(_ context.Context, key []byte, chain []byte) error { | ||
s.chains.Store(string(key), chain) | ||
return nil | ||
} | ||
|
||
type fakeIssuanceChainCache struct { | ||
chains sync.Map | ||
} | ||
|
||
func (c *fakeIssuanceChainCache) Get(_ context.Context, key []byte) ([]byte, error) { | ||
val, _ := c.chains.Load(string(key)) | ||
chain, _ := val.([]byte) | ||
return chain, nil | ||
} | ||
|
||
func (c *fakeIssuanceChainCache) Set(_ context.Context, key []byte, chain []byte) error { | ||
c.chains.Store(string(key), chain) | ||
return nil | ||
} |