SECURITY ENGINEERING · CASE STUDY
DEYSİS: Mapping the Boundaries of Trust
A security engineering case study on attendance verification, protocol trust boundaries, and evidence-driven system design.
A black-box study of an attendance workflow, the limits of client-originated evidence, and a deliberately incompatible Rust mock for testing defensive protocol properties.
1. Scope: what an attendance action can prove
DEYSİS is the attendance platform used at Dokuz Eylül University. This investigation examined trust assumptions in a legitimate attendance workflow: what the client appears to contribute, where session and device state enter the lifecycle, and which properties a server would need to establish before recording an attendance decision. It did not set out to automate attendance or to reproduce the production protocol.
The starting point was to keep four ideas separate. Authentication establishes that a session was created through an accepted login flow. Device identity associates an action with a registered cryptographic identity or key. A location value is a claim supplied through the client workflow. Physical presence is a real-world condition. A login, a device key, or a coordinate-like claim may contribute evidence, but none alone proves that a particular person was physically present at the required place and time.
That separation shaped the research questions: which values are client-originated, what a device key actually demonstrates, where authentication stops, and how action challenges should be scoped. The public result is an evidence-weighted case study and a small safe reproduction of defensive ideas—not a finding that DEYSİS accepts invalid attendance.
2. Method: model the lifecycle, then classify the evidence
The private analysis followed legitimate client state transitions. It compared normal flows to distinguish stable values from short-lived state, and mapped the broad lifecycle of session creation, device identity, challenge issuance, signing, and action submission. The public write-up deliberately omits raw requests, production endpoints, headers, identifiers, exact serialization, and operational sequences.
The unit of analysis was a state transition rather than a copied request. For each transition, the question was: what can be seen from the client side, what does that suggest, and what must remain undecided without backend access? Hypotheses were to be checked only with authorized accounts and synthetic inputs. A missing client-visible check was never treated as proof that the server did not validate the same condition.
This matters in black-box security work because an observation, an inference, and a possible consequence have different evidential weight. The study observed a client-originated location claim, a persistent device identity/signing concept, and challenge-like state. It inferred that key possession does not establish location. Replay impact in production remains hypothetical because the server’s exact validation and consumption behavior is unknown.
The Rust implementation is a separate evidence category. Its successful and rejected flows show what the mock does under tests; they say nothing about the production service. Keeping that boundary explicit makes the article useful without converting uncertainty into a vulnerability claim.
3. Mapping the trust boundaries
The client can present values; the server owns the decision. In the observed workflow, a location claim originated in the client-driven action path, and device identity used an asymmetric signing concept. A valid signature can show possession of a corresponding private key and protect the integrity of the fields it covers. It does not by itself show that the key is on an expected physical device, that the account holder is operating it, or that either is at a particular location.
The same logic applies to authentication. A valid session is evidence about account access and session state. Authorization asks whether that identity may perform this action in the current attendance session. Presence verification asks whether the person or device satisfies the real-world attendance policy. These checks can be related, but the data model and audit trail should not collapse them into one boolean.
The diagram is conceptual: user intent and client assertions cross a trust boundary; session state, challenge state, and policy checks are server-side responsibilities. The mock’s `region:demo` value is fictional. No actual production endpoint, field name, or request contract is represented here.
Trust boundary at an attendance decision
Claims cross from the client; verification and policy decisions belong on the server.
4. Threat model: properties, threats, and findings
The documented attacker controls their own client: they may inspect their own client traffic, change their software, replay locally generated state, and submit arbitrary client-controlled values. The model does not assume a compromised server or database, administrator access, or another student’s account. Assets include attendance integrity, identity and session state, device identity, location assertions, challenge state, credentials, and audit records.
Within that boundary, the research asks whether a client claim is being treated as a fact; whether a device key proves only key possession or something more; whether a challenge is fresh, expiring, one-time, and scoped to its intended user, device, session, and action; whether the signed action context is protected against mutation; and whether queued work can be processed without persisting long-lived secrets.
Replay and context substitution are potential threats when a challenge is accepted more than once or in a different action context. Request integrity is a security property: changing signed data should invalidate verification. Session binding and challenge expiry are protocol properties to enforce. None of these statements, by themselves, is a verified production finding. The public tests establish rejection behavior only for the mock.
The practical defensive question is therefore conditional: if a backend relied on a client-reported location as its sole presence evidence, attendance integrity could be weakened. The research does not establish that DEYSİS does so. Server corroboration, attestation, proximity checks, instructor confirmation, anomaly review, and other policy controls remain unknown from this external analysis.
Evidence labels used in the research
Each label states how far a claim is supported by the available material.
- 01Observed
- Directly visible during legitimate client operation.
- 02Inferred
- Strongly suggested by multiple observations, not directly proven.
- 03Hypothetical
- Possible consequence that depends on an unverified condition.
- 04Public mock
- Behavior implemented only in this repository’s fictional provider.
- 05Unknown
- Not externally distinguishable without backend access or further authorized evidence.
- 06Withheld
- Confirmed implementation detail omitted because it would add operational abuse value.
5. Engineering a safer protocol in a deliberately incompatible mock
The public Rust app keeps an `AttendanceProvider` seam and implements only `MockAttendanceProvider`. Its invented protocol has four conceptual operations: authenticate, register a device, request a challenge, and submit an action. It is explicitly not wire-compatible with DEYSİS: names, fields, encoding, and semantics are fictional, and no production adapter is present.
When requested, the mock stores an opaque challenge with server-side `user_id`, `device_id`, `session_id`, `action`, and `expires_at` context. On submission it compares those values with the request. The request’s URL-safe P-256 signature covers the fictional string `challenge | session_id | action | location_claim`; user and device are checked against the stored challenge binding rather than included in that signed string. That distinction is deliberate and visible in the source.
Here is a short excerpt from the real implementation. It constructs the mock’s signed message and verifies it against the registered device identity; it is not production protocol code.
The challenge map is protected by a mutex, and `submit` removes a challenge from the pending map before checking expiry, bindings, and signature. Thus every submission attempt consumes the pending entry: a valid request is recorded as consumed and later replay is rejected; an invalid, expired, or mismatched attempt also cannot retry that challenge. This is the behavior of this in-memory mock, not a claim about a production atomic-consumption design.
The crypto module provides an AES-256-GCM encrypted-key representation. It generates a fresh nonce, decrypts the private key back into a P-256 signing key, and checks that the derived public key matches the stored identity; a unit test checks round-trip signing and tamper rejection. In the demo worker, however, the encrypted value returned by key generation is discarded. That makes this a key-protection example, not a persisted device-key lifecycle.
The mock also demonstrates specific engineering boundaries. `Job` serializes action metadata and identifiers, not credentials or private keys; protected material is obtained or created during worker execution. `run_bounded` holds a Tokio semaphore permit across the provider interaction, and the test uses an instrumented provider to check its concurrency limit. PostgreSQL schema and queue methods show a durable persistence boundary, but the repository explicitly does not implement a persistent dequeue, lease, retry, or crash-recovery loop.
let message = format!(
"{}|{}|{}|{}",
request.challenge, request.session_id, request.action, request.location_claim
);
DeviceCredential::verify(identity, message.as_bytes(), &request.signature)
.map_err(|_| ProviderError::Request("invalid signature".into()))?;Source: `src/mock_provider.rs`. This excerpt is from the intentionally incompatible educational mock.
Mock challenge lifecycle
Lifecycle as implemented in `MockAttendanceProvider::submit`.
- 01Issue
Create opaque ID; store user, device, session, action, and expiry (30-second default).
- 02Bind
Build a signed action over challenge, session, action, and location claim.
- 03Attempt
Submission removes the pending challenge before checks.
- 04Validate
Check expiry and bindings, then verify against the registered key.
- 05Finish
Valid: receipt and consumed marker. Invalid: reject; pending entry is already gone.
Binding and consumption logic: mock_provider.rs ↗ · Key protection: crypto.rs ↗ · Worker example: worker.rs ↗ · Tests: mock_protocol.rs ↗
6. Evidence and limitations
Observed: a legitimate client flow included a session lifecycle, a persistent device identity/signing concept, a client-originated location claim, and challenge-like state. These statements describe client-visible behavior, not server enforcement.
Inferred: a device signature is evidence of key possession and integrity for signed fields, not physical presence. Hypothetical: if a server accepted a client location claim as sufficient presence proof, that could weaken attendance integrity. Public mock: the Rust tests exercise its own expiry, replay, binding, and signature behavior. Unknown: exact production validation, challenge consumption, backend policy, and whether every workflow has been examined. Withheld: operational request details and the production adapter.
External observation has a hard limit: server-side controls that produce no distinguishable client behavior cannot be established from the client alone. Findings can also become stale as the service changes. The mock is not a substitute for backend access, an authorized production assessment, or a complete audit, and it should not be read backward as evidence about DEYSİS.
7. Engineering lessons
Model identity and presence separately. Authentication answers who established a session; device identity answers which key signed; authorization answers whether that session may take an action; presence policy evaluates evidence about where and when. A strong workflow names these properties independently and records their evidence separately.
Bind freshness to meaning. A nonce only helps when it is short-lived, tied to the intended context, and unusable again. The mock makes user, device, session, action, and expiry explicit in server-held challenge state, while signing the session, action, and location claim. Tests make mutations and replay visible as failures.
Treat evidence labels as part of the engineering output. Saying “unknown” is more useful than confidently guessing about an inaccessible backend. A safe reproduction can still be rigorous when it is small, testable, and clearly incompatible with the system being studied. The goal is to make a defensive property reviewable without publishing a deployable integration.
- Keep authentication, authorization, device identity, and physical presence as separate properties.
- Treat client-supplied location as a claim that needs policy and corroboration, not as a fact by itself.
- Scope a challenge to the user, device, session, action, and time window; make replay and mutation behavior testable.
- Keep credentials out of durable job payloads and bound asynchronous work explicitly.
- Publish uncertainty as part of the result. Unknown backend behavior is not evidence of missing controls.
- Use a reviewable mock to demonstrate defensive invariants without shipping a production-compatible integration.
Research references
The public repository contains the full methodology, threat and evidence models, Rust mock, and tests. Its provider protocol is fictional and is not a DEYSİS client.
DEYSİS research repository