forked from scionproto/scion
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cert: Define signed AS certificate format
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
Showing
8 changed files
with
646 additions
and
63 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
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,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 | ||
} |
Oops, something went wrong.