Files
neural-hunt/internal/customer/identity.go
jbergner bcfbef390f
Some checks failed
release-tag / release-image (push) Failing after 2m44s
RC-7
2026-08-11 16:58:07 +02:00

71 lines
2.1 KiB
Go

package customer
import (
"crypto/elliptic"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"math/big"
"neuralhunt/internal/auth"
)
var identityRawURL = base64.RawURLEncoding
type rawPrivateJWK struct {
Kty string `json:"kty"`
Crv string `json:"crv"`
X string `json:"x"`
Y string `json:"y"`
D string `json:"d"`
}
type rawIdentityFile struct {
Version int `json:"version"`
PublicJWK auth.PublicJWK `json:"publicJwk"`
PrivateJWK rawPrivateJWK `json:"privateJwk"`
}
func padP256(b []byte) []byte {
out := make([]byte, 32)
if len(b) > 32 {
b = b[len(b)-32:]
}
copy(out[32-len(b):], b)
return out
}
// ValidateRawIdentity validates the portable raw CLI identity before it is
// written into a managed worker's private Docker volume. It intentionally does
// not accept the encrypted browser-export envelope because the service never
// needs or asks for the customer's export passphrase.
func ValidateRawIdentity(b []byte) error {
var id rawIdentityFile
if err := json.Unmarshal(b, &id); err != nil {
return fmt.Errorf("invalid identity JSON: %w", err)
}
if id.Version != 1 || id.PublicJWK.Kty != "EC" || id.PublicJWK.Crv != "P-256" || id.PrivateJWK.Kty != "EC" || id.PrivateJWK.Crv != "P-256" || id.PrivateJWK.D == "" {
return errors.New("unsupported identity; expected Neural Hunt version 1 P-256 raw identity")
}
db, err := identityRawURL.DecodeString(id.PrivateJWK.D)
if err != nil {
return errors.New("invalid private JWK encoding")
}
d := new(big.Int).SetBytes(db)
curve := elliptic.P256()
if d.Sign() <= 0 || d.Cmp(curve.Params().N) >= 0 {
return errors.New("invalid P-256 private scalar")
}
x, y := curve.ScalarBaseMult(padP256(db))
xs := identityRawURL.EncodeToString(padP256(x.Bytes()))
ys := identityRawURL.EncodeToString(padP256(y.Bytes()))
if xs != id.PublicJWK.X || ys != id.PublicJWK.Y || xs != id.PrivateJWK.X || ys != id.PrivateJWK.Y {
return errors.New("identity public/private key mismatch")
}
if _, err := auth.ClientID(id.PublicJWK); err != nil {
return fmt.Errorf("invalid public identity: %w", err)
}
return nil
}