Documentation
¶
Overview ¶
Package boundcheck is a generic, application-agnostic homomorphic bound check (ARES-BC, generalizing ARES v2.6 SC-5). Between input submission and scoring, it computes — per party uniformly — a single safe-to-decrypt squared magnitude ‖x - c‖² (public center c) over the party's encrypted input, threshold-decrypts it across the participant quorum, and aborts the session via an application-supplied ViolationHandler when the value falls outside the committed [lo, hi].
NormCircuit (c=0, the SC-5 embedding norm check) and DistanceBoundCircuit (geo-radius, multi-dimensional resource budgets) are the built-in squared-magnitude circuits. Inputs must be multi-dimensional: the phase refuses dim < 2, because for a scalar the squared magnitude reveals the value up to a sign (scalar range needs a homomorphic-comparison circuit). Circuit depth is determined offline with pkg/ares/crypto/fhecalib.
The phase computes each enc_check and Provides them under CtxBoundCheckCiphers; the consuming application is responsible for unicasting them to participants, who reply with bound_check.partial (their partial decrypt of each check ciphertext).
Index ¶
- Constants
- type Bound
- type BoundCircuit
- type DistanceBoundCircuit
- func (c DistanceBoundCircuit) Bound() Bound
- func (c DistanceBoundCircuit) Dim() int
- func (c DistanceBoundCircuit) Eval(h fhecalib.ContextHandle, enc [][]byte) ([]byte, error)
- func (c DistanceBoundCircuit) Expected(in [][]float64) []float64
- func (c DistanceBoundCircuit) Inputs() [][]float64
- func (DistanceBoundCircuit) Name() string
- type NormCircuit
- type Params
- type Phase
- func (p *Phase) CheckComplete(ctx *phase.SessionContext) bool
- func (Phase) ConsumedMessageTypes() []string
- func (p *Phase) Enter(ctx *phase.SessionContext) error
- func (p *Phase) EntryState() phase.SessionState
- func (p *Phase) Exit(ctx *phase.SessionContext) error
- func (p *Phase) ExitState() phase.SessionState
- func (Phase) InternalStates() []phase.SessionState
- func (Phase) Lifetime() phase.Lifetime
- func (Phase) Name() string
- func (p *Phase) OnMessage(ctx *phase.SessionContext, _ string, from string, payload []byte) error
- func (Phase) Provides() phase.ContextSchema
- func (Phase) Requires() phase.ContextSchema
- func (Phase) RunsAt() phase.RunsAt
- type Severity
- type ViolationHandler
Constants ¶
const ( CtxEncryptedInputs = "boundcheck.encrypted_inputs" // map[string][]byte: party -> lineage-committed ciphertext CtxInputDim = "boundcheck.input_dim" // int: slot count of each input vector CtxEvalKeyBundle = "boundcheck.eval_key_bundle" // serialized cgo.EvalKeyFinal CtxJointPublicKey = "boundcheck.joint_public_key" // []byte: pk_joint CtxBoundCheckCiphers = "boundcheck.check_ciphertexts" // map[string][]byte: party -> enc_check (phase Provides; app unicasts) CtxBoundCheckCommitments = "boundcheck.check_commitments" // map[string][]byte: party -> H(enc_check ‖ H(enc_x) ‖ session_id) // CtxBoundCheckHandle / CtxBoundCheckFuse let a shared-runner session-service // supply a PER-SESSION fhecalib.ContextHandle and fuse function via the // SessionContext (set by the app's trigger at BeginSession), for apps that // cannot bake a single handle into a process-shared Phase. They are honored // only when the construction-time handle/fuse (NewPhaseWithCrypto) are nil. CtxBoundCheckHandle = "boundcheck.handle" // fhecalib.ContextHandle (server-set) CtxBoundCheckFuse = "boundcheck.fuse" // func(partials [][]byte, nSlots int) ([]float64, error) (server-set) )
Context keys: the app's input-submit phase Provides these; the bound phase Requires them. (Use defaults.CtxParticipants for the participant set — NOT a boundcheck-local copy.)
const MsgBoundPartial = "bound_check.partial"
MsgBoundPartial is the message type each party replies with: its partial decrypt of the broadcast check value.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Bound ¶
type Bound struct{ Lo, Hi float64 }
Bound is the closed interval the decrypted check value must lie in.
type BoundCircuit ¶
type BoundCircuit interface {
fhecalib.CircuitUnderTest
Bound() Bound
// Dim returns the expected input-vector dimension (slot count) for this
// circuit. Enter rejects sessions where CtxInputDim != Dim() to prevent
// silently summing the wrong number of slots and producing a wrong norm.
Dim() int
}
BoundCircuit is the homomorphic map producing the single safe-to-decrypt check value for one encrypted input, plus the bound the value must satisfy. It is also a fhecalib.CircuitUnderTest, so the exact circuit run at phase runtime is the one calibrated for depth.
type DistanceBoundCircuit ¶
DistanceBoundCircuit checks ‖x - Center‖² ∈ [Lo, Hi] (public Center). Covers geographic-radius and multi-dimensional resource budgets.
func (DistanceBoundCircuit) Bound ¶
func (c DistanceBoundCircuit) Bound() Bound
func (DistanceBoundCircuit) Dim ¶
func (c DistanceBoundCircuit) Dim() int
Dim returns the expected input-vector dimension for this circuit.
func (DistanceBoundCircuit) Eval ¶
func (c DistanceBoundCircuit) Eval(h fhecalib.ContextHandle, enc [][]byte) ([]byte, error)
func (DistanceBoundCircuit) Expected ¶
func (c DistanceBoundCircuit) Expected(in [][]float64) []float64
func (DistanceBoundCircuit) Inputs ¶
func (c DistanceBoundCircuit) Inputs() [][]float64
Inputs returns the center itself (squared distance 0 — a representative in-bound point for calibration).
func (DistanceBoundCircuit) Name ¶
func (DistanceBoundCircuit) Name() string
type NormCircuit ¶
NormCircuit is the SC-5 embedding norm check: center 0, check value ‖x‖², bound [1-Eps, 1+Eps]. NDim is the embedding dimension (the number of CKKS slots summed); it must match the input vector length at runtime (CtxInputDim).
func (NormCircuit) Bound ¶
func (c NormCircuit) Bound() Bound
func (NormCircuit) Dim ¶
func (c NormCircuit) Dim() int
Dim returns the expected input-vector dimension for this circuit.
func (NormCircuit) Eval ¶
func (c NormCircuit) Eval(h fhecalib.ContextHandle, enc [][]byte) ([]byte, error)
func (NormCircuit) Expected ¶
func (NormCircuit) Expected(in [][]float64) []float64
func (NormCircuit) Inputs ¶
func (c NormCircuit) Inputs() [][]float64
Inputs returns a representative unit-norm vector of length NDim (for depth calibration).
func (NormCircuit) Name ¶
func (NormCircuit) Name() string
type Params ¶
type Params struct {
EpsNorm float64 // noise floor; nu <= EpsNorm is treated as in-bound
NuHard float64 // hard-violation threshold on the distance outside the bound
}
Params are ARES-core's detection bands (distinct from any app penalty curve).
func DefaultParams ¶
func DefaultParams() Params
DefaultParams mirror ARES v2.6 SC-5 reference values.
type Phase ¶
type Phase struct {
// contains filtered or unexported fields
}
Phase is the ARES v2.6 Phase-1c bound-check round. One instance is shared across every session that uses it; per-session state lives in SessionContext.
Construct via NewPhase (stub mode, no FHE at runtime) or NewPhaseWithCrypto (real mode, FHE-backed Enter and Exit).
func NewPhase ¶
func NewPhase( circuit BoundCircuit, handler ViolationHandler, params Params, entryState phase.SessionState, exitState phase.SessionState, ) *Phase
NewPhase returns a Phase in stub mode (Enter/Exit no-op the FHE path). Suitable for compose-time structural tests and pipelines where crypto is injected later via NewPhaseWithCrypto.
entryState is the session state that triggers this phase (commonly defaults.StateScoring). exitState is the state to transition to on completion.
func NewPhaseWithCrypto ¶
func NewPhaseWithCrypto( circuit BoundCircuit, handler ViolationHandler, params Params, entryState phase.SessionState, exitState phase.SessionState, handle fhecalib.ContextHandle, fuse func(partials [][]byte, nSlots int) ([]float64, error), ) *Phase
NewPhaseWithCrypto returns a Phase in real mode. handle is a provisioned fhecalib.ContextHandle (e.g. from fhecalib.NewContextHandle); fuse is the partial-fusion function (e.g. a closure over cgo.FuseCKKSPartialsForContract bound to the session's ContractParams).
Neither handle nor fuse is called on the happy path when no violations occur and Enter reaches the no-compute branch — they are invoked only when CtxEncryptedInputs is non-empty (Enter) and when partials have been accumulated (Exit).
func (*Phase) CheckComplete ¶
func (p *Phase) CheckComplete(ctx *phase.SessionContext) bool
CheckComplete returns true once every participant has submitted a MsgBoundPartial reply (N-of-N quorum).
func (Phase) ConsumedMessageTypes ¶
ConsumedMessageTypes lists the partial-decrypt reply type.
func (*Phase) Enter ¶
func (p *Phase) Enter(ctx *phase.SessionContext) error
Enter enforces invariant #5 (dim refusal), then computes enc_check_i for every party in CtxEncryptedInputs and stores the results in CtxBoundCheckCiphers. In stub mode (handle == nil), the FHE compute is skipped and CtxBoundCheckCiphers is not set; structural tests that do not need real ciphertexts can use stub mode.
The consuming app must read CtxBoundCheckCiphers immediately after Enter and unicast each enc_check_i to the corresponding participant.
func (*Phase) EntryState ¶
func (p *Phase) EntryState() phase.SessionState
func (*Phase) Exit ¶
func (p *Phase) Exit(ctx *phase.SessionContext) error
Exit assembles the N-party quorum of partial decrypts for each check ciphertext, fuses them, classifies the result against the circuit's Bound, and invokes the ViolationHandler for violating parties before aborting with ErrAppAttributable. In stub mode (fuse == nil), Exit returns nil unconditionally.
Each sender's MsgBoundPartial payload is a JSON-serialised map[string][]byte keyed by checkedParty → that sender's partial of that check ciphertext. Exit collects, for each checked party i, all N senders' partials for i into a [][]byte slice of length N and calls fuse(partialsForI, 1). This is the correct threshold-decrypt quorum: all N parties must contribute a partial per ciphertext for the fusion to produce a valid plaintext.
On violation:
- handler.OnViolation is called for every violating party (sequentially).
- A random jitter sleep is introduced to prevent timing oracles.
- A generic fmt.Errorf("%w: bound violation", ErrAppAttributable) is returned — no party identifiers or fused values are included to avoid leaking information to the transport layer.
On all-OK: returns nil; the runner advances the session to ExitState.
func (*Phase) ExitState ¶
func (p *Phase) ExitState() phase.SessionState
func (Phase) InternalStates ¶
func (Phase) InternalStates() []phase.SessionState
InternalStates returns nil — the bound-check round has no sub-states.
func (Phase) Lifetime ¶
Lifetime is per-session: check ciphertexts and partial decrypts are not reusable across sessions.
func (*Phase) OnMessage ¶
OnMessage accumulates each party's partial-decrypt map into the internal bucket. Each MsgBoundPartial payload is a JSON-serialised map[string][]byte keyed by checkedParty → that sender's partial of that check ciphertext. Multiple submissions from the same sender overwrite (only the most recent map is kept per sender, which is the correct N-of-N pattern).
func (Phase) Provides ¶
func (Phase) Provides() phase.ContextSchema
Provides declares the SessionContext keys this phase writes. The consuming application MUST read CtxBoundCheckCiphers after Enter returns and unicast enc_check_i to each participant before their MsgBoundPartial reply is expected.
CtxBoundCheckCommitments is also written by Enter (in real mode) alongside CtxBoundCheckCiphers. Each commitment binds the server-computed enc_check_i to the lineage-committed enc_x_i via
H(enc_check_i ‖ H(enc_x_i) ‖ session_id)
Client-side verification of the commitment is the consuming app's responsibility: the framework produces and stores the binding but does not enforce it on the protocol path.
func (Phase) Requires ¶
func (Phase) Requires() phase.ContextSchema
Requires declares the SessionContext keys this phase reads from the preceding submission phase. The consuming app's submission phase must Provide all of these.
Note: CtxSecretShares is deliberately NOT required here. The server does not hold individual secret shares; each party's partial-decrypt blob already encodes its contribution and FuseCKKSPartialsForContract operates directly on the wire blobs.
type ViolationHandler ¶
type ViolationHandler interface {
// OnViolation is called synchronously before the jitter sleep and the
// session abort. The returned error is advisory (for application-layer
// logging or side effects); the framework discards it and aborts the
// session regardless of what OnViolation returns.
OnViolation(ctx *phase.SessionContext, party string, nu float64, sev Severity) error
}
ViolationHandler is the application boundary: invoked once per violating party before the session aborts. nu is the distance outside the bound; the app maps it to a domain penalty.