speckl/out_fresh/OAuth2AuthorizationCode.ts
Scott Scoble 3ca5a8d21a fix(z3): preserve implies args, multi-level field access, len(), nil, distinct operator
Z3 expression translator fixes:
- implies(x, y) now preserves both arguments (was dropping both → (=> true true))
- Multi-level field access m.price.amount → m_price_amount (was m_price.(>= amount 0))
- len() now handled same as length() (was left as bare len())
- nil/null translated to speckl_nil sentinel constant
- != now properly converts to (distinct X Y) via convertToPrefix (was bare 'distinct')
- Added 'distinct' as binary operator in convertToPrefix
- speckl_nil declared as Int constant in output

All 8 product spec constraints now produce valid SMT-LIB2.
57/57 tests pass.
2026-06-29 17:31:31 -07:00

192 lines
10 KiB
TypeScript

// Auto-generated by speckl-compile from OAuth2AuthorizationCode
// DO NOT EDIT MANUALLY — regenerate from .speckdl source
export const OAuth2AuthorizationCodeOAuth2AuthorizationCodeState = {
Open: 'open',
Closed: 'closed',
} as const;
export type OAuth2AuthorizationCodeOAuth2AuthorizationCodeState = typeof OAuth2AuthorizationCodeOAuth2AuthorizationCodeState[keyof typeof OAuth2AuthorizationCodeOAuth2AuthorizationCodeState];
// Simple hash utility for SpeckDL hash() function
// In production, replace with a real crypto hash
function simpleHash(s: string, method?: string): string {
let h = 0;
for (let i = 0; i < s.length; i++) {
h = ((h << 5) - h + s.charCodeAt(i)) | 0;
}
return 'h_' + Math.abs(h).toString(16);
}
// Runtime type placeholders (opaque record types)
type AccessToken = any;
type AuthorizationCode = any;
type Bool }) = any;
type ClientId = any;
type ClientSecret = any;
type CodeChallenge = any;
type CodeVerifier = any;
type Map(AccessToken, { client_id: ClientId = any;
type Map(AuthorizationCode, { client_id: ClientId = any;
type Map(RefreshToken, { client_id: ClientId = any;
type Nat }) = any;
type RedirectUri = any;
type RefreshRequest = any;
type RefreshToken }) = any;
type Scope = any;
type SessionId = any;
type State = any;
type TokenRequest = any;
type UserId = any;
export class OAuth2AuthorizationCodeMachine {
clients!: Map<ClientId, { redirect_uris: Set<RedirectUri>; client_secret: ClientSecret }>;
authorizationCodes!: Map(AuthorizationCode, { client_id: ClientId;
redirect_uri!: RedirectUri;
state!: State;
code_challenge!: CodeChallenge;
code_challenge_method!: string;
scope!: Scope;
user_id!: UserId;
session_id!: SessionId;
created_at!: number;
used!: Bool });
accessTokens!: Map(AccessToken, { client_id: ClientId;
user_id!: UserId;
scope!: Scope;
expires_at!: number;
session_id!: SessionId;
refresh_token!: RefreshToken });
refreshTokens!: Map(RefreshToken, { client_id: ClientId;
user_id!: UserId;
scope!: Scope;
access_token!: AccessToken;
created_at!: Nat });
sessions!: Map<SessionId, { user_id: UserId }>;
stateNonces!: Map<State, { client_id: ClientId; redirect_uri: RedirectUri }>;
pkceChallenges!: Map<CodeChallenge, { code_verifier_hash: string; method: string }>;
currentTime!: number;
decisionLog: any[] = [];
constructor() {
// No init block defined
}
Authorize(client: ClientId, redirectUri: RedirectUri, scope: Scope, userId: UserId, sessionId: SessionId) {
const xxx: any = {} as any; // FIXME: parser dropped let/forall
const client_id: any = {} as any; // FIXME: parser dropped let/forall
const code_verifier_hash: any = {} as any; // FIXME: parser dropped let/forall
const __STR_LIT_0__: any = {} as any; // FIXME: parser dropped let/forall
const method: any = {} as any; // FIXME: parser dropped let/forall
const __STR_LIT_1__: any = {} as any; // FIXME: parser dropped let/forall
if (!(this.clients.has(client))) throw new Error('Guard failed: this.clients.has(client)');
if (!(this.clients.get(client)!.redirect_uris.has(redirectUri))) throw new Error('Guard failed: this.clients.get(client)!.redirect_uris.has(redirectUri)');
if (!(this.sessions.has(sessionId))) throw new Error('Guard failed: this.sessions.has(sessionId)');
if (!(this.sessions.get(sessionId)!.user_id === userId)) throw new Error('Guard failed: this.sessions.get(sessionId)!.user_id === userId');
const state = "state_" + simpleHash(client + redirectUri + this.currentTime);
const codeVerifier = "verifier_" + simpleHash(client + userId + this.currentTime);
const codeChallenge = simpleHash(codeVerifier, "S256");
this.stateNonces.set(state, { client_id: client, redirect_uri: redirectUri });
this.pkceChallenges.set(codeChallenge, { code_verifier_hash: simpleHash(codeVerifier, __STR_LIT_0__), method: __STR_LIT_1__ });
const authCode = "code_" + simpleHash(client + userId + this.currentTime);
this.authorizationCodes.set(authCode, { client_id: client, redirect_uri: redirectUri, state: state, code_challenge: codeChallenge, code_challenge_method: __STR_LIT_0__, scope: scope, user_id: userId, session_id: sessionId, created_at: this.currentTime, used: false });
}
ApproveAuthorization(code: AuthorizationCode, userId: UserId) {
const xxx: any = {} as any; // FIXME: parser dropped let/forall
if (!(this.authorizationCodes.has(code))) throw new Error('Guard failed: this.authorizationCodes.has(code)');
const c = this.authorizationCodes.get(code)!;
if (!(c.user_id === userId)) throw new Error('Guard failed: c.user_id === userId');
if (!(c.used === false)) throw new Error('Guard failed: c.used === false');
}
ExchangeCode(req: TokenRequest) {
const xxx: any = {} as any; // FIXME: parser dropped let/forall
const client_id: any = {} as any; // FIXME: parser dropped let/forall
if (!(this.clients.has(req.client_id))) throw new Error('Guard failed: this.clients.has(req.client_id)');
if (!(this.authorizationCodes.has(req.code))) throw new Error('Guard failed: this.authorizationCodes.has(req.code)');
const c = this.authorizationCodes.get(req.code)!;
if (!(c.used === false)) throw new Error('Guard failed: c.used === false');
if (!(c.client_id === req.client_id)) throw new Error('Guard failed: c.client_id === req.client_id');
if (!(c.redirect_uri === req.redirect_uri)) throw new Error('Guard failed: c.redirect_uri === req.redirect_uri');
const expectedChallenge = simpleHash(req.code_verifier, c.code_challenge_method);
if (!(expectedChallenge === c.code_challenge)) throw new Error('Guard failed: expectedChallenge === c.code_challenge');
this.pkceChallenges.delete(c.code_challenge);
this.stateNonces.delete(c.state);
const accessToken = "access_" + simpleHash(req.client_id + c.user_id + this.currentTime);
const refreshToken = "refresh_" + simpleHash(req.client_id + c.user_id + this.currentTime);
const expiresAt = this.currentTime + 3600;
this.accessTokens.set(accessToken, { client_id: req.client_id, user_id: c.user_id, scope: c.scope, expires_at: expiresAt, session_id: c.session_id, refresh_token: refreshToken });
this.refreshTokens.set(refreshToken, { client_id: req.client_id, user_id: c.user_id, scope: c.scope, access_token: accessToken, created_at: this.currentTime });
}
RefreshToken(req: RefreshRequest) {
const xxx: any = {} as any; // FIXME: parser dropped let/forall
const client_id: any = {} as any; // FIXME: parser dropped let/forall
if (!(this.clients.has(req.client_id))) throw new Error('Guard failed: this.clients.has(req.client_id)');
if (!(this.refreshTokens.has(req.refresh_token))) throw new Error('Guard failed: this.refreshTokens.has(req.refresh_token)');
const r = this.refreshTokens.get(req.refresh_token)!;
if (!(r.client_id === req.client_id)) throw new Error('Guard failed: r.client_id === req.client_id');
this.accessTokens.delete(r.access_token);
const newAccessToken = "access_" + simpleHash(req.client_id + r.user_id + this.currentTime);
const newRefreshToken = "refresh_" + simpleHash(req.client_id + r.user_id + this.currentTime);
const expiresAt = this.currentTime + 3600;
this.accessTokens.set(newAccessToken, { client_id: req.client_id, user_id: r.user_id, scope: req.scope.or(r.scope), expires_at: expiresAt, session_id: r.access_token.session_id, refresh_token: newRefreshToken });
this.refreshTokens.set(newRefreshToken, { client_id: req.client_id, user_id: r.user_id, scope: req.scope.or(r.scope), access_token: newAccessToken, created_at: this.currentTime });
this.refreshTokens.delete(req.refresh_token);
}
RevokeToken(client: ClientId, token: string, tokenType: string) {
const xxx: any = {} as any; // FIXME: parser dropped let/forall
const t: any = {} as any; // FIXME: parser dropped let/forall
const r: any = {} as any; // FIXME: parser dropped let/forall
if (!(this.clients.has(client))) throw new Error('Guard failed: this.clients.has(client)');
if (tokenType === "access") {
if (!(this.accessTokens.has(token))) throw new Error('Guard failed: this.accessTokens.has(token)');
this.t = this.accessTokens.get(token)!;
if (!(t.client_id === client)) throw new Error('Guard failed: t.client_id === client');
this.s[token] = null;
this.s[t.refresh_token] = null;
} else {
if (tokenType === "refresh") {
if (!(this.refreshTokens.has(token))) throw new Error('Guard failed: this.refreshTokens.has(token)');
this.r = this.refreshTokens.get(token)!;
if (!(r.client_id === client)) throw new Error('Guard failed: r.client_id === client');
this.s[token] = null;
this.s[r.access_token] = null;
}
}
}
VerifyPKCE(code: AuthorizationCode, verifier: CodeVerifier) {
const xxx: any = {} as any; // FIXME: parser dropped let/forall
if (!(this.authorizationCodes.has(code))) throw new Error('Guard failed: this.authorizationCodes.has(code)');
const c = this.authorizationCodes.get(code)!;
if (!(c.used === false)) throw new Error('Guard failed: c.used === false');
const expectedChallenge = simpleHash(verifier, c.code_challenge_method);
if (!(expectedChallenge === c.code_challenge)) throw new Error('Guard failed: expectedChallenge === c.code_challenge');
}
// V1: Always(NoTokenLeakage)
// Z3 call-out: Bounded Model Checking (BMC) with depth = decisionLog.length+1.
// Mirrors the SMT-LIB BMC emitted by compiler/src/generators/z3.ts (emitBMC).
// The inner expression is SpeckDL; we translate it to a TS boolean
// check that reads the current class state via this.<stateVar>.
async verify1(): Promise<boolean> {
// Temporal: Always(NoTokenLeakage)
// LTL: always(p). Bounded check — p must hold at every step in the
// bounded trace (decision log + current state). The first step
// where p is false violates the always-quantified property.
if (!(NoTokenLeakage)) return false;
for (const _evt of this.decisionLog) {
// Each emit() entry is a step boundary; p is re-evaluated
// against the live state (BMC "safety-at-horizon" view).
if (!(NoTokenLeakage)) return false;
}
return true;
}
}