Skip to content

Commit

Permalink
fix: Parse basic constraints only for ca KeyUsageCertSign
Browse files Browse the repository at this point in the history
Don't check basic constrainsts for non-ca certs.

Parse identities from files.
  • Loading branch information
ananthb committed Mar 4, 2024
1 parent 9776486 commit 7e8e939
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 82 deletions.
8 changes: 4 additions & 4 deletions certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
113 changes: 35 additions & 78 deletions cmd/bf/id.go
Original file line number Diff line number Diff line change
@@ -1,98 +1,55 @@
package main

import (
"encoding/pem"
"errors"
"fmt"
"io"
"log/slog"
"os"

"github.com/RealImage/bifrost"
"github.com/google/uuid"
"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
},
}
76 changes: 76 additions & 0 deletions identity.go
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit 7e8e939

Please sign in to comment.