package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"os"
jose "github.com/go-jose/go-jose/v4"
)
type signingKey struct {
id string
key *ecdsa.PrivateKey
}
func (k *signingKey) SignatureAlgorithm() jose.SignatureAlgorithm { return jose.ES256 }
func (k *signingKey) Algorithm() jose.SignatureAlgorithm { return jose.ES256 }
func (k *signingKey) ID() string { return k.id }
func (k *signingKey) Use() string { return "sig" }
func (k *signingKey) Key() any { return k.key }
type publicKey struct{ *signingKey }
func (k publicKey) Key() any { return &k.key.PublicKey }
func loadOrCreateSigningKey(path string) (*signingKey, error) {
raw, err := os.ReadFile(path)
var key *ecdsa.PrivateKey
switch {
case err == nil:
block, _ := pem.Decode(raw)
if block == nil {
return nil, fmt.Errorf("%s: not PEM", path)
}
parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("%s: %w", path, err)
}
var ok bool
if key, ok = parsed.(*ecdsa.PrivateKey); !ok || key.Curve != elliptic.P256() {
return nil, fmt.Errorf("%s: want a P-256 ECDSA key", path)
}
case errors.Is(err, os.ErrNotExist):
if key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil {
return nil, err
}
der, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
if err := os.WriteFile(path, pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: der}), 0o600); err != nil {
return nil, err
}
default:
return nil, err
}
pub, err := key.PublicKey.Bytes()
if err != nil {
return nil, err
}
sum := sha256.Sum256(pub)
return &signingKey{id: base64.RawURLEncoding.EncodeToString(sum[:12]), key: key}, nil
}