Repository: scionproto/scion
Pull request #3612 (2020-01-16 08:10:24)

cert renewal: Adapt request to design document
We can not use `cert.Base` because the KeyType and keys map is different in the renewal request than in `cert.Base`

Documentation is in https://github.com/scionproto/scion/pull/3598

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/scionproto/scion/3612)
<!-- Reviewable:end -->



Patch:
diff --git a/go/lib/ctrl/cert_mgmt/chain_iss_req.go b/go/lib/ctrl/cert_mgmt/chain_iss_req.go
index 07374e74d..67e34c01d 100644
--- a/go/lib/ctrl/cert_mgmt/chain_iss_req.go
+++ b/go/lib/ctrl/cert_mgmt/chain_iss_req.go
@@ -46,5 +46,5 @@ func (c *ChainIssReq) String() string {
 		return fmt.Sprintf("Invalid renewal req(encoded.info): %v", err)
 	}
 	return fmt.Sprintf("Renewal request: IA: %s, Version: %d, ReqTime: %s",
-		ri.Base.Subject, ri.Base.Version, ri.RequestTime)
+		ri.Subject, ri.Version, ri.RequestTime)
 }
diff --git a/go/lib/scrypto/cert/renewal/BUILD.bazel b/go/lib/scrypto/cert/renewal/BUILD.bazel
index 6c918a512..960f2aada 100644
--- a/go/lib/scrypto/cert/renewal/BUILD.bazel
+++ b/go/lib/scrypto/cert/renewal/BUILD.bazel
@@ -2,7 +2,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 
 go_library(
     name = "go_default_library",
-    srcs = ["request.go"],
+    srcs = [
+        "keytype.go",
+        "request.go",
+    ],
     importpath = "github.com/scionproto/scion/go/lib/scrypto/cert/renewal",
     visibility = ["//visibility:public"],
     deps = [
@@ -17,13 +20,13 @@ go_library(
 go_test(
     name = "go_default_test",
     srcs = [
+        "keytype_test.go",
         "request_json_test.go",
         "request_test.go",
     ],
     embed = [":go_default_library"],
     deps = [
         "//go/lib/scrypto:go_default_library",
-        "//go/lib/scrypto/cert:go_default_library",
         "//go/lib/util:go_default_library",
         "//go/lib/xtest:go_default_library",
         "@com_github_stretchr_testify//assert:go_default_library",
diff --git a/go/lib/scrypto/cert/renewal/keytype.go b/go/lib/scrypto/cert/renewal/keytype.go
new file mode 100644
index 000000000..076791d62
--- /dev/null
+++ b/go/lib/scrypto/cert/renewal/keytype.go
@@ -0,0 +1,63 @@
+// Copyright 2019 Anapaya Systems
+//
+// 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 renewal
+
+import (
+	"github.com/scionproto/scion/go/lib/scrypto/cert"
+	"github.com/scionproto/scion/go/lib/serrors"
+)
+
+// ErrInvalidKeyType indicates an inexistent key type.
+var ErrInvalidKeyType = serrors.New("invalid key type")
+
+// KeyType is the type of the key used in renewal. Can either be `signing` or
+// `revocation`.
+type KeyType string
+
+// KeyType values
+const (
+	// SigningKey is the signing key type.
+	SigningKey = KeyType(cert.SigningKeyJSON)
+	// RevocationKey is the revocation key type.
+	RevocationKey = KeyType(cert.RevocationKeyJSON)
+)
+
+// Validate validates that the KeyType has a valid value.
+func (t KeyType) Validate() error {
+	switch string(t) {
+	case string(SigningKey), string(RevocationKey):
+		return nil
+	default:
+		return serrors.WithCtx(ErrInvalidKeyType, "raw", string(t))
+	}
+}
+
+// UnmarshalText allows to do validation on KeyType when parsing
+func (t *KeyType) UnmarshalText(b []byte) error {
+	*t = KeyType(b)
+	if err := t.Validate(); err != nil {
+		*t = ""
+		return err
+	}
+	return nil
+}
+
+// MarshalText validates the KeyType before
+func (t KeyType) MarshalText() ([]byte, error) {
+	if err := t.Validate(); err != nil {
+		return nil, err
+	}
+	return []byte(t), nil
+}
diff --git a/go/lib/scrypto/cert/renewal/request.go b/go/lib/scrypto/cert/renewal/request.go
index 7ba736ba9..8de6f647c 100644
--- a/go/lib/scrypto/cert/renewal/request.go
+++ b/go/lib/scrypto/cert/renewal/request.go
@@ -33,10 +33,38 @@ var (
 	ErrNotUTF8 = serrors.New("not utf-8 encoded")
 )
 
+// KeyMeta is the meta information about a key.
+type KeyMeta struct {
+	// Key is the public key.
+	Key []byte `json:"key"`
+}
+
+// Keys contains the public keys.
+type Keys struct {
+	Signing    KeyMeta `json:"signing"`
+	Revocation KeyMeta `json:"revocation"`
+}
+
 // RequestInfo is the information of the renewal request.
 type RequestInfo struct {
-	cert.Base
-	Issuer      addr.IA       `json:"issuer"`
+	// Subject identifies the subject of the requested certificate.
+	Subject addr.IA `json:"subject"`
+	// Version indicates the requested certificate version.
+	Version scrypto.Version `json:"version"`
+	// FormatVersion is the request format version.
+	FormatVersion cert.FormatVersion `json:"format_version"`
+	// Description is the requested human-readable description of the certificate.
+	Description string `json:"description"`
+	// OptionalDistributionPoints contains optional certificate revocation
+	// distribution points.
+	OptionalDistributionPoints []addr.IA `json:"optional_distribution_points"`
+	// Validity defines the requested validity period of the certificate.
+	Validity *scrypto.Validity `json:"validity"`
+	// Keys holds all keys authenticated by this certificate.
+	Keys Keys `json:"keys"`
+	// Issuer identifies the Issuer this request is addressed to.
+	Issuer addr.IA `json:"issuer"`
+	// RequestTime indicates when this request was created.
 	RequestTime util.UnixTime `json:"request_time"`
 }
 
@@ -161,7 +189,7 @@ func (e EncodedProtected) Decode() (Protected, error) {
 // Protected contains the signature metadata.
 type Protected struct {
 	Algorithm  string             `json:"alg"`
-	KeyType    cert.KeyType       `json:"key_type"`
+	KeyType    KeyType            `json:"key_type"`
 	KeyVersion scrypto.KeyVersion `json:"key_version"`
 	Crit       CritRequest        `json:"crit"`
 }
@@ -189,7 +217,7 @@ func (p *Protected) UnmarshalJSON(b []byte) error {
 type protectedAlias struct {
 	Algorithm  *string             `json:"alg"`
 	Crit       *CritRequest        `json:"crit"`
-	KeyType    *cert.KeyType       `json:"key_type"`
+	KeyType    *KeyType            `json:"key_type"`
 	KeyVersion *scrypto.KeyVersion `json:"key_version"`
 }
 


Test patch:
diff --git a/go/lib/scrypto/cert/renewal/keytype_test.go b/go/lib/scrypto/cert/renewal/keytype_test.go
new file mode 100644
index 000000000..a75514f05
--- /dev/null
+++ b/go/lib/scrypto/cert/renewal/keytype_test.go
@@ -0,0 +1,114 @@
+// Copyright 2019 Anapaya Systems
+//
+// 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 renewal_test
+
+import (
+	"encoding/json"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/scionproto/scion/go/lib/scrypto/cert/renewal"
+	"github.com/scionproto/scion/go/lib/xtest"
+)
+
+func TestKeyTypeValidate(t *testing.T) {
+	tests := map[string]struct {
+		KeyType renewal.KeyType
+		Error   error
+	}{
+		"valid signing":    {KeyType: renewal.SigningKey},
+		"valid revocation": {KeyType: renewal.RevocationKey},
+		"invalid encryption": {
+			KeyType: renewal.KeyType("encryption"),
+			Error:   renewal.ErrInvalidKeyType,
+		},
+		"invalid garbage": {
+			KeyType: renewal.KeyType("garbage"),
+			Error:   renewal.ErrInvalidKeyType,
+		},
+	}
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			xtest.AssertErrorsIs(t, test.KeyType.Validate(), test.Error)
+		})
+	}
+}
+
+func TestKeyTypeUnmarshal(t *testing.T) {
+	tests := map[string]struct {
+		Input     string
+		Expected  renewal.KeyType
+		Assertion assert.ErrorAssertionFunc
+	}{
+		"Garbage": {
+			Input:     `"test"`,
+			Assertion: assert.Error,
+		},
+		"Integer": {
+			Input:     `42`,
+			Assertion: assert.Error,
+		},
+		"Wrong case": {
+			Input:     `"Signing"`,
+			Assertion: assert.Error,
+		},
+		"SigningKey": {
+			Input:     `"signing"`,
+			Assertion: assert.NoError,
+			Expected:  renewal.SigningKey,
+		},
+		"RevocationKey": {
+			Input:     `"revocation"`,
+			Assertion: assert.NoError,
+			Expected:  renewal.RevocationKey,
+		},
+	}
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			var keyType renewal.KeyType
+			test.Assertion(t, json.Unmarshal([]byte(test.Input), &keyType))
+			assert.Equal(t, test.Expected, keyType)
+		})
+	}
+}
+
+func TestKeyTypeMarshal(t *testing.T) {
+	tests := map[string]struct {
+		KeyType renewal.KeyType
+		Error   error
+	}{
+		"valid signing":    {KeyType: renewal.SigningKey},
+		"valid revocation": {KeyType: renewal.RevocationKey},
+		"invalid encryption": {
+			KeyType: renewal.KeyType("encryption"),
+			Error:   renewal.ErrInvalidKeyType,
+		},
+		"invalid garbage": {
+			KeyType: renewal.KeyType("garbage"),
+			Error:   renewal.ErrInvalidKeyType,
+		},
+	}
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			b, err := json.Marshal(test.KeyType)
+			xtest.AssertErrorsIs(t, err, test.Error)
+			if test.Error == nil {
+				assert.Equal(t, string(test.KeyType), strings.Trim(string(b), `"`))
+			}
+		})
+	}
+}
diff --git a/go/lib/scrypto/cert/renewal/request_json_test.go b/go/lib/scrypto/cert/renewal/request_json_test.go
index 1098c2cc5..adf7b8629 100644
--- a/go/lib/scrypto/cert/renewal/request_json_test.go
+++ b/go/lib/scrypto/cert/renewal/request_json_test.go
@@ -22,7 +22,6 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/scionproto/scion/go/lib/scrypto"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
 	"github.com/scionproto/scion/go/lib/scrypto/cert/renewal"
 )
 
@@ -87,7 +86,7 @@ func TestProtectedUnmarshalJSON(t *testing.T) {
 			}`,
 			Protected: renewal.Protected{
 				Algorithm:  scrypto.Ed25519,
-				KeyType:    cert.SigningKey,
+				KeyType:    renewal.SigningKey,
 				KeyVersion: 2,
 			},
 		},
diff --git a/go/lib/scrypto/cert/renewal/request_test.go b/go/lib/scrypto/cert/renewal/request_test.go
index bb734397a..d16bd2cc9 100644
--- a/go/lib/scrypto/cert/renewal/request_test.go
+++ b/go/lib/scrypto/cert/renewal/request_test.go
@@ -22,7 +22,6 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"github.com/scionproto/scion/go/lib/scrypto"
-	"github.com/scionproto/scion/go/lib/scrypto/cert"
 	"github.com/scionproto/scion/go/lib/scrypto/cert/renewal"
 	"github.com/scionproto/scion/go/lib/util"
 	"github.com/scionproto/scion/go/lib/xtest"
@@ -175,7 +174,7 @@ func TestEncodeProtected(t *testing.T) {
 		},
 		"Invalid KeyType": {
 			Modify: func(base *renewal.Protected) {
-				base.KeyType = cert.KeyType(404)
+				base.KeyType = renewal.KeyType("not found")
 			},
 			Assertion: assert.Error,
 		},
@@ -184,7 +183,7 @@ func TestEncodeProtected(t *testing.T) {
 		t.Run(name, func(t *testing.T) {
 			base := renewal.Protected{
 				Algorithm:  scrypto.Ed25519,
-				KeyType:    cert.SigningKey,
+				KeyType:    renewal.SigningKey,
 				KeyVersion: 2,
 			}
 			test.Modify(&base)
@@ -203,7 +202,7 @@ func TestEncodeProtected(t *testing.T) {
 func TestEncodedProtectedDecode(t *testing.T) {
 	valid, err := renewal.EncodeProtected(renewal.Protected{
 		Algorithm:  scrypto.Ed25519,
-		KeyType:    cert.SigningKey,
+		KeyType:    renewal.SigningKey,
 		KeyVersion: 2,
 	})
 	require.NoError(t, err)
@@ -243,32 +242,17 @@ func encode(input string) string {
 func newRequestInfo(now time.Time) renewal.RequestInfo {
 	now = now.Truncate(time.Second)
 	return renewal.RequestInfo{
-		Base: cert.Base{
-			Subject:       xtest.MustParseIA("1-ff00:0:111"),
-			Version:       2,
-			FormatVersion: 1,
-			Description:   "This is a base request",
-			Validity: &scrypto.Validity{
-				NotBefore: util.UnixTime{Time: now},
-				NotAfter:  util.UnixTime{Time: now.Add(8760 * time.Hour)},
-			},
-			Keys: map[cert.KeyType]scrypto.KeyMeta{
-				cert.EncryptionKey: {
-					KeyVersion: 1,
-					Algorithm:  scrypto.Curve25519xSalsa20Poly1305,
-					Key:        []byte("encryptKey1"),
-				},
-				cert.RevocationKey: {
-					KeyVersion: 1,
-					Algorithm:  scrypto.Ed25519,
-					Key:        []byte("revKey1"),
-				},
-				cert.SigningKey: {
-					KeyVersion: 1,
-					Algorithm:  scrypto.Ed25519,
-					Key:        []byte("signKey1"),
-				},
-			},
+		Subject:       xtest.MustParseIA("1-ff00:0:111"),
+		Version:       2,
+		FormatVersion: 1,
+		Description:   "This is a base request",
+		Validity: &scrypto.Validity{
+			NotBefore: util.UnixTime{Time: now},
+			NotAfter:  util.UnixTime{Time: now.Add(8760 * time.Hour)},
+		},
+		Keys: renewal.Keys{
+			Signing:    renewal.KeyMeta{Key: []byte("signKey1")},
+			Revocation: renewal.KeyMeta{Key: []byte("revKey1")},
 		},
 		Issuer:      xtest.MustParseIA("1-ff00:0:110"),
 		RequestTime: util.UnixTime{Time: now},