Skip to content

Commit

Permalink
SPKI: Display certs in a human readable format (#3454)
Browse files Browse the repository at this point in the history
Add support for displaying issuer certificates and certificate chains
in a human readable format.
  • Loading branch information
oncilla authored Dec 6, 2019
1 parent b8cfdd4 commit 2fe4f12
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions go/tools/scion-pki/internal/v2/certs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"cmd.go",
"human.go",
"issuer.go",
"loader.go",
"util.go",
Expand Down
17 changes: 17 additions & 0 deletions go/tools/scion-pki/internal/v2/certs/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ var genIssuerCmd = &cobra.Command{
},
}

var humanCmd = &cobra.Command{
Use: "human",
Short: "Display human readable issuer certificates and certificate chains",
Long: `
'human' parses the provided issuer certificate and certificate chain files
and displays them in a human readable format.
`,
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if err := runHuman(args); err != nil {
return err
}
return nil
},
}

func init() {
Cmd.AddCommand(genIssuerCmd)
Cmd.AddCommand(humanCmd)
}
117 changes: 117 additions & 0 deletions go/tools/scion-pki/internal/v2/certs/human.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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 certs

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"

"github.com/scionproto/scion/go/lib/scrypto/cert/v2"
"github.com/scionproto/scion/go/lib/serrors"
)

func runHuman(files []string) error {
issuers, chains := MatchFiles(files)
for _, file := range issuers {
if err := genHumanIssuer(file); err != nil {
return serrors.WrapStr("unable to display issuer certificate", err, "file", file)
}
}
for _, file := range chains {
if err := genHumanChain(file); err != nil {
return serrors.WrapStr("unable to display certificate chain", err, "file", file)
}
}
return nil
}

func genHumanIssuer(file string) error {
raw, err := ioutil.ReadFile(file)
if err != nil {
return err
}
signed, err := cert.ParseSignedIssuer(raw)
if err != nil {
return serrors.WrapStr("unable to parse signed issuer certificate", err)
}
d, err := decodeIssuer(&signed)
if err != nil {
return serrors.WrapStr("unable to decode issuer certificate", err)
}
if raw, err = json.MarshalIndent(d, "", " "); err != nil {
return serrors.WrapStr("unable to write human readable issuer certificate", err)
}
_, err = fmt.Fprintln(os.Stdout, string(raw))
return err
}

func genHumanChain(file string) error {
raw, err := ioutil.ReadFile(file)
if err != nil {
return err
}
signed, err := cert.ParseChain(raw)
if err != nil {
return serrors.WrapStr("unable to parse signed certificate chain", err)
}
humanReadable := make([]decodedCert, 2)
humanReadable[0], err = decodeIssuer(&signed.Issuer)
if err != nil {
return serrors.WrapStr("unable to decode issuer certificate", err)
}
humanReadable[1], err = decodeAS(&signed.AS)
if err != nil {
return serrors.WrapStr("unable to decode AS certificate", err)
}
if raw, err = json.MarshalIndent(humanReadable, "", " "); err != nil {
return serrors.WrapStr("unable to write human readable certificate chain", err)
}
_, err = fmt.Fprintln(os.Stdout, string(raw))
return err
}

type decodedCert struct {
Payload interface{} `json:"payload"`
Protected interface{} `json:"protected"`
Signature []byte `json:"signature"`
}

func decodeAS(c *cert.SignedAS) (decodedCert, error) {
var err error
var d decodedCert
if d.Payload, err = c.Encoded.Decode(); err != nil {
return decodedCert{}, err
}
if d.Protected, err = c.EncodedProtected.Decode(); err != nil {
return decodedCert{}, err
}
d.Signature = c.Signature
return d, nil
}

func decodeIssuer(c *cert.SignedIssuer) (decodedCert, error) {
var err error
var d decodedCert
if d.Payload, err = c.Encoded.Decode(); err != nil {
return decodedCert{}, err
}
if d.Protected, err = c.EncodedProtected.Decode(); err != nil {
return decodedCert{}, err
}
d.Signature = c.Signature
return d, nil
}
23 changes: 23 additions & 0 deletions go/tools/scion-pki/internal/v2/certs/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package certs
import (
"fmt"
"path/filepath"
"strings"

"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/keyconf"
Expand Down Expand Up @@ -47,3 +48,25 @@ func translateKeys(keys map[cert.KeyType]keyconf.Key) map[cert.KeyType]scrypto.K
}
return m
}

// MatchFiles matches all issuer certificate and certificate chain file names.
func MatchFiles(files []string) ([]string, []string) {
var issuers, chains []string
for _, file := range files {
_, name := filepath.Split(file)
switch {
case match(pkicmn.IssuerNameFmt, name):
issuers = append(issuers, file)
case match(pkicmn.CertNameFmt, name):
chains = append(chains, file)
default:
pkicmn.QuietPrint("Skipping non-certificate file: %s\n", file)
}
}
return issuers, chains
}

func match(fmtString, name string) bool {
matched, _ := filepath.Match(strings.NewReplacer("%d", "*", "%s", "*").Replace(fmtString), name)
return matched
}

0 comments on commit 2fe4f12

Please sign in to comment.