Skip to content

Commit

Permalink
SPKI: Generate private keys (#3218)
Browse files Browse the repository at this point in the history
Adds:
 - support to generate private keys form keys.toml

runGenKeys is not replaced currently, because it is in use by the
trust/v2 tests. It will be replaced as soon as TRC and certificate
signing with the new key format is available.
  • Loading branch information
oncilla authored Oct 7, 2019
1 parent 2eab837 commit 4588351
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 10 deletions.
2 changes: 1 addition & 1 deletion go/lib/keyconf/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ type Key struct {
}

// KeyFromPEM parses the PEM block.
func KeyFromPEM(block pem.Block) (Key, error) {
func KeyFromPEM(block *pem.Block) (Key, error) {
k := Key{}
if err := k.Type.UnmarshalText([]byte(block.Type)); err != nil {
return Key{}, serrors.WrapStr("unable to parse key type", err)
Expand Down
2 changes: 1 addition & 1 deletion go/lib/keyconf/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func TestKeyFromPEM(t *testing.T) {
t.Run(name, func(t *testing.T) {
block := pemBlock(t)
test.Modify(&block)
k, err := keyconf.KeyFromPEM(block)
k, err := keyconf.KeyFromPEM(&block)
test.ErrAssertion(t, err)
if err != nil {
return
Expand Down
14 changes: 14 additions & 0 deletions go/tools/scion-pki/internal/pkicmn/pkicmn.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ var (
Quiet bool
)

// Dirs holds the directory configuration.
type Dirs struct {
Root string
Out string
}

// GetDirs returns the directory configuration.
func GetDirs() Dirs {
return Dirs{
Root: RootDir,
Out: OutDir,
}
}

// ParseSelector parses the given selector. The returned strings are in file format.
func ParseSelector(selector string) (string, string, error) {
toks := strings.Split(selector, "-")
Expand Down
14 changes: 11 additions & 3 deletions go/tools/scion-pki/internal/v2/conf/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,26 @@ package conf
import (
"encoding"
"io"
"path/filepath"
"strconv"

"github.com/BurntSushi/toml"

"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/scrypto"
"github.com/scionproto/scion/go/lib/scrypto/cert/v2"
"github.com/scionproto/scion/go/lib/scrypto/trc/v2"
"github.com/scionproto/scion/go/lib/serrors"
"github.com/scionproto/scion/go/tools/scion-pki/internal/pkicmn"
)

// KeysFileName is the file name of the key configuration.
const KeysFileName = "keys.toml"
// keysFileName is the file name of the key configuration.
const keysFileName = "keys.toml"

// KeysFile returns the file where the keys config is written to.
func KeysFile(dir string, ia addr.IA) string {
return filepath.Join(pkicmn.GetAsPath(dir, ia), keysFileName)
}

// Keys holds the key configuration.
type Keys struct {
Expand Down Expand Up @@ -95,7 +103,7 @@ func (k Keys) validateKeyMetas(metas map[scrypto.KeyVersion]KeyMeta) error {
return nil
}

// KeyMeta defines the
// KeyMeta defines the key metadata.
type KeyMeta struct {
Algorithm string `toml:"algorithm"`
Validity Validity `toml:"validity"`
Expand Down
15 changes: 15 additions & 0 deletions go/tools/scion-pki/internal/v2/conf/validity.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
package conf

import (
"time"

"github.com/scionproto/scion/go/lib/scrypto"
"github.com/scionproto/scion/go/lib/serrors"
"github.com/scionproto/scion/go/lib/util"
)
Expand All @@ -32,3 +35,15 @@ func (v Validity) Validate() error {
}
return nil
}

// Eval returns the validity period. The not before parameter is only used if
// the struct's not before field value is zero.
func (v Validity) Eval(notBefore time.Time) scrypto.Validity {
if v.NotBefore != 0 {
notBefore = util.SecsToTime(v.NotBefore)
}
return scrypto.Validity{
NotBefore: util.UnixTime{Time: notBefore},
NotAfter: util.UnixTime{Time: notBefore.Add(v.Validity.Duration)},
}
}
25 changes: 24 additions & 1 deletion go/tools/scion-pki/internal/v2/keys/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = [
"cmd.go",
"gen.go",
"priv.go",
"util.go",
],
importpath = "github.com/scionproto/scion/go/tools/scion-pki/internal/v2/keys",
visibility = ["//go/tools/scion-pki:__subpackages__"],
deps = [
"//go/lib/addr:go_default_library",
"//go/lib/common:go_default_library",
"//go/lib/keyconf:go_default_library",
"//go/lib/scrypto:go_default_library",
"//go/lib/scrypto/cert/v2:go_default_library",
"//go/lib/scrypto/trc/v2:go_default_library",
"//go/lib/serrors:go_default_library",
"//go/tools/scion-pki/internal/pkicmn:go_default_library",
"//go/tools/scion-pki/internal/v2/conf:go_default_library",
"@com_github_spf13_cobra//:go_default_library",
"@org_golang_x_crypto//ed25519:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["priv_test.go"],
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//go/lib/addr:go_default_library",
"//go/lib/keyconf:go_default_library",
"//go/lib/scrypto:go_default_library",
"//go/lib/xtest:go_default_library",
"//go/tools/scion-pki/internal/pkicmn:go_default_library",
"//go/tools/scion-pki/internal/v2/conf:go_default_library",
"//go/tools/scion-pki/internal/v2/conf/testdata:go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
],
)
209 changes: 209 additions & 0 deletions go/tools/scion-pki/internal/v2/keys/priv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright 2018 ETH Zurich
// Copyright 2019 ETH Zurich, 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 keys

import (
"encoding/pem"
"os"
"path/filepath"
"time"

"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/keyconf"
"github.com/scionproto/scion/go/lib/scrypto"
"github.com/scionproto/scion/go/lib/scrypto/cert/v2"
"github.com/scionproto/scion/go/lib/scrypto/trc/v2"
"github.com/scionproto/scion/go/lib/serrors"
"github.com/scionproto/scion/go/tools/scion-pki/internal/pkicmn"
"github.com/scionproto/scion/go/tools/scion-pki/internal/v2/conf"
)

type privGen struct {
Dirs pkicmn.Dirs
}

func (g privGen) Run(asMap map[addr.ISD][]addr.IA) error {
cfgs, err := g.loadConfigs(asMap)
if err != nil {
return err
}
keys, err := g.generateAllKeys(cfgs)
if err != nil {
return err
}
if err := g.createDirs(keys); err != nil {
return err
}
if err := g.writeKeys(keys); err != nil {
return err
}
return nil
}

func (g privGen) loadConfigs(asMap map[addr.ISD][]addr.IA) (map[addr.IA]conf.Keys, error) {
cfgs := make(map[addr.IA]conf.Keys)
for _, ases := range asMap {
for _, ia := range ases {
file := conf.KeysFile(g.Dirs.Root, ia)
keys, err := conf.LoadKeys(file)
if err != nil {
return nil, serrors.WrapStr("unable to load keys config file", err, "file", file)
}
cfgs[ia] = keys
}
}
return cfgs, nil
}

func (g privGen) generateAllKeys(cfgs map[addr.IA]conf.Keys) (map[addr.IA][]keyconf.Key, error) {
keys := make(map[addr.IA][]keyconf.Key)
for ia, cfg := range cfgs {
k, err := g.generateKeys(ia, cfg)
if err != nil {
return nil, serrors.WrapStr("unable to generate keys for AS", err, "ia", ia)
}
keys[ia] = k
}
return keys, nil
}

func (g privGen) generateKeys(ia addr.IA, cfg conf.Keys) ([]keyconf.Key, error) {
var keys []keyconf.Key
for keyType, metas := range cfg.Primary {
for version, meta := range metas {
usage, err := usageFromTRCKeyType(keyType)
if err != nil {
return nil, serrors.WrapStr("error determining key usage", err,
"type", keyType, "version", version)
}
key, err := g.generateKey(ia, version, usage, meta)
if err != nil {
return nil, serrors.WrapStr("error generating key", err, "type", keyType,
"version", version)
}
keys = append(keys, key)
}
}
for keyType, metas := range cfg.Issuer {
for version, meta := range metas {
usage, err := usageFromIssuerKeyType(keyType)
if err != nil {
return nil, serrors.WrapStr("error determining key usage", err,
"type", keyType, "version", version)
}
key, err := g.generateKey(ia, version, usage, meta)
if err != nil {
return nil, serrors.WrapStr("error generating key", err, "type", keyType,
"version", version)
}
keys = append(keys, key)
}
}
for keyType, metas := range cfg.AS {
for version, meta := range metas {
usage, err := usageFromASKeyType(keyType)
if err != nil {
return nil, serrors.WrapStr("error determining key usage", err,
"type", keyType, "version", version)
}
key, err := g.generateKey(ia, version, usage, meta)
if err != nil {
return nil, serrors.WrapStr("error generating key", err, "type", keyType,
"version", version)
}
keys = append(keys, key)
}
}
return keys, nil
}

func (g privGen) generateKey(ia addr.IA, version scrypto.KeyVersion,
usage keyconf.Usage, meta conf.KeyMeta) (keyconf.Key, error) {

raw, err := genKey(meta.Algorithm)
if err != nil {
return keyconf.Key{}, err
}
key := keyconf.Key{
Type: keyconf.PrivateKey,
Usage: usage,
Algorithm: meta.Algorithm,
Validity: meta.Validity.Eval(time.Now()),
Version: version,
IA: ia,
Bytes: raw,
}
return key, nil
}

func (g privGen) createDirs(keys map[addr.IA][]keyconf.Key) error {
for ia := range keys {
if err := os.MkdirAll(PrivateDir(g.Dirs.Out, ia), 0700); err != nil {
return serrors.WrapStr("unable to make private keys directory", err, "ia", ia)
}
}
return nil
}

func (g privGen) writeKeys(keys map[addr.IA][]keyconf.Key) error {
for ia, list := range keys {
for _, key := range list {
b := key.PEM()
file := filepath.Join(PrivateDir(g.Dirs.Out, ia), key.File())
if err := pkicmn.WriteToFile(pem.EncodeToMemory(&b), file, 0600); err != nil {
return serrors.WrapStr("error writing private key file", err, "file", file)
}
}
}
return nil
}

func usageFromTRCKeyType(keyType trc.KeyType) (keyconf.Usage, error) {
switch keyType {
case trc.IssuingKey:
return keyconf.TRCIssuingKey, nil
case trc.OnlineKey:
return keyconf.TRCVotingOnlineKey, nil
case trc.OfflineKey:
return keyconf.TRCVotingOfflineKey, nil
default:
return "", serrors.New("unsupported key type", "type", keyType)
}
}

func usageFromASKeyType(keyType cert.KeyType) (keyconf.Usage, error) {
switch keyType {
case cert.SigningKey:
return keyconf.ASSigningKey, nil
case cert.EncryptionKey:
return keyconf.ASDecryptionKey, nil
case cert.RevocationKey:
return keyconf.ASRevocationKey, nil
default:
return "", serrors.New("unsupported key type", "type", keyType)
}
}

func usageFromIssuerKeyType(keyType cert.KeyType) (keyconf.Usage, error) {
switch keyType {
case cert.IssuingKey:
return keyconf.IssCertSigningKey, nil
case cert.RevocationKey:
return keyconf.IssRevocationKey, nil
default:
return "", serrors.New("unsupported key type", "type", keyType)
}
}
Loading

0 comments on commit 4588351

Please sign in to comment.