diff --git a/certificate-authority/pb/signingRecords.go b/certificate-authority/pb/signingRecords.go index e4e650ef1..c9a2fdd54 100644 --- a/certificate-authority/pb/signingRecords.go +++ b/certificate-authority/pb/signingRecords.go @@ -32,8 +32,8 @@ func (credential *CredentialStatus) Validate() error { if _, ok := serial.SetString(credential.GetSerial(), 10); !ok { return errors.New("invalid signing record credential certificate serial number") } - if credential.GetIssuerId() == "" { - return errors.New("empty signing record credential issuer's ID") + if _, err := uuid.Parse(credential.GetIssuerId()); err != nil { + return fmt.Errorf("invalid signing record issuer's ID(%v): %w", credential.GetIssuerId(), err) } return nil } diff --git a/certificate-authority/service/cleanDatabase_test.go b/certificate-authority/service/cleanDatabase_test.go index 733ac4618..0db8d0540 100644 --- a/certificate-authority/service/cleanDatabase_test.go +++ b/certificate-authority/service/cleanDatabase_test.go @@ -50,7 +50,7 @@ func TestCertificateAuthorityServerCleanUpSigningRecords(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, } diff --git a/certificate-authority/service/grpc/deleteSigningRecords_test.go b/certificate-authority/service/grpc/deleteSigningRecords_test.go index f49898a12..ba6002c42 100644 --- a/certificate-authority/service/grpc/deleteSigningRecords_test.go +++ b/certificate-authority/service/grpc/deleteSigningRecords_test.go @@ -33,7 +33,7 @@ func TestCertificateAuthorityServerDeleteSigningRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerId", + IssuerId: "42424242-4242-4242-4242-424242424242", }, } type args struct { diff --git a/certificate-authority/service/grpc/getSigningRecords_test.go b/certificate-authority/service/grpc/getSigningRecords_test.go index ccb13c6c0..14d48a7a3 100644 --- a/certificate-authority/service/grpc/getSigningRecords_test.go +++ b/certificate-authority/service/grpc/getSigningRecords_test.go @@ -41,7 +41,7 @@ func TestCertificateAuthorityServerGetSigningRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerId", + IssuerId: "42424242-4242-4242-4242-424242424242", }, } type args struct { diff --git a/certificate-authority/service/http/revocationList.go b/certificate-authority/service/http/revocationList.go index 6131f590c..e58b99432 100644 --- a/certificate-authority/service/http/revocationList.go +++ b/certificate-authority/service/http/revocationList.go @@ -5,10 +5,12 @@ import ( "crypto/rand" "crypto/x509" "errors" + "fmt" "math/big" "net/http" "time" + "github.com/google/uuid" "github.com/gorilla/mux" "github.com/plgd-dev/hub/v2/certificate-authority/service/uri" "github.com/plgd-dev/hub/v2/certificate-authority/store" @@ -26,7 +28,10 @@ func errCannotGetRevocationList(err error) error { func (requestHandler *RequestHandler) revocationList(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) issuerID := vars[uri.IssuerIDKey] - + if _, err := uuid.Parse(issuerID); err != nil { + serverMux.WriteError(w, errCannotGetRevocationList(fmt.Errorf("invalid issuer's ID(%s): %w", issuerID, err))) + return + } template := &x509.RevocationList{ NextUpdate: time.Now().Add(time.Minute * 10), // TODO: pridat konfiguraciu, default napr. 10min } diff --git a/certificate-authority/service/http/revocationList_test.go b/certificate-authority/service/http/revocationList_test.go index fee05e4c5..2147df734 100644 --- a/certificate-authority/service/http/revocationList_test.go +++ b/certificate-authority/service/http/revocationList_test.go @@ -4,26 +4,51 @@ import ( "context" "crypto/x509" "io" + "math/big" "net/http" "testing" "time" certAuthURI "github.com/plgd-dev/hub/v2/certificate-authority/service/uri" + "github.com/plgd-dev/hub/v2/certificate-authority/store" "github.com/plgd-dev/hub/v2/certificate-authority/test" httpgwTest "github.com/plgd-dev/hub/v2/http-gateway/test" "github.com/plgd-dev/hub/v2/pkg/config/database" pkgGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" + pkgTime "github.com/plgd-dev/hub/v2/pkg/time" "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" testService "github.com/plgd-dev/hub/v2/test/service" "github.com/stretchr/testify/require" ) +func checkRevocationList(t *testing.T, crl *x509.RevocationList, certificates []*store.RevocationListCertificate) { + require.NotEmpty(t, crl.ThisUpdate) + require.NotEmpty(t, crl.NextUpdate) + expected := make([]x509.RevocationListEntry, 0, len(certificates)) + for _, cert := range certificates { + var serial big.Int + _, ok := serial.SetString(cert.Serial, 10) + require.True(t, ok) + expected = append(expected, x509.RevocationListEntry{ + SerialNumber: &serial, + RevocationTime: pkgTime.Unix(pkgTime.Unix(0, cert.Revocation).Unix(), 0).UTC(), + }) + } + actual := make([]x509.RevocationListEntry, 0, len(crl.RevokedCertificateEntries)) + for _, cert := range crl.RevokedCertificateEntries { + newCert := cert + newCert.Raw = nil + actual = append(actual, newCert) + } + require.Equal(t, expected, actual) +} + func TestRevocationList(t *testing.T) { if config.ACTIVE_DATABASE() == database.CqlDB { t.Skip("revocation list not supported for CqlDB") } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*3600) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() shutDown := testService.SetUpServices(context.Background(), t, testService.SetUpServicesOAuth|testService.SetUpServicesMachine2MachineOAuth) @@ -36,17 +61,56 @@ func TestRevocationList(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = pkgGrpc.CtxWithToken(ctx, token) - test.AddRevocationListToStore(ctx, t, s, time.Now()) + stored := test.AddRevocationListToStore(ctx, t, s, time.Now().Add(-2*time.Hour-time.Minute)) - request := httpgwTest.NewRequest(http.MethodGet, certAuthURI.SigningRevocationList, nil).Host(config.CERTIFICATE_AUTHORITY_HTTP_HOST).AuthToken(token).AddIssuerID(test.GetIssuerID(2)).Build() - httpResp := httpgwTest.HTTPDo(t, request) - respBody, err := io.ReadAll(httpResp.Body) - require.NoError(t, err) - err = httpResp.Body.Close() - require.NoError(t, err) - - _, err = x509.ParseRevocationList(respBody) - require.NoError(t, err) + type args struct { + issuer string + } + tests := []struct { + name string + args args + verifyCRL func(crl *x509.RevocationList) + wantErr bool + }{ + { + name: "all certificates expired", + args: args{ + issuer: test.GetIssuerID(0), + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + issuer: test.GetIssuerID(7), + }, + verifyCRL: func(crl *x509.RevocationList) { + var certificates []*store.RevocationListCertificate + for _, issuerCerts := range stored { + if issuerCerts.Id != test.GetIssuerID(7) { + continue + } + certificates = append(certificates, issuerCerts.Certificates...) + } + checkRevocationList(t, crl, certificates) + }, + }, + } - // time.Sleep(time.Minute) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + request := httpgwTest.NewRequest(http.MethodGet, certAuthURI.SigningRevocationList, nil).Host(config.CERTIFICATE_AUTHORITY_HTTP_HOST).AuthToken(token).AddIssuerID(tt.args.issuer).Build() + httpResp := httpgwTest.HTTPDo(t, request) + respBody, err := io.ReadAll(httpResp.Body) + require.NoError(t, err) + err = httpResp.Body.Close() + require.NoError(t, err) + crl, err := x509.ParseRevocationList(respBody) + if tt.wantErr { + require.Error(t, err) + return + } + tt.verifyCRL(crl) + }) + } } diff --git a/certificate-authority/store/cqldb/signingRecords_test.go b/certificate-authority/store/cqldb/signingRecords_test.go index 22cd27cb1..8335e81b3 100644 --- a/certificate-authority/store/cqldb/signingRecords_test.go +++ b/certificate-authority/store/cqldb/signingRecords_test.go @@ -33,7 +33,7 @@ func TestStoreInsertSigningRecord(t *testing.T) { Date: date.UnixNano() - 1, ValidUntilDate: date.UnixNano() - 1, Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, } tests := []struct { @@ -53,7 +53,7 @@ func TestStoreInsertSigningRecord(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, }, @@ -108,7 +108,7 @@ func TestStoreUpdateSigningRecord(t *testing.T) { Date: date.UnixNano() - 1, ValidUntilDate: date.UnixNano() - 1, Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, } tests := []struct { @@ -128,7 +128,7 @@ func TestStoreUpdateSigningRecord(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, }, @@ -148,7 +148,7 @@ func TestStoreUpdateSigningRecord(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, }, @@ -167,7 +167,7 @@ func TestStoreUpdateSigningRecord(t *testing.T) { Date: date1.UnixNano(), ValidUntilDate: date1.UnixNano(), Serial: big.NewInt(43).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, }, @@ -292,7 +292,7 @@ func TestStoreDeleteSigningRecord(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }) require.NoError(t, err) @@ -308,7 +308,7 @@ func TestStoreDeleteSigningRecord(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(43).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }) require.NoError(t, err) @@ -324,7 +324,7 @@ func TestStoreDeleteSigningRecord(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(44).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }) require.NoError(t, err) @@ -361,7 +361,7 @@ func TestStoreDeleteExpiredRecords(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }) require.NoError(t, err) @@ -406,7 +406,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, { @@ -421,7 +421,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(43).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, { @@ -436,7 +436,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(44).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, } @@ -572,7 +572,7 @@ func BenchmarkSigningRecords(b *testing.B) { Date: date.UnixNano(), ValidUntilDate: date.UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }) } diff --git a/certificate-authority/store/mongodb/revocationList.go b/certificate-authority/store/mongodb/revocationList.go index 4ff0b9067..b7d450b50 100644 --- a/certificate-authority/store/mongodb/revocationList.go +++ b/certificate-authority/store/mongodb/revocationList.go @@ -96,6 +96,9 @@ func (s *Store) GetRevocationLists(ctx context.Context, query store.RevocationLi }, } projection := bson.M{ + "_id": 1, + store.NumberKey: 1, + store.UpdatedAtKey: 1, store.CertificatesKey: bson.M{ "$filter": bson.M{ "input": "$" + store.CertificatesKey, diff --git a/certificate-authority/store/mongodb/revocationList_test.go b/certificate-authority/store/mongodb/revocationList_test.go index a88f622d8..328845942 100644 --- a/certificate-authority/store/mongodb/revocationList_test.go +++ b/certificate-authority/store/mongodb/revocationList_test.go @@ -9,7 +9,6 @@ import ( "github.com/google/uuid" "github.com/plgd-dev/hub/v2/certificate-authority/store" "github.com/plgd-dev/hub/v2/certificate-authority/test" - pkgTime "github.com/plgd-dev/hub/v2/pkg/time" "github.com/stretchr/testify/require" ) @@ -64,8 +63,8 @@ func TestRevokeCertificates(t *testing.T) { id: id, certificate: &store.RevocationListCertificate{ Serial: "1", - Expiration: pkgTime.UnixNano(time.Now().Add(time.Hour)), - Revocation: time.Now().UnixNano(), + Expiration: time.Now().Add(time.Hour).Unix(), + Revocation: time.Now().Unix(), }, }, }, @@ -75,8 +74,8 @@ func TestRevokeCertificates(t *testing.T) { id: id, certificate: &store.RevocationListCertificate{ Serial: "2", - Expiration: pkgTime.UnixNano(time.Now().Add(time.Hour)), - Revocation: time.Now().UnixNano(), + Expiration: time.Now().Add(time.Hour).Unix(), + Revocation: time.Now().Unix(), }, }, }, @@ -86,8 +85,8 @@ func TestRevokeCertificates(t *testing.T) { id: id, certificate: &store.RevocationListCertificate{ Serial: "2", - Expiration: pkgTime.UnixNano(time.Now().Add(time.Hour)), - Revocation: time.Now().UnixNano(), + Expiration: time.Now().Add(time.Hour).Unix(), + Revocation: time.Now().Unix(), }, }, wantErr: true, @@ -98,8 +97,8 @@ func TestRevokeCertificates(t *testing.T) { id: uuid.NewString(), certificate: &store.RevocationListCertificate{ Serial: "2", - Expiration: pkgTime.UnixNano(time.Now().Add(time.Hour)), - Revocation: time.Now().UnixNano(), + Expiration: time.Now().Add(time.Hour).Unix(), + Revocation: time.Now().Unix(), }, }, }, @@ -138,7 +137,7 @@ func TestGetRevocationLists(t *testing.T) { { name: "no matching ID", args: args{ - query: store.RevocationListsQuery{IssuerIdFilter: []string{"invalid"}}, + query: store.RevocationListsQuery{IssuerIdFilter: []string{"00000000-0000-0000-0000-123456789012"}}, }, wantErr: true, }, diff --git a/certificate-authority/store/mongodb/signingRecords_test.go b/certificate-authority/store/mongodb/signingRecords_test.go index 90ddf4ad4..e82044992 100644 --- a/certificate-authority/store/mongodb/signingRecords_test.go +++ b/certificate-authority/store/mongodb/signingRecords_test.go @@ -42,7 +42,7 @@ func TestStoreUpdateSigningRecord(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, }, @@ -62,7 +62,7 @@ func TestStoreUpdateSigningRecord(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, }, @@ -81,7 +81,7 @@ func TestStoreUpdateSigningRecord(t *testing.T) { Date: constDate1().UnixNano(), ValidUntilDate: constDate1().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, }, @@ -215,7 +215,7 @@ func TestStoreDeleteSigningRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }) require.NoError(t, err) @@ -281,7 +281,7 @@ func TestStoreDeleteExpiredRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }) require.NoError(t, err) @@ -324,7 +324,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, { @@ -339,7 +339,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, { @@ -354,7 +354,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, { @@ -369,7 +369,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { Date: constDate().UnixNano(), ValidUntilDate: constDate().UnixNano(), Serial: big.NewInt(42).String(), - IssuerId: "issuerID", + IssuerId: "42424242-4242-4242-4242-424242424242", }, }, } diff --git a/certificate-authority/store/revocationList.go b/certificate-authority/store/revocationList.go index aae816f58..06fc9e43a 100644 --- a/certificate-authority/store/revocationList.go +++ b/certificate-authority/store/revocationList.go @@ -1,12 +1,19 @@ package store -import "errors" +import ( + "errors" + "fmt" + + "github.com/google/uuid" +) const ( - CertificatesKey = "certificates" // must match with RevocationList.Certificates tag - SerialKey = "serial" // must match with RevocationListCertificate.Serial tag - ExpirationKey = "expiration" // must match with RevocationListCertificate.Expiration tag - RevocationKey = "revocation" // must match with RevocationListCertificate.Revocation tag + CertificatesKey = "certificates" // must match with RevocationList.Certificates bson tag + UpdatedAtKey = "updatedAt" // must match with RevocationListCertificate.UpdatedAt bson tag + NumberKey = "number" // must match with RevocationListCertificate.NumberKey bson tag + SerialKey = "serial" // must match with RevocationListCertificate.Serial bson tag + ExpirationKey = "expiration" // must match with RevocationListCertificate.Expiration bson tag + RevocationKey = "revocation" // must match with RevocationListCertificate.Revocation bson tag ) type RevocationListCertificate struct { @@ -41,8 +48,8 @@ type RevocationList struct { } func (rl *RevocationList) Validate() error { - if rl.Id == "" { - return errors.New("id not set") + if _, err := uuid.Parse(rl.Id); err != nil { + return fmt.Errorf("invalid ID(%v): %w", rl.Id, err) } if rl.UpdatedAt == 0 { return errors.New("update time not set") diff --git a/certificate-authority/test/revocationList.go b/certificate-authority/test/revocationList.go index f756a25ee..f724839c9 100644 --- a/certificate-authority/test/revocationList.go +++ b/certificate-authority/test/revocationList.go @@ -20,7 +20,7 @@ var ( ) func GetIssuerID(i int) string { - return "i" + fmt.Sprintf("%011d", i) + return fmt.Sprintf("49000000-0000-0000-0000-%012d", i) } func GetCertificateSerial(i int) string { @@ -70,6 +70,8 @@ func CheckRevocationLists(t *testing.T, expected, actual map[string]*store.Revoc for id, rl := range actual { expRl, ok := expected[id] require.True(t, ok) + require.Equal(t, expRl.Number, rl.Number) + require.Equal(t, expRl.UpdatedAt, rl.UpdatedAt) require.Len(t, rl.Certificates, len(expRl.Certificates)) for i := range rl.Certificates { require.Equal(t, expRl.Certificates[i].Serial, rl.Certificates[i].Serial)