From 7e8e939705c1663c7e7adccf0e0330cfb4133357 Mon Sep 17 00:00:00 2001 From: Ananth Bhaskararaman Date: Mon, 4 Mar 2024 23:33:27 +0530 Subject: [PATCH] fix: Parse basic constraints only for ca KeyUsageCertSign Don't check basic constrainsts for non-ca certs. Parse identities from files. --- certificate.go | 8 ++-- cmd/bf/id.go | 113 +++++++++++++++---------------------------------- identity.go | 76 +++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 82 deletions(-) create mode 100644 identity.go diff --git a/certificate.go b/certificate.go index 74f87af..c9eebcf 100644 --- a/certificate.go +++ b/certificate.go @@ -41,11 +41,11 @@ func ParseCertificate(asn1Data []byte) (*Certificate, error) { // It checks for the correct signature algorithm, identity namespace, and identity. // On success, it sets the ID, Namespace, and PublicKey fields. func NewCertificate(cert *x509.Certificate) (*Certificate, error) { - if !cert.BasicConstraintsValid { - return nil, fmt.Errorf("%w: basic constraints not valid", ErrCertificateInvalid) - } - if cert.IsCA { + if !cert.BasicConstraintsValid { + return nil, fmt.Errorf("%w: basic constraints not valid", ErrCertificateInvalid) + } + if cert.KeyUsage&x509.KeyUsageCertSign == 0 { return nil, fmt.Errorf("%w: certificate is a CA but cannot sign", ErrCertificateInvalid) } diff --git a/cmd/bf/id.go b/cmd/bf/id.go index bc51f37..4ad8c4f 100644 --- a/cmd/bf/id.go +++ b/cmd/bf/id.go @@ -1,10 +1,9 @@ package main import ( - "encoding/pem" - "errors" "fmt" "io" + "log/slog" "os" "github.com/RealImage/bifrost" @@ -12,87 +11,45 @@ import ( "github.com/urfave/cli/v2" ) -var ( - bfns uuid.UUID - idCmd = &cli.Command{ - Name: "identity", - Aliases: []string{"id"}, - Flags: []cli.Flag{ - nsFlag, - }, - Action: func(cliCtx *cli.Context) error { - ns, id, err := parseUUIDFromFile(bfns, cliCtx.Args().First()) - if err != nil { - return cli.Exit(fmt.Sprintf("Error parsing file: %s", err), 1) - } - - if ns == uuid.Nil { - return cli.Exit("Error: Namespace is required", 1) - } - - if bfns != ns { - fmt.Printf("Namespace: %s\n", ns) - } - - fmt.Println(id) - return nil - }, - } -) - -func parseUUIDFromFile(ns uuid.UUID, filename string) (uuid.UUID, uuid.UUID, error) { - var data []byte - var err error - switch filename { - case "", "-": - data, err = io.ReadAll(os.Stdin) - default: - data, err = os.ReadFile(filename) - } - if err != nil { - return ns, uuid.Nil, err - } - - block, _ := pem.Decode(data) - if block == nil { - return ns, uuid.Nil, errors.New("no PEM data found") - } - - // Parse the key or certificate. - switch block.Type { - case "PRIVATE KEY": - privkey, err := bifrost.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - return ns, uuid.Nil, err +var idCmd = &cli.Command{ + Name: "identity", + Aliases: []string{"id"}, + Flags: []cli.Flag{ + nsFlag, + }, + Action: func(cliCtx *cli.Context) error { + filename := cliCtx.Args().First() + + var data []byte + var err error + switch filename { + case "", "-": + data, err = io.ReadAll(os.Stdin) + default: + data, err = os.ReadFile(filename) } - return ns, bifrost.UUID(ns, privkey.PublicKey()), nil - case "EC PRIVATE KEY": - privkey, err := bifrost.ParseECPrivateKey(block.Bytes) if err != nil { - return ns, uuid.Nil, err + return err } - return ns, bifrost.UUID(ns, privkey.PublicKey()), nil - case "PUBLIC KEY": - pubkey, err := bifrost.ParsePKIXPublicKey(block.Bytes) + + id, err := bifrost.ParseIdentity(data) if err != nil { - return ns, uuid.Nil, err + return cli.Exit(fmt.Sprintf("Error parsing file: %s", err), 1) } - return ns, bifrost.UUID(ns, pubkey), nil - case "CERTIFICATE": - cert, err := bifrost.ParseCertificate(block.Bytes) - if err != nil { - return ns, uuid.Nil, err + + if id.Namespace == uuid.Nil && namespace == uuid.Nil { + return cli.Exit("Error: Namespace is required", 1) } - ns = cert.Namespace - return ns, cert.PublicKey.UUID(ns), nil - case "CERTIFICATE REQUEST": - csr, err := bifrost.ParseCertificateRequest(block.Bytes) - if err != nil { - return ns, uuid.Nil, err + if id.Namespace != uuid.Nil && namespace != uuid.Nil && id.Namespace != namespace { + return cli.Exit("Error: Namespace mismatch", 1) } - ns = csr.Namespace - return ns, csr.PublicKey.UUID(ns), nil - default: - return ns, uuid.Nil, fmt.Errorf("unsupported PEM block type: %s", block.Type) - } + if namespace != uuid.Nil { + id.Namespace = namespace + } + + slog.Debug("using", "namespace", id.Namespace) + fmt.Println(id.UUID()) + + return nil + }, } diff --git a/identity.go b/identity.go new file mode 100644 index 0000000..d2d477a --- /dev/null +++ b/identity.go @@ -0,0 +1,76 @@ +package bifrost + +import ( + "encoding/pem" + "errors" + "fmt" + + "github.com/google/uuid" +) + +// Identity represents a unique identity in the system. +type Identity struct { + Namespace uuid.UUID + PublicKey *PublicKey +} + +func (i Identity) UUID() uuid.UUID { + return i.PublicKey.UUID(i.Namespace) +} + +func ParseIdentity(data []byte) (*Identity, error) { + block, _ := pem.Decode(data) + if block == nil { + return nil, errors.New("no PEM data found") + } + + // Parse the key or certificate. + switch block.Type { + case "PRIVATE KEY": + privkey, err := ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + return &Identity{ + PublicKey: privkey.PublicKey(), + }, nil + case "EC PRIVATE KEY": + privkey, err := ParseECPrivateKey(block.Bytes) + if err != nil { + return nil, err + } + return &Identity{ + PublicKey: privkey.PublicKey(), + }, nil + case "PUBLIC KEY": + pubkey, err := ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + return &Identity{ + PublicKey: pubkey, + }, nil + case "CERTIFICATE": + cert, err := ParseCertificate(block.Bytes) + if err != nil { + return nil, err + } + ns := cert.Namespace + return &Identity{ + Namespace: ns, + PublicKey: cert.PublicKey, + }, nil + case "CERTIFICATE REQUEST": + csr, err := ParseCertificateRequest(block.Bytes) + if err != nil { + return nil, err + } + ns := csr.Namespace + return &Identity{ + Namespace: ns, + PublicKey: csr.PublicKey, + }, nil + default: + return nil, fmt.Errorf("unsupported PEM block type: %s", block.Type) + } +}