Mermaid to ProVerif
Contract
| Field | Bound contract |
|---|---|
| Trigger | A cryptographic Mermaid sequenceDiagram exists and the user asks for a ProVerif model or secrecy/authentication/replay/forward-secrecy verification. |
| Authority | Reversible local: writes only named .pv and report files; rollback is deleting uncommitted files. No remote mutation. |
| Side effect | A named .pv model file and verifier output; may execute ProVerif. |
| Done | The model type-checks, participant sends and receives match, reachability is established before security queries are trusted, and assumptions plus each query result are reported. |
Inputs
- Required: a Mermaid
sequenceDiagramblock or file containing at least two named participants and at least one message between them. - Optional: a list of security properties to verify, one or more of
secrecy,authentication,replay,forward_secrecy. Defaults to all four if not specified. - Optional: a ProVerif output filename (
.pv). Defaults to<diagram-name>.pv.
Procedure
1. Parse the Mermaid sequenceDiagram
Parse the Mermaid sequenceDiagram into an abstract syntax tree. Extract every named participant and every directed message (A->>B: label). Record message order. Identify cryptographic operation keywords in each label: encrypt, sign, hash, sharedkey, dh, pk, sk. If a label contains no recognized keyword, treat the message as a plaintext transfer over the channel type indicated by the arrow style (solid for public, dotted for private). Done when: every participant and message is extracted with its order and cryptographic keywords identified.
2. Define ProVerif cryptographic primitives, channels, and attacker model
Define the Dolev-Yao cryptographic model in ProVerif based on the keywords found in the diagram. This stage establishes the primitive vocabulary that Stage 3 uses to map messages; without it, free-form Mermaid labels cannot be translated into valid ProVerif constructs.
Declare types: key, bitstring, and any protocol-specific types the labels reference.
Declare constructors for each cryptographic operation the diagram uses:
| Mermaid keyword | ProVerif constructor |
|---|---|
encrypt |
fun enc(bitstring, key): bitstring |
pk |
fun pk(key): key |
sk |
fun sk(key): key (or free for long-term keys) |
sign |
fun sign(bitstring, key): bitstring |
hash |
fun hash(bitstring): bitstring |
sharedkey |
fun sharedkey(key, key): key (or a KDF) |
dh |
fun dh(key, key): key (Diffie-Hellman combine) |
Declare destructors for the inverse operations:
| Operation | ProVerif destructor |
|---|---|
| decrypt | reduc forall m: bitstring, k: key; dec(enc(m, k), k) = m |
| verify signature | reduc forall m: bitstring, k: key; getmsg(sign(m, k), pk(k)) = m |
If the diagram uses an operation with no standard Dolev-Yao primitive, stop and report the unsupported construct rather than inventing semantics.
Define channels: a public channel c for messages sent over public networks (the Dolev-Yao attacker controls these), and private channels for messages marked as authenticated or confidential in the diagram annotations. The attacker model is the standard Dolev-Yao attacker: it can intercept, modify, replay, and compose messages on public channels but cannot break cryptographic primitives.
Done when: all types, constructors, destructors, channels, and the attacker model are declared and cover every cryptographic keyword in the diagram.
3. Map messages to ProVerif process actions and events
Represent each participant as a let p = ... process in ProVerif. Map each actor declaration to new p:name;.
Convert each message into ProVerif process actions using the primitives from Stage 2:
- A plaintext message
A->>B: mover a public channel becomesout(c, m)in A's process andin(c, x: bitstring)in B's process. - An encrypted message
A->>B: encrypt(m, k)becomesout(c, enc(m, k))andin(c, x: bitstring); let m = dec(x, k). - A signed message
A->>B: sign(m, sk_A)becomesout(c, sign(m, sk_A))andin(c, x: bitstring); let m = getmsg(x, pk(sk_A)). - A hashed message becomes
out(c, hash(m))with no destructor (hash is one-way). - A Diffie-Hellman exchange maps to two
out/inpairs for the public values and asharedkeyordhcomputation on each side.
Emit event(e_sent(A, m)) before each out and event(e_received(B, m)) after each in, preserving the message order from the AST. These events anchor the reachability and authentication queries in Stage 4.
Write the .pv model file. Prefix it with a (* ADAPTED FROM: Trail of Bits mermaid-to-proverif skill; CC-BY-SA-4.0; https://github.com/trailofbits/skills *) comment block and a (* ASSUMPTIONS: <list of assumptions> *) block derived from the diagram annotations or human-supplied constraints.
Done when: every message is mapped to ProVerif process actions with events, and the .pv file is written with the attribution and assumptions blocks.
4. Append reachability and security queries
Append one reachability query query event(e_start) ... event(e_end) ... for each end-to-end message sequence before any security query, so reachability is established before security results are trusted.
Append security queries based on the requested properties:
secrecy:query secret ~m.;per sensitive message variablem.authentication:query event(e_received(A,m)) ==> event(e_sent(B,m)).per message variablem.replay:query not event(replay_attempted).per identified vulnerable transition.forward_secrecy:query secret ~m. @weak_agree ...per session-key-derived variable.
Done when: reachability queries are appended for every end-to-end sequence and security queries are appended for every requested property.
5. Execute ProVerif and generate a verification report
Execute proverif <output>.pv. Collect the type-check result, all query results, and any WARNING or RESULT line.
Validate the output: type-check must succeed; participant sends and receives in ProVerif must correspond to the original diagram participants; each security query must be preceded by a proved reachability query; all assumptions listed in the file header must be acknowledged in the report.
Write the verification report to <output>_report.txt containing type-check status, participant correspondence confirmation, each reachability result, each security query result, and the assumption list.
Done when: ProVerif executes, the output is validated, and the report is written.
Failure and recovery
- Type-check failure: ProVerif reports a syntax or type error. Report the error verbatim, stop. Do not trust any query result. No partial model is produced.
- Participant mismatch: the number of senders or receivers in the ProVerif output does not match the diagram participants. Report the mismatch and stop. The model is not trustworthy.
- Security query passes without prior reachability proof: treat the security result as
RESULT i: noninterference_interpreted_as_secrecy_not_provedor equivalent indeterminate. Do not report it as proved. - Reachability failure: a required event is unreachable. Report which query cannot be evaluated because its precondition is unreachable. Do not claim the security property holds.
- Unsupported cryptographic construct: a Mermaid label uses an operation with no standard Dolev-Yao primitive. Stop and report the unsupported construct; do not invent constructors, destructors, or event semantics.
- Partial-result rule: if ProVerif produces a partial output (crash, timeout, unparsable result), report
verification_inconclusiveand the concrete reason. Do not claim success. - Rollback: if the model file was created and verification failed, delete the uncommitted file before reporting.
Output
- The named
.pvmodel file containing the converted protocol model, Dolev-Yao primitive declarations, assumption block, reachability queries, and security queries. - A verification report
<output>_report.txtcontaining:- Type-check status (
SUCCESSor failure detail). - Participant correspondence confirmation or mismatch report.
- Each reachability query result.
- Each security query result with
RESULTclassification. - The complete assumption list.
- Type-check status (