2
0
Fork
You've already forked mitid-connect
0
No description
  • Rust 71.6%
  • Python 23.1%
  • Nix 3.4%
  • C++ 1.3%
  • Just 0.6%
Find a file
2026年07月05日 11:16:52 +02:00
arduino/servo-test bring up-to-date 2025年07月26日 13:14:17 +02:00
docs adds the final ruling from domæneklagenævnet 2026年07月05日 11:16:52 +02:00
example-images adds ocr on the client + tests 2025年10月10日 12:23:42 +02:00
images up-to-date 2025年08月03日 20:16:13 +02:00
python-tests update python client script 2025年08月11日 00:40:57 +02:00
src fixed formatting of the logo 2026年07月03日 20:26:42 +02:00
.envrc added all files 2025年07月24日 20:44:14 +02:00
.gitignore added all files 2025年07月24日 20:44:14 +02:00
Cargo.lock fixed all clippy warnings/suggestions 2025年12月10日 14:16:51 +01:00
Cargo.toml adds totp support 2025年10月11日 21:32:08 +02:00
devenv.lock bumped devenv.lock 2026年07月03日 19:48:00 +02:00
devenv.nix added a justfile 2025年08月19日 16:43:59 +02:00
devenv.yaml adds support for cross-rs for rpi1b + initial work on rust client 2025年08月11日 00:38:35 +02:00
justfile justfile: fixed missing space 2025年08月21日 17:17:08 +02:00
LICENSE Added MIT license 2025年10月14日 19:53:20 +02:00
package.nix bumped cargo hash 2025年12月27日 21:05:35 +01:00
readme.md bit of code cleanup/styling + typo 2025年11月29日 16:21:04 +01:00
rustfmt.toml added rustfmt.yml because of aphelia 2025年08月08日 21:38:49 +02:00

MitID Connect

This project allows you to connect your MitID Code Display to the internet using simple, readily available components. If you want to build your own, check out build.md for instructions.

Why was this made?

Because I am tired of the following things (incomplete):

  • Closed-source proprietary app
  • Obfuscated app
    • Harder to validate the white-paper/expected behaviour
    • Which information does the app leak? No clue
    • Aids security? Nope, does jack shit
  • Reliance on Google Play Integrity Check
    • Why does it work on a phone whose last security patch level was in 2018, but an up-to-date GrapheneOS/LineageOS without root is banned?
  • Technically possible to bring-your-own-key (BYOK), am I allowed to? Nope
    • Bulky Chinesium that is impractical to carry around
    • Additional hardware I must back up/take care of
    • Want added security (e.g. biometrics)? Sucks to be you
    • Do not plan to fall in the water with the forced hardware token you must carry everywhere. The consequence is the above: sucks to be you (includes dropping it or other fun stuff)
    • Why not allow this? Because Denmark is special, and nobody else offers this, oh wait
  • Continuing the fetish of being forced to use outside-of-eu-things-and-fuck-your-privacy-just-comply

It is a trend we keep seeing in the EU, and especially in Denmark.

I've been in contact with Digitaliseringsstyrelsen, and I've decided to publish our email correspondence here.

This is the perfect time to mention fuck Chat Control, and fuck trying to limit the internet with, e.g. age verification EVERYWHERE.

Security

Surely nothing can go wrong with connecting your national 2FA to the internet. Right?

Frontend

How it works?

Sequence diagram

Here is how it looks when requesting a TOTP token using auth totp.

It is important to keep in mind that the frontend and backend are 100% seperate, and that the backend works by polling the frontend. Ie. the backend is not publicly accessible, and is in charge of all rate limiting, authentication, etc. So even if the frontend is compromised, it does not affect the security in any way.

Configuration

Below the configuration files for the server and client can be seen. For the most up-to-date configuration, run the commands listed in each section.

Keep in mind the client configuration probe_points have been omitted, as it is very long.

Server configuration

cargo run -- server print-config

{
 "host": "127.0.0.1",
 "port": 8000,
 "api_key": "change-me",
 "remove_totp_after": "30s",
 "auth_totp_enabled": false
}

Client configuration

cargo run -- client print-config

{
 "camera_adjust_delay": "1s",
 "hardware_token_wakeup_delay": "1s",
 "api_check_interval": "500ms",
 "delay_after_success": "15s",
 "hardware_config": {
 "servo_pwm_channel": 0,
 "led_pin": 2,
 "servo_idle_angle": 0.0,
 "servo_click_angle": 35.0,
 "servo_click_sleep": "500ms",
 "servo_config": {
 "period": {
 "secs": 0,
 "nanos": 20000000
 },
 "pulse_min": {
 "secs": 0,
 "nanos": 500000
 },
 "pulse_max": {
 "secs": 0,
 "nanos": 2400000
 },
 "angle_min": 0.0,
 "angle_max": 180.0
 }
 },
 "camera_config": {
 "camera_index": 0,
 "frame_rate": 5,
 "drop_frames": 10
 },
 "ocr_config": {
 "enabled": true,
 "probe_points": [ ... omitted ... ],
 "block_radius": 71
 },
 "api_config": {
 "base_url": "http://192.168.1.30:8000/api/v1",
 "api_key": "<replace_me>"
 },
 "totp_config": {
 "enabled": false,
 "digits": 8,
 "skew": 1,
 "step": 30,
 "encoded_secret": "<change-me>"
 },
 "ratelimit_config": {
 "ratelimit_window": "30m",
 "ratelimit_requests_pr_window": 30
 }
}