Skip to content

Commit

Permalink
cert: Define signed AS certificate format
Browse files Browse the repository at this point in the history
This PR defines the signed format for the AS certificate according to
the Flattened JWS JSON Serialization Syntax.

contributes to scionproto#2853
  • Loading branch information
oncilla committed Aug 6, 2019
1 parent 1bcfa6d commit 0a1b65d
Show file tree
Hide file tree
Showing 8 changed files with 646 additions and 63 deletions.
4 changes: 4 additions & 0 deletions go/lib/scrypto/cert/v2/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ go_library(
name = "go_default_library",
srcs = [
"as.go",
"as_signed.go",
"base.go",
"base_signed.go",
"issuer.go",
],
importpath = "github.com/scionproto/scion/go/lib/scrypto/cert/v2",
Expand All @@ -20,6 +22,8 @@ go_test(
name = "go_default_test",
srcs = [
"as_json_test.go",
"as_signed_json_test.go",
"as_signed_test.go",
"as_test.go",
"base_json_test.go",
"base_test.go",
Expand Down
186 changes: 186 additions & 0 deletions go/lib/scrypto/cert/v2/as_signed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// 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 cert

import (
"bytes"
"encoding/json"
"errors"
"unicode/utf8"

"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/common"
"github.com/scionproto/scion/go/lib/scrypto"
)

// ErrIANotSet indicates the issuing IA is not set.
var ErrIANotSet = errors.New("IA not set")

type SignedAS struct {
Encoded EncodedAS `json:"payload"`
EncodedProtected EncodedProtectedAS `json:"protected"`
Signature common.RawBytes `json:"signature"`
}

// SigInput computes the signature input according to rfc7517 (see:
// https://tools.ietf.org/html/rfc7515#section-5.1)
func (s SignedAS) SigInput() common.RawBytes {
return scrypto.JWSignatureInput(s.EncodedProtected, s.Encoded)
}

// EncodedAS is the the base64url encoded marshaled AS certificate.
type EncodedAS []byte

// EncodeAS encodes and returns the packed AS certificate.
func EncodeAS(c *AS) (EncodedAS, error) {
b, err := json.Marshal(c)
if err != nil {
return nil, err
}
return []byte(scrypto.Base64.EncodeToString(b)), nil
}

// Decode returns the decoded Decode.
func (p *EncodedAS) Decode() (*AS, error) {
b, err := scrypto.Base64.DecodeString(string(*p))
if err != nil {
return nil, err
}
var c AS
if err := json.Unmarshal(b, &c); err != nil {
return nil, err
}
return &c, nil
}

// EncodedProtectedAS is the base64url encoded utf-8 metadata.
type EncodedProtectedAS []byte

// EncodeProtectedAS encodes the protected header.
func EncodeProtectedAS(p ProtectedAS) (EncodedProtectedAS, error) {
// json.Marshal forces the necessary utf-8 encoding.
b, err := json.Marshal(p)
if err != nil {
return nil, err
}
return []byte(scrypto.Base64.EncodeToString(b)), nil
}

// Decode decodes and return the protected header.
func (h *EncodedProtectedAS) Decode() (ProtectedAS, error) {
b, err := scrypto.Base64.DecodeString(string(*h))
if err != nil {
return ProtectedAS{}, err
}
if !utf8.Valid(b) {
return ProtectedAS{}, ErrNotUTF8
}
var meta ProtectedAS
if err := json.Unmarshal(b, &meta); err != nil {
return ProtectedAS{}, err
}
return meta, nil
}

// ProtectedAS is the signature metadata.
type ProtectedAS struct {
Algorithm string `json:"alg"`
Type SignatureTypeCertificate `json:"Type"`
CertificateVersion scrypto.Version `json:"CertificateVersion"`
IA addr.IA `json:"IA"`
Crit CritAS `json:"crit"`
}

// UnmarshalJSON checks that all fields are set.
func (p *ProtectedAS) UnmarshalJSON(b []byte) error {
var alias ProtectedASAlias
dec := json.NewDecoder(bytes.NewReader(b))
dec.DisallowUnknownFields()
if err := dec.Decode(&alias); err != nil {
return err
}
if err := alias.checkAllSet(); err != nil {
return err
}
*p = ProtectedAS{
Algorithm: *alias.Algorithm,
Type: *alias.Type,
CertificateVersion: *alias.CertificateVersion,
IA: *alias.IA,
Crit: *alias.Crit,
}
return nil
}

type ProtectedASAlias struct {
Algorithm *string `json:"alg"`
Type *SignatureTypeCertificate `json:"Type"`
CertificateVersion *scrypto.Version `json:"CertificateVersion"`
IA *addr.IA `json:"IA"`
Crit *CritAS `json:"crit"`
}

func (p *ProtectedASAlias) checkAllSet() error {
switch {
case p.Algorithm == nil:
return ErrAlgorithmNotSet
case p.Type == nil:
return ErrSignatureTypeNotSet
case p.CertificateVersion == nil:
return ErrIssuerCertificateVersionNotSet
case p.IA == nil:
return ErrIANotSet
case p.Crit == nil:
return ErrCritNotSet
}
return nil
}

const SignatureTypeCertificateJSON = "Certificate"

// SignatureTypeCertificate indicates the public key is authenticated by an
// issuer certificate.
type SignatureTypeCertificate struct{}

// UnmarshalText checks the signature type is correct.
func (t *SignatureTypeCertificate) UnmarshalText(b []byte) error {
if string(b) != SignatureTypeCertificateJSON {
return common.NewBasicError(InvalidSignatureType, nil, "input", string(b))
}
return nil
}

func (t SignatureTypeCertificate) MarshalText() ([]byte, error) {
return []byte(SignatureTypeCertificateJSON), nil
}

var (
critASFields = []string{"Type", "CertificateVersion", "IA"}
packedCritFields, _ = json.Marshal(critASFields)
)

// CritAS is the "crit" section for the AS certificate (see:
// https://tools.ietf.org/html/rfc7515#section-4.1.11).
type CritAS struct{}

// UnmarshalJSON checks that all expected elements and no other are in the array.
func (CritAS) UnmarshalJSON(b []byte) error {
return checkCrit(b, critASFields)
}

// MarshalJSON returns a json array with the expected crit elements.
func (CritAS) MarshalJSON() ([]byte, error) {
return packedCritFields, nil
}
Loading

0 comments on commit 0a1b65d

Please sign in to comment.