Loading portfolio...
I am a current student majoring in SFU Computing Science, with a Bachelor of Applied Science. I'm a passionate programmer with a strong interest in software development, web development, and UI design. As a quick learner, it's always exciting to me when I get to learn new things, improve my skills, and build creative projects. I am currently seeking an internship opportunity to further develop my skills.
ResumeBuilding FEITIAN's PQC-ready WebAuthn tooling from end to end while translating hardware lab findings into actionable software fixes and personal learning milestones.
I entered FEITIAN with zero security background and rapidly built my own roadmap to contribute to their post-quantum authentication efforts. Each week blends deep dives into WebAuthn, CTAP2, and PQC standards with practical experiments on FEITIAN's test security keys so I can translate theory into reliable tooling.
I designed study sprints around NIST PQC finalists, WebAuthn ceremony flows, and FEITIAN's firmware guidelines, logging takeaways that let me ask sharper questions and pair with senior engineers after only a few weeks on the job.
I extend our WebAuthn test platform to surface PQC capabilities, capture telemetry from FEITIAN security keys, and script automated ceremony replays that stress every firmware branch before hardware releases leave the lab.
Acting as the bridge to the hardware department, I reproduce PQC failures, capture traces, and convert them into actionable bug reports so new security key prototypes align with the server-side expectations.
I document reproducible test playbooks, reliability dashboards, and API sanity checks so other interns can operate the PQC stack without waiting for me.
Weekly syncs with electrical engineers turn my platform logs into shared terminology, keeping PQC timing, signature formats, and key policies in sync across teams.
I track each experiment in a learning journal, capturing roadblocks and breakthroughs so my growth curve stays as intentional as the product roadmap I help shape.
WebAuthn/FIDO2 server + web app with PQC support for testing security keys.
The test platform provides Post-Quantum Cryptography (PQC) and WebAuthn/FIDO2 servers and web UI that showcases authenticator algorithms, flags, counters, and extension outputs, and bringing post-quantum algorithms alongside classical algorithms.
The backend is a Flask application written for Python 3.8+ that wires Fido2Server
helpers from the python-fido2
library to REST endpoints and WebAuthn ceremonies,
while persisting state with lightweight on-disk storage so demonstrations can run without
external infrastructure.
Metadata and attestation handling lean on companion libraries—cbor2
parses
authenticator CBOR objects, pycose
and pyjwt
validate COSE keys and JWS
signatures, and requests
fetches fresh FIDO Alliance metadata BLOBs—with
optional PQC extras (oqs
, pqcrypto
) available when experimenting with
post-quantum authenticators.
The frontend is composed of vanilla JavaScript modules, structured CSS bundles, and HTML templates served directly by Flask; these assets drive the tabbed interface, JSON editor, modal windows, and interactive tables without requiring a separate build pipeline.
Presents a quick username field with register and authenticate buttons plus inline status and progress feedback for rapid passkey trials.
Layers extensive configuration panels, a live JSON editor, and a saved credential list so you can craft bespoke ceremonies and review stored passkeys in one place.
Accepts pasted payloads, explains which WebAuthn structures it understands, and displays parsed and raw views of decoded responses.
Loads a sortable, filterable table with metadata entries, update controls, and modals for certificates and authenticator details.
The simple tab supplies a minimal form: users enter a username (with a helper button to randomize it), initiate registration or authentication, and watch the inline status and spinner update through each step.
Behind the scenes, simpleRegister
calls the server for credential creation options,
normalizes requested extensions, invokes WebAuthn create
, and posts the resulting credential
back to /api/register/complete
, reporting success, randomizing the username, and refreshing
the saved credential cache. Similarly, simpleAuthenticate
fetches assertion options, calls
WebAuthn get
, and submits the assertion to /api/authenticate/complete
, mapping
common WebAuthn errors to readable status messages.
The advanced tab splits into registration and authentication sub-tabs, each containing expandable panels for user identity, authenticator selection, other options, and extensions with bilingual tooltips that explain every parameter choice. A dedicated JSON editor column mirrors the form state, offers expand, save, and reset controls, and keeps the textarea optimized for manual edits. On the right, a binary-format selector and saved credential panel provide quick format conversions and bulk deletion capabilities. When the page loads, helper scripts randomize IDs and challenges, seed large-blob and PRF fields, pull saved credentials, and wire input events so that any change updates the JSON representation automatically.
During advanced registration, the JSON editor’s payload is validated, optional extensions such as
minPinLength
are merged in, WebAuthn create
is invoked, and the response is
posted to /api/advanced/register/complete
; success triggers modal details, status feedback,
and a saved credential refresh, while failures surface likely incompatibilities (e.g., unsupported
algorithms or extensions).
Advanced authentication mirrors that flow by validating request JSON, enforcing hint rules, performing
WebAuthn get
, and submitting the result to /api/advanced/authenticate/complete
,
again surfacing human-friendly outcomes or guidance when no suitable credential is found.
Saved credentials are fetched from /api/credentials
, normalized with derived hex
identifiers, and stored in client state before rendering the panel or updating JSON previews. Each list
entry highlights discoverable, large-blob, or PQC traits, supports keyboard activation of the detail
modal, and exposes a delete shortcut that targets the correct server record. Server-backed delete and
clear-all handlers confirm intent, post to /api/deletepub
or /api/credentials
,
and update both UI and status messaging when the operations complete.
The credential detail modal itself is defined in HTML but populated entirely by JavaScript at runtime. The renderer gathers user information, user handle encodings, and credential ID encodings; it evaluates stored attestation summaries and flags to describe discoverability, large blob support, minPinLength, attestation checks, authenticator data bits, signature counters, and any client extension outputs captured during registration. Public key information is annotated with COSE key type, ML-DSA parameter sets, and base64/base64url/hex encodings to aid PQC inspection.
When present, archived registration details are embedded for deeper comparison, and an AAGUID "Info" button appears for root-verified attestations; clicking it triggers an asynchronous lookup that can wait for metadata to load, surfaces status updates in the modal, and then closes the modal once the related authenticator entry is highlighted in the MDS table.
The decoder tab enumerates all recognized WebAuthn payload formats—covering credential JSON, CBOR
attestation objects, authenticator data, client data, PEM or DER certificates, general JSON, and CBOR
blobs—and notes that binary inputs may be base64, base64url, or hex (with colon tolerance for hex).
Submissions post the pasted payload to /api/decode
, clear prior output, display progress, and
then either render a structured summary with status chips and sectioned data or report parsing errors;
users can also toggle a raw JSON textarea view for deeper inspection or hide it to focus on the summary.
The MDS tab features an update button, live entry counters, a sortable and filterable table, and modals dedicated to certificate decoding and authenticator detail browsing. When the page loads, any server-provided metadata snapshot is exposed through a hidden bootstrap element so the client can initialize without an extra network call.
The loadMdsData
routine checks for forced reloads, applies bootstrap or cached payloads when
available, and otherwise fetches the metadata BLOB with cache-aware options while handling missing files,
clearing caches, and resetting UI state appropriately. Once metadata entries arrive, they are
transformed, indexed by AAGUID, used to populate filter dropdowns, annotated with next-update deadlines,
and reported through the status banner before column resizing is re-enabled.
Update requests toggle button states, post to /api/mds/update
, interpret whether a reload is
required, and either re-fetch the BLOB or inform the user when data is already current (highlighting
overdue deadlines when necessary). When a credential detail’s AAGUID info button is pressed, the helper
switches to the MDS tab, waits for metadata to load if needed, and then asks the explorer to locate and
highlight the relevant authenticator; the highlighting routine normalizes AAGUIDs, resets filters,
scrolls smoothly to the matching row, and returns whether the focus succeeded.
Clicking an authenticator name stores the active entry (preferring the cached byAaguid
map), scrolls the table row into view, and opens the authenticator modal for focused inspection. The
modal content is generated by buildDetailContent
, which reads the metadata statement to
populate overview grids, description and legal headers, schema information, cryptographic strength,
attestation key IDs, UPV values, algorithm families, attachment and matcher hints, display modes, and
user-verification combinations, each rendered as labeled grids or chip lists for clarity.
Additional helpers append attestation certificate buttons that launch the certificate decoder modal,
render authenticatorGetInfo
fields (including numeric limits, supported versions,
extensions, transports, algorithms, and option flags), and tabulate status reports with formatted dates
and descriptors—all derived from the metadata entry returned by the MDS payload.
Automated trip planning website integrating OpenAI and Tripadvisor APIs.
Travel Advisor is an interactive, AI-enhanced travel planning web application that guides users through building a trip from scratch. It supports both an assisted (AI recommendation) path and a manual selection path. Users can (optionally) let the system suggest a destination based on traveler details and dates, or directly enter a city and country. The app then gathers or infers destination imagery and descriptive context, calculates trip duration, and presents categorized travel content, all wrapped in a smooth, animated multi-step experience. A floating AI chat assistant is always available for follow-up questions, tips, or clarification. The design emphasizes clarity (step-by-step flow), dynamism (Framer Motion animations), and adaptability (AI fallbacks when data is missing).
Uses traveler count, passport/visa info, and trip dates to generate a suggested destination via OpenAI, with retry logic to handle transient failures.
Allows travelers to choose a country and city manually while the system performs basic spelling correction before fetching data.
Supports adding or removing multiple travelers, tracking passport and visa details that inform context-rich AI prompts.
Features custom calendars for departure and return dates with outside-click closing, month navigation, and automatic inclusive trip duration calculation.
Retrieves destination imagery via a TripAdvisor-style API with graceful loading, error, and placeholder states; supplies descriptive text from external data with an AI-generated fallback; and assembles a trip summary covering destination, formatted dates, duration, traveler count (AI path), and dynamic category tabs.
Provides a modular layout for browsing segmented travel resources—such as hotels or activities—tailored to the selected destination.
Maintains conversation history for continuous Q&A, complete with a typing indicator and robust fallback messaging.
Delivers step transitions, content fades, and slides using Framer Motion to keep the multi-step flow clear and engaging.
Includes a one-click reset that restores the initial configuration for rapid iteration or exploring alternative scenarios.
Guards data integrity with spelling verification for manual entries, retry loops for AI calls, and cautious fetch patterns that avoid unnecessary requests.
Framer Motion powers declarative motion, including entrances, layout transitions, and staged fade/slide effects.
Express backend (deployed separately) proxies TripAdvisor-style location data and secures AI request handling.
Cypress end-to-end suites validate multi-step progression, results rendering, and chat functionality.
Vercel hosts both the frontend and serverless backend for fast global distribution.
Visitors land on the Home view with the internal step state set to zero as background visuals, the header, and the floating chat initialize.
Users decide whether to let AI suggest a destination; the choice toggles useAiRecommendation
and advances the workflow.
The AI path captures traveler records—including passport and visa entries—to enrich prompts, while the manual path accepts typed destinations with AI-assisted spell checking.
Departure and return calendars support month navigation and click-outside dismissal; once both dates are chosen, the trip duration is computed using an inclusive day count.
The AI path builds a structured prompt and retries OpenAI calls up to two times when needed, while the manual path validates spelling before querying backend services for imagery and metadata; missing descriptions trigger a secondary AI summary.
The results view assembles a contextual title, destination visuals or placeholders, formatted dates with duration, traveler counts for the AI path, descriptive copy, and dynamic category tabs.
The floating chat remains available throughout, appending user messages, streaming AI replies asynchronously, and showing a typing indicator until responses arrive.
Back navigation lets users revisit previous steps without losing progress, and Start Over resets travelers, dates, destinations, categories, and AI mode selections.
Distinct placeholders communicate loading, error, and empty states for images or descriptions, backed by resilient fallback messaging.
Travelers can explore additional categories, continue the chat, or restart to generate alternate AI recommendations or experiment with manual destinations.
Personal portfolio website with creative UI/UX design and interactive elements.
This site doubles as both the codebase and live experience for Rain's portfolio, pairing handcrafted HTML with layered CSS styling and JavaScript enhancements to deliver a polished personal brand.
The repository organizes assets, typography, animations, and scripts so changes flow cleanly from development to deployment, keeping the portfolio easy to iterate on as new work is added.
Semantic HTML, custom Montserrat and Audiowide font embeds, and responsive CSS modules shape the layout, grid, and card components without a heavy framework.
Vanilla JavaScript powers the intro animation, accessibility-aware project modals, contact form handling, and typewriter headline, keeping dependencies minimal.
Optimized assets and structured source control make it straightforward to ship updates to GitHub Pages or Vercel while preserving fast load times.
A floating dock replicates macOS hover physics with scaling, glow effects, and shortcuts to social links, all tuned for mouse and keyboard access.
The dock's theme button switches between light and dark palettes, updating typography, cards, and accent colors with smooth transitions.
Each portfolio tile opens into a modal window that enlarges the header, preserves focus, and reveals long-form project narratives.
Language learning app with graphics, audio, and text file analyzation.
Fully interactive snake survival game with game logic and graphics implementation.
Feel free to reach out if you have any questions or would like to connect.