aboutsummaryrefslogtreecommitdiff
path: root/docs/Certificates.md
blob: c050754e1a8e9eb5d5434ead7d578e4dc39cdce8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# Certificates and server authentication

## Trust on first use

### Default configuration

Use the default configuration like this:
```rust
client.open_connection(/*...*/, CertificateVerificationMode::TrustOnFirstUse(TrustOnFirstUseConfig {
        ..Default::default()
    }),
);
```

With the default configuration, known hosts and their fingerprints are stored in a file, which defaults to `quinnet/known_hosts`.

The defaults verifier behaviours are:
- For an `unknown` certificate (first time this server is encountered) => the client trusts this certificate, stores its fingerprint and continue the connection;
- For a `trusted` certificate (the fingerprint matches the one stored for this server) => the client trusts this certificate and continue the connection;
- For an `untrusted` certificate (the certificate's fingerprint does not match the one in the store) => the client raises an event to the Bevy app and waits for an action to apply.
 
### Examples configurations

Default verifier behaviours with a custom store file:
```rust
client.open_connection(/*...*/, CertificateVerificationMode::TrustOnFirstUse(TrustOnFirstUseConfig {
        known_hosts: KnownHosts::HostsFile("MyCustomFile".to_string()),
        ..Default::default()
    }),
);
```

Custom verifier behaviours with a custom store:
```rust
client.open_connection(/*...*/, CertificateVerificationMode::TrustOnFirstUse(TrustOnFirstUseConfig {
        known_hosts: KnownHosts::Store(my_cert_store),
        verifier_behaviour: HashMap::from([
                (
                    CertVerificationStatus::UnknownCertificate,
                    CertVerifierBehaviour::ImmediateAction(CertVerifierAction::TrustAndStore),
                ),
                (
                    CertVerificationStatus::UntrustedCertificate,
                    CertVerifierBehaviour::ImmediateAction(CertVerifierAction::AbortConnection),
                ),
                (
                    CertVerificationStatus::TrustedCertificate,
                    CertVerifierBehaviour::ImmediateAction(CertVerifierAction::TrustOnce),
                ),
            ]),
    }),
);
```

### Events

The Quinnet client plugin raises Bevy events during the connection process (during the certificate verification).

- `CertInteractionEvent`: a user action is requested before continuing. This event is only raised when the verifier behaviour for a specific certificate status is set to `CertVerifierBehaviour::RequestClientAction`
- `CertTrustUpdateEvent`: the client plugin encoutered a new trust entry to register. If the store is a file, the client plugin has already updated it. If the store is a custom hashmap given to the client plugin (via `KnownHosts::Store(my_cert_store)`), it is up to the user to update its store accordingly.
- `CertConnectionAbortEvent`: signals that the connection was aborted during the certificate verification (through the `CertVerifierAction::AbortConnection`).

Here is a simple example for a custom handler of `CertInteractionEvent`:

```rust
fn handle_cert_events(mut cert_action_events: EventReader<CertInteractionEvent>) {
    // We may receive a CertInteractionEvent during the connection
    for cert_event in cert_action_events.iter() {
        match cert_event.status {
            // We want to abort the connection if the certificate is untrusted, else we continue
            CertVerificationStatus::UntrustedCertificate => cert_event
                .apply_cert_verifier_action(CertVerifierAction::AbortConnection)
                .unwrap(),
            _ => cert_event
                .apply_cert_verifier_action(CertVerifierAction::TrustOnce)
                .unwrap(),
        }
    }
}
```

### Fingerprints

Fingerprints in Quinnet are a SHA-256 hash of the certificate data in DER form.

### Known hosts file format

This hosts file format is really simplistic for now.
There is one line per entry, and each entry is a server name (as dns or ip) followed by a space, followed by the currently known fingerprint encoded in base64.

Example:
```
127.0.0.1 o1cpTe602uTq4pVwT+km8QtEPQE/xCAgk+3AicW/i9g=
123.123.123.123 kzXIwhvMSbWCQOimT3btnFlmc/Lq0UN0JhSeQadaGbg=
```

#### Limitations 

This simple format implies that if two servers are hosted on the same machine on two different ports, they should currently share the same certificate to avoid any conflict.