Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove opencontainer dependency for timestamping code #11

Merged
merged 4 commits into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
module github.com/notaryproject/notation-core-go

go 1.17

require github.com/opencontainers/go-digest v1.0.0
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
10 changes: 10 additions & 0 deletions timestamp/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package timestamp

// MalformedRequestError is used when timestamping request is malformed.
type MalformedRequestError struct {
Comment on lines +3 to +4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is better to name it ErrorMalformedRequest. It's a convention to put Error as a prefix.

msg string
}

func (e MalformedRequestError) Error() string {
return e.msg
}
20 changes: 11 additions & 9 deletions timestamp/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ func TestHTTPTimestampGranted(t *testing.T) {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
message := []byte("notation")
req, err := NewRequestFromBytes(message)
req, err := NewRequestFromContent(message, crypto.SHA256)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
t.Fatalf("NewRequestFromContent() error = %v", err)
}
ctx := context.Background()
resp, err := tsa.Timestamp(ctx, req)
Expand Down Expand Up @@ -128,7 +128,7 @@ func TestHTTPTimestampGranted(t *testing.T) {
if err != nil {
t.Fatal("SignedToken.Info() error =", err)
}
if err := info.Verify(message); err != nil {
if err := info.VerifyContent(message); err != nil {
t.Errorf("TSTInfo.Verify() error = %v", err)
}
timestamp, accuracy := info.Timestamp()
Expand Down Expand Up @@ -173,9 +173,9 @@ func TestHTTPTimestampRejection(t *testing.T) {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
message := []byte("notation")
req, err := NewRequestFromBytes(message)
req, err := NewRequestFromContent(message, crypto.SHA256)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
t.Fatalf("NewRequestFromContent() error = %v", err)
}
ctx := context.Background()
resp, err := tsa.Timestamp(ctx, req)
Expand Down Expand Up @@ -215,9 +215,10 @@ func TestHTTPTimestampBadEndpoint(t *testing.T) {
if err != nil {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
req, err := NewRequestFromString("notation")
message := []byte("notation")
req, err := NewRequestFromContent(message, crypto.SHA256)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
t.Fatalf("NewRequestFromContent() error = %v", err)
}
ctx := context.Background()
_, err = tsa.Timestamp(ctx, req)
Expand All @@ -238,9 +239,10 @@ func TestHTTPTimestampEndpointNotFound(t *testing.T) {
if err != nil {
t.Fatalf("NewHTTPTimestamper() error = %v", err)
}
req, err := NewRequestFromString("notation")
message := []byte("notation")
req, err := NewRequestFromContent(message, crypto.SHA256)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
t.Fatalf("NewRequestFromContent() error = %v", err)
}
ctx := context.Background()
_, err = tsa.Timestamp(ctx, req)
Expand Down
55 changes: 34 additions & 21 deletions timestamp/request.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package timestamp

import (
"crypto"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"errors"
"fmt"
"math/big"

"github.com/notaryproject/notation-core-go/internal/crypto/hashutil"
"github.com/notaryproject/notation-core-go/internal/crypto/oid"
digest "github.com/opencontainers/go-digest"
)

// MessageImprint contains the hash of the datum to be time-stamped.
Expand Down Expand Up @@ -37,36 +38,37 @@ type Request struct {
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())
// NewRequest creates a request based on the given digest and hash algorithm.
func NewRequest(digest []byte, alg crypto.Hash) (*Request, error) {
priteshbandi marked this conversation as resolved.
Show resolved Hide resolved
err := validate(digest, alg)
if err != nil {
return nil, err
}
hashedMessage, err := hex.DecodeString(contentDigest.Encoded())

hashAlg, err := getOID(alg)
if err != nil {
return nil, err
}
return &Request{
Version: 1,
MessageImprint: MessageImprint{
HashAlgorithm: pkix.AlgorithmIdentifier{
Algorithm: hashAlgorithm,
Algorithm: hashAlg,
},
HashedMessage: hashedMessage,
HashedMessage: digest,
},
CertReq: true,
}, nil
}

// NewRequestFromBytes creates a request based on the given byte slice.
func NewRequestFromBytes(content []byte) (*Request, error) {
return NewRequest(digest.FromBytes(content))
}
// NewRequestFromContent creates a request based on the given data and hash algorithm.
func NewRequestFromContent(content []byte, alg crypto.Hash) (*Request, error) {
digest, err := hashutil.ComputeHash(alg, content)
if err != nil {
return nil, err
}

// NewRequestFromString creates a request based on the given string.
func NewRequestFromString(content string) (*Request, error) {
return NewRequest(digest.FromString(content))
return NewRequest(digest, alg)
}

// MarshalBinary encodes the request to binary form.
Expand All @@ -85,15 +87,26 @@ func (r *Request) UnmarshalBinary(data []byte) error {
return err
}

// getOIDFromDigestAlgorithm returns corresponding ASN.1 OID for the given digest algorithm.
func getOIDFromDigestAlgorithm(alg digest.Algorithm) (asn1.ObjectIdentifier, error) {
// getOID returns corresponding ASN.1 OID for the given Hash algorithm.
func getOID(alg crypto.Hash) (asn1.ObjectIdentifier, error) {
switch alg {
case digest.SHA256:
case crypto.SHA256:
return oid.SHA256, nil
case digest.SHA384:
case crypto.SHA384:
return oid.SHA384, nil
case digest.SHA512:
case crypto.SHA512:
return oid.SHA512, nil
}
return nil, digest.ErrDigestUnsupported
return nil, MalformedRequestError{msg: fmt.Sprintf("unsupported hashing algorithm: %s", alg)}
}

func validate(digest []byte, alg crypto.Hash) error {
if !(alg == crypto.SHA256 || alg == crypto.SHA384 || alg == crypto.SHA512) {
return MalformedRequestError{msg: fmt.Sprintf("unsupported hashing algorithm: %s", alg)}
}

if len(digest) != alg.Size() {
return MalformedRequestError{msg: fmt.Sprintf("digest is of incorrect size: %d", len(digest))}
}
return nil
}
17 changes: 9 additions & 8 deletions timestamp/timestamptest/tsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package timestamptest

import (
"context"
"crypto"
"crypto/x509"
"testing"
"time"
Expand All @@ -24,9 +25,9 @@ func TestTSATimestampGranted(t *testing.T) {

// do timestamp
message := []byte("notation")
req, err := timestamp.NewRequestFromBytes(message)
req, err := timestamp.NewRequestFromContent(message, crypto.SHA256)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
t.Fatalf("NewRequestFromContent() error = %v", err)
}
ctx := context.Background()
resp, err := tsa.Timestamp(ctx, req)
Expand Down Expand Up @@ -55,13 +56,13 @@ func TestTSATimestampGranted(t *testing.T) {
if err != nil {
t.Fatal("SignedToken.Info() error =", err)
}
if err := info.Verify(message); err != nil {
if err := info.VerifyContent(message); err != nil {
t.Errorf("TSTInfo.Verify() error = %v", err)
}
timestamp, accuracy := info.Timestamp()
ts, accuracy := info.Timestamp()
wantTimestamp := now
if timestamp != wantTimestamp {
t.Errorf("TSTInfo.Timestamp() Timestamp = %v, want %v", timestamp, wantTimestamp)
if ts != wantTimestamp {
t.Errorf("TSTInfo.Timestamp() Timestamp = %v, want %v", ts, wantTimestamp)
}
wantAccuracy := time.Second
if accuracy != wantAccuracy {
Expand All @@ -78,9 +79,9 @@ func TestTSATimestampRejection(t *testing.T) {

// do timestamp
message := []byte("notation")
req, err := timestamp.NewRequestFromBytes(message)
req, err := timestamp.NewRequestFromContent(message, crypto.SHA256)
if err != nil {
t.Fatalf("NewRequestFromString() error = %v", err)
t.Fatalf("NewRequestFromContent() error = %v", err)
}
req.MessageImprint.HashAlgorithm.Algorithm = oid.SHA1WithRSA // set bad algorithm
ctx := context.Background()
Expand Down
10 changes: 8 additions & 2 deletions timestamp/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ type TSTInfo struct {
Extensions []pkix.Extension `asn1:"optional,tag:1"`
}

// Verify verifies the message against the timestamp token information.
func (tst *TSTInfo) Verify(message []byte) error {
// VerifyContent verifies the message against the timestamp token information.
func (tst *TSTInfo) VerifyContent(message []byte) error {
hashAlg := tst.MessageImprint.HashAlgorithm.Algorithm
hash, ok := oid.ConvertToHash(hashAlg)
if !ok {
Expand All @@ -117,6 +117,12 @@ func (tst *TSTInfo) Verify(message []byte) error {
if err != nil {
return err
}

return tst.Verify(messageDigest)
}

// Verify verifies the message digest against the timestamp token information.
func (tst *TSTInfo) Verify(messageDigest []byte) error {
if !bytes.Equal(tst.MessageImprint.HashedMessage, messageDigest) {
return errors.New("mismatch message digest")
}
Expand Down
4 changes: 2 additions & 2 deletions x509/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func parseCertificates(data []byte) ([]*x509.Certificate, error) {
// ValidateCertChain takes an ordered certificate chain and validates issuance from leaf to root
func ValidateCertChain(certChain []*x509.Certificate) error {
if len(certChain) < 2 {
return errors.New("certificate chain must contain at least two certificates")
return errors.New("certificate chain must contain at least two certificates: a root and a leaf certificate")
}

for i, cert := range certChain {
Expand All @@ -60,7 +60,7 @@ func ValidateCertChain(certChain []*x509.Certificate) error {
if isSelfSigned(cert) {
return errors.New("certificate chain must not contain self-signed intermediate certificates")
} else if nextCert := certChain[i+1]; !isIssuedBy(cert, nextCert) {
return fmt.Errorf("signature on certificate %q is not issued by %q", cert.Subject.String(), nextCert.Subject.String())
return fmt.Errorf("certificate with subject %q is not issued by %q", cert.Subject, nextCert.Subject)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions x509/cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func TestFailEmptyChain(t *testing.T) {
certChain := []*x509.Certificate{signingCert}

err := ValidateCertChain(certChain)
assertErrorEqual("certificate chain must contain at least two certificates", err, t)
assertErrorEqual("certificate chain must contain at least two certificates: a root and a leaf certificate", err, t)
}

func TestFailChainNotEndingInRoot(t *testing.T) {
Expand All @@ -155,14 +155,14 @@ func TestFailChainNotOrdered(t *testing.T) {
certChain := []*x509.Certificate{signingCert, intermediateCert2, intermediateCert1, rootCert}

err := ValidateCertChain(certChain)
assertErrorEqual("signature on certificate \"CN=Wallaby,OU=Signer,O=Amazon.com Services LLC,L=Seattle,ST=Washington,C=US,1.2.840.113549.1.9.1=#0c126a64646f6e617340616d617a6f6e2e636f6d\" is not issued by \"CN=jddonas-intermediate,OU=Wallaby,O=Marsupial Ventures\"", err, t)
assertErrorEqual("certificate with subject \"CN=Wallaby,OU=Signer,O=Amazon.com Services LLC,L=Seattle,ST=Washington,C=US,1.2.840.113549.1.9.1=#0c126a64646f6e617340616d617a6f6e2e636f6d\" is not issued by \"CN=jddonas-intermediate,OU=Wallaby,O=Marsupial Ventures\"", err, t)
}

func TestFailChainWithUnrelatedCert(t *testing.T) {
certChain := []*x509.Certificate{signingCert, unrelatedCert, intermediateCert2, rootCert}

err := ValidateCertChain(certChain)
assertErrorEqual("signature on certificate \"CN=Wallaby,OU=Signer,O=Amazon.com Services LLC,L=Seattle,ST=Washington,C=US,1.2.840.113549.1.9.1=#0c126a64646f6e617340616d617a6f6e2e636f6d\" is not issued by \"CN=imburger-dev-root,OU=AWS Cryptography,O=Marsupial Ventures,L=Seattle,ST=Washington,C=US\"", err, t)
assertErrorEqual("certificate with subject \"CN=Wallaby,OU=Signer,O=Amazon.com Services LLC,L=Seattle,ST=Washington,C=US,1.2.840.113549.1.9.1=#0c126a64646f6e617340616d617a6f6e2e636f6d\" is not issued by \"CN=imburger-dev-root,OU=AWS Cryptography,O=Marsupial Ventures,L=Seattle,ST=Washington,C=US\"", err, t)
}

func TestFailChainWithDuplicateRepeatedRoots(t *testing.T) {
Expand Down