{"id":20483394,"url":"https://github.com/styczynski/needham-schroeder-promela","last_synced_at":"2026-06-22T08:31:58.262Z","repository":{"id":79817395,"uuid":"183958193","full_name":"styczynski/Needham-Schroeder-promela","owner":"styczynski","description":"The formal verification of Needham-Schroeder public key protocol with help of Spin","archived":false,"fork":false,"pushed_at":"2019-04-29T02:47:03.000Z","size":238,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-31T04:41:35.079Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/styczynski.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-28T20:57:59.000Z","updated_at":"2019-12-19T11:33:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"8ef97966-38b3-4c30-b36e-f3f834439ea6","html_url":"https://github.com/styczynski/Needham-Schroeder-promela","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/styczynski/Needham-Schroeder-promela","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styczynski%2FNeedham-Schroeder-promela","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styczynski%2FNeedham-Schroeder-promela/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styczynski%2FNeedham-Schroeder-promela/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styczynski%2FNeedham-Schroeder-promela/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/styczynski","download_url":"https://codeload.github.com/styczynski/Needham-Schroeder-promela/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/styczynski%2FNeedham-Schroeder-promela/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34641636,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T16:17:15.014Z","updated_at":"2026-06-22T08:31:58.232Z","avatar_url":"https://github.com/styczynski.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ex 1 Concurrency theory - modelling with Spin\n\n## The task specification\n\nWe are given two programs - the encoder and the decoder.\n* Encoder receives message `X` and public key `k_-1` and returns enrypted message `{X}k_-1`\n* Decoder receives message `{X}k_-1` and key `l` and returns `X` only and only if `k == l`\n\nWe will be working with asymmetric protocol that looks like the following:\n* Party `A` downloads from `CA` server public key `kB_-1` of party `B` that it wants to connect to\n* `A` chooses random message `X` and sends message `{X, A}kB_-1` to `B`\n* Party `B` receives that message, decodes, downloads from the `CA` the public key of `A` and sends the message `{X, A}kA_-1` back to `A`\n* `A` receives that message and sends back `{X, Y}kA_-1` where `Y` is just a randomly chosen mesage\n* `Y` is now a common secret shared by both of the parties\n\nThe task is to model the system in Promela and find the technique that can be exploited by the attacker to compromise the connection.\n\n## Initial model\n\nThe first approach was to model the system such way that the nonces (X, Y) are radomly selected from range of predefined values and the message itself consist of 2-element array of bytes. Such model is hard to work with as we do not need to know exact values of nonces, but rather we care about the fact of knowing them. It also have a lot of flaws as it was the very first approach of mine to modeling in Pamela language.\n\nThe model is available in file [prototype.pml](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/src/prototype.pml)\n\n## Improving the model\n\nFor simplicity we assume that for now all parts of key-exchange algorithm do not communicate with certificate provider (all public keys are known to everyone). That assumption clarifies all MSC diagrams and reduces trivial code.\n\nThe basic model with A, B and basic interceptor C (who passes messages transparently) is present in the file [basic_model.pml](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/src/basic_model.pml)\n\n![Basic mitm model](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/img/model_image1.png?raw=true)\n\n### Code structure\n\nThe channels:\n```promela\nchan chExchange = [0] of {\n\tmtype:DataType, mtype:DataType, mtype:DataType, mtype:DataType\n};\n\nchan chConfirm = [0] of {\n\tmtype:DataType, mtype:DataType, mtype:DataType\n};\n```\nAre defined as zero-capacity Rendezvous ports for passing 3 and 2-components messages.\n\nThe messages have the following format:\n```\n  \u003csource_of_message/destination\u003e \u003cdata1\u003e \u003cdata2\u003e \u003cpublic_key_used_for_encryption\u003e\n    representing {data1, data2} PUB(key)\n        or\n  \u003csource_of_message/destination\u003e \u003cdata\u003e \u003cpublic_key_used_for_encryption\u003e\n    representing {data} PUB(key)\n```\n\n`source_of_message/destination` can be anyting (i.e. `A`, `B` or `C`), data can be `X`, `Y`, `ANY` (representing any data value - explained later)\n\nFor messages sent to channel we assume that the value represents the source of a message.\nFor messages received from the channel we assume that the value represents the destination of a message.\n\n### Processes\n\nThe process C reads messages sent by A and B and switches first element of tuple thus forwards them to valid destinations.\n(process C is a transparent proxy).\n\n## Interceptor knowledge base\n\nThe model itself explicitly defines MITM attack model.\n\nTo make the Spin find the solution to the insecure protocol we have to specify all meaningful possible combinations of inputs/outputs for agent (process C) and requirements/additions to the knowledge database implied by them.\n\nThe following tables are representing correlations between message and the additional knowledge that it brings into internal state machine of process C and the knowledge required to send such messages.\n\n### Data values semantics\n\nWe will be using data values unused till this moment i.e `ANY` and `NA`.\n\n* `ANY` represents any data that is known to the process C. It can be a new nonce generated by the agent or any randomized value.\n* `NA` represents value that is not known to the process C. It's used in situation when required knowledge conditions to send a message are not met. Its usage simplifies the code. \n\n### Agent accumulating knowledge\n\n| Received message | Additional knowledge |\n|------------------|--------------|\n| (X, A, C)        | X            |\n| (X, A, B)        | (X, A, B)    |\n| (X, C)           | X            |\n| (Y, C)           | Y            |\n| (ANY, C)         | -            |\n| (A, C)           | -            |\n| (B, C)           | -            |\n| (C, C)           | -            |\n| (Y, Y, C)        | Y            |\n| (ANY, Y, C)      | Y            |\n| (X, Y, A)        | (X, Y, A)    |\n| (Y, Y, A)        | (Y, Y, A)    |\n| (ANY, Y, A)      | (ANY, Y, A)  |\n| (A, Y, C)        | Y            |\n| (B, Y, C)        | Y            |\n| (C, Y, C)        | Y            |\n| (A, Y, A)        | (A, Y, A)    |\n| (B, Y, A)        | (B, Y, A)    |\n| (C, Y, A)        | (C, Y, A)    |\n| (X, B)           | (X, B)       |\n| (Y, B)           | (Y, B)       |\n| (ANY, B)         | (ANY, B)     |\n| (A, B)           | (A, B)       |\n| (B, B)           | (B, B)       |\n| (C, B)           | (C, B)       |\n\nThis table is modeled with the following Promela code:\n```promela\n    :: d_step {\n      chExchange ? _, data1, data2, data3; if\n        :: (data3 == C)-\u003e learn1(data1); learn1(data2)\n        :: else learn3(data1,data2,data3)\n      fi;\n      data1 = 0; data2 = 0; data3 = 0;\n    }\n    :: d_step {\n      chConfirm ? _, data1, data2; if\n        :: (data2 == C)-\u003e learn1(data1)\n        :: else learn2(data1,data2)\n      fi;\n      data1 = 0; data2 = 0;\n    }\n```\n\nLearn macros are helper macros to switch bit states representing knowledge of facts:\n```promela\n    #define learn1(data1) if \\\n            :: (data1 == X)-\u003e know_X = 1 \\\n            :: (data1 == Y)-\u003e know_Y = 1 \\\n            :: else skip \\\n        fi;\n\n    #define learn2(data1,data2) if \\\n            :: (data1 == Y \u0026\u0026 data2 == B)-\u003e know_YB = 1 \\\n            :: else skip \\\n        fi;\n\n    #define learn3(data1,data2,data3) if \\\n            :: (data1 == X \u0026\u0026 data2 == A \u0026\u0026 data3 == B) -\u003e know_XAB = 1 \\\n            :: (data1 == X \u0026\u0026 data2 == Y \u0026\u0026 data3 == A) -\u003e know_XYA = 1 \\\n            :: else skip \\\n        fi;\n```\n\n### Agent using its knowledge\n\n| Sent message | Required knowledge   |\n|--------------|----------------------|\n| (X, A, B)    | X or (X, A, B)       |\n| (X, B, B)    | X or (X, B, B)       |\n| (X, C, B)    | X or (X, C, B)       |\n| (Y, A, B)    | Y or (Y, A, B)       |\n| (Y, B, B)    | Y or (Y, B, B)       |\n| (Y, C, B)    | Y or (Y, C, B)       |\n| (ANY, A, B)  | -                    |\n| (ANY, B, B)  | -                    |\n| (ANY, C, B)  | -                    |\n| (X, A, A)    | X or (X, A, A)       |\n| (X, B, A)    | X or (X, B, A)       |\n| (X, C, A)    | X or (X, C, A)       |\n| (A, A, B)    | -                    |\n| (A, B, B)    | -                    |\n| (A, C, B)    | -                    |\n| (B, A, B)    | -                    |\n| (B, B, B)    | -                    |\n| (B, C, B)    | -                    |\n| (C, A, B)    | -                    |\n| (C, B, B)    | -                    |\n| (C, C, B)    | -                    |\n| (Y, B)       | Y or (Y, B)          |\n| (X, X, A)    | X or (X, X, A)       |\n| (X, Y, A)    | (X \u0026 Y) or (X, Y, A) |\n| (X, ANY, A)  | X or (X, ANY, A)     |\n\nThe table is modeled with the following Promela code:\n```promela\n/* ... */\n    :: chExchange ! ((kX || k_X_A__B) -\u003e B : NA), X, A, B\n    :: chExchange ! (kX -\u003e B : NA), X, B, B\n    :: chExchange ! (kX -\u003e B : NA), X, C, B\n    :: chExchange ! (kY -\u003e B : NA), Y, A, B\n    :: chExchange ! (kY -\u003e B : NA), Y, B, B\n    :: chExchange ! (kY -\u003e B : NA), Y, C, B\n    :: chExchange ! B, ANY, A, B\n    :: chExchange ! B, ANY, B, B\n    :: chExchange ! B, ANY, C, B\n    :: chExchange ! (kX -\u003e A : NA), X, A, A\n    :: chExchange ! (kX -\u003e A : NA), X, B, A\n    :: chExchange ! (kX -\u003e A : NA), X, C, A\n    :: chExchange ! B, A, A, B\n    :: chExchange ! B, A, B, B\n    :: chExchange ! B, A, C, B\n    :: chExchange ! B, B, A, B\n    :: chExchange ! B, B, B, B\n    :: chExchange ! B, B, C, B\n    :: chExchange ! B, C, A, B\n    :: chExchange ! B, C, B, B\n    :: chExchange ! B, C, C, B\n    :: chConfirm ! ((k_Y__B || kY) -\u003e B : NA), Y, B\n    :: chConfirm ! ((k_Y__B || kY) -\u003e B : NA), Y, B\n    :: chExchange ! (kX -\u003e A : NA), X, X, A\n    :: chExchange ! (((kX \u0026\u0026 kY) || k_X_Y__A) -\u003e A : NA), X, Y, A\n    :: chExchange ! (kX -\u003e A : NA), X, ANY, A\n/* ... */\n```\n\n### Finding the solution\n\nTo find the solution please run the following command:\n```\n  $ bash ./smart_c_find.sh\n```\n\nThe Spin tool will generate a trace with the result.\n\n![Autogenerated mitm trace](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/img/model_image2.png?raw=true)\n\nThe diagram differs only in layout terms from the top one, which means we found a working MITM trace!\n\n## Modelling CA proccess\n\nWe want to add A CA process to the model, such `CA` will communicate with all parties, but `C` won't intercept messages on lines `A\u003c-\u003eCA` or `B\u003c-\u003eCA`.\n\nWe will change hardcoded `A` and `B`s on last element of message tuples to values received exactly once during process lifetime - when the public keys are required to continue for the first time.\n\nYou can see the example with CA process here: [with_ca.pml](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/src/with_ca.pml)\n\n**Trail for that example can be found [here](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/trail/with_ca.pml.trail)**\n\nThe diagram is nearly the same, but with queries back and forth to `CA`.\n\n## Modelling CA middleman\n\nWe can model `MIDM` on `A/B\u003c-\u003eCA` connections as well:\n```promela\n\t/* Channels for incoming and outcoming requests for pubkey */\n\tchan chCAIn = [0] of {\n\t\tmtype:DataType, mtype:CAName\n\t};\n\tchan chCAOut = [0] of {\n\t\tmtype:DataType, mtype:DataType\n\t};\n```\n\nCA process iself looks like this:\n```promela\n/* Process that answers with public keys */\nproctype ProcessCA() {\n\tmtype:CAName chReqIn;\n\tdo\n\t\t:: (processASuccess \u0026\u0026 processBSuccess) -\u003e break;\n\t\t:: else -\u003e\n\t\t\tchCAIn ? CA, chReqIn;\n\t\t\tif\n\t\t\t\t:: (chReqIn == CA_A) -\u003e chCAOut ! CA, A;\n\t\t\t\t:: (chReqIn == CA_B) -\u003e chCAOut ! CA, B;\n\t\t\tfi\n\tod\n}\n```\n\nAnd our agent can send, receive and change messages:\n```\n\t/* ... */\n\t:: atomic {\n\t\tchCAIn ? B, chReqInC;\n\t\tchCAIn ! CA, chReqInC;\n\t}\n\t:: atomic {\n\t\tchCAOut ? CA, chReqInC;\n\t\tchCAOut ! B, chReqInC;\n\t}\n\t:: atomic {\n\t\tchCAOut ? CA, chReqInC;\n\t\tchCAOut ! B, C;\n\t}\n\t/* ... */\n```\n\nAs earlier you can generate trace for that model simply calling:\n```bash\n$ bash ./broken_ca_find.sh\n```\n\nYou can find the example in the [broken_ca.pml](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/src/broken_ca.pml) file. Trace is available here - [broken_ca.pml.trail](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/trail/broken_ca.pml.trail) with MSC diagram as well - [broken_ca.msc](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/trail/broken_ca.msc)\n\nAs we may expect the trace diagram looks like follows:\n![Autogenerated CA mitm trace](https://github.com/styczynski/Needham-Schroeder-promela/blob/master/img/model_image3.png?raw=true)\n\nThe agent swaps pubkeys and now the knowledge stat is as follows:\n```promela\n\n\tknow_X = 1\n\tknow_Y = 1\n\n```\n\nThat's great! We just swapped the public keys with our own and the agent managed to completely compromise the connection! Hooray! :rocket:\n\nPiotr Styczyński 25-04-2019\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstyczynski%2Fneedham-schroeder-promela","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstyczynski%2Fneedham-schroeder-promela","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstyczynski%2Fneedham-schroeder-promela/lists"}