package webauthn

import (
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
)

// The WebAuthn PRF extension is where cold-start unlock gets its secret
// (Vikunja #14). The authenticator evaluates a keyed PRF over a salt we
// choose and returns 32 bytes that are:
//
//   - stable — the same credential and the same salt always give the same
//     bytes, which is what lets a blob wrapped today be opened tomorrow;
//   - secret — they never leave the authenticator except as this output, so
//     unlike the credential public key they are not sitting in passkeys.json;
//   - bound to user verification — the assertion that produces them required
//     a gesture, so the bytes cannot be harvested silently.
//
// The salt is fixed and public. It is a domain separator, not a secret: it
// makes maven's PRF output different from any other relying party's use of
// the same credential.

// prfSaltInput — the string hashed into the 32-byte evaluation salt. Changing
// it invalidates every wrapped key file in existence, which is why it is a
// constant and not configuration.
const prfSaltInput = "maven-coldstart-unlock-v1"

// PRFSalt returns the fixed 32-byte PRF evaluation salt.
func PRFSalt() []byte {
	sum := sha256.Sum256([]byte(prfSaltInput))
	return sum[:]
}

// ErrNoPRF is returned when a browser reports no PRF result — either the
// authenticator does not implement the extension, or the platform stripped
// it. Cold-start unlock is unavailable for that credential, and the correct
// response is to say so rather than to fall back to something weaker.
var ErrNoPRF = errors.New("webauthn: authenticator returned no PRF result (cold-start unlock unavailable)")

// DecodePRFResult parses the base64url PRF output the browser read out of
// getClientExtensionResults().prf.results.first and checks it is usable as
// wrapping key material.
//
// The browser is not trusted to send something sensible: a short, empty, or
// all-zero result would silently produce a blob that anyone can open, so all
// three are refused here rather than at the crypto layer.
func DecodePRFResult(b64 string) ([]byte, error) {
	if b64 == "" {
		return nil, ErrNoPRF
	}
	secret, err := decodeB64Any(b64)
	if err != nil {
		return nil, fmt.Errorf("webauthn: prf result: %w", err)
	}
	if err := checkSecret(secret); err != nil {
		return nil, err
	}
	return secret, nil
}

// decodeB64Any accepts padded or unpadded base64url — browsers differ, and
// the JS helper on the passkey page strips padding.
func decodeB64Any(s string) ([]byte, error) {
	if b, err := base64.RawURLEncoding.DecodeString(s); err == nil {
		return b, nil
	}
	return base64.URLEncoding.DecodeString(s)
}
