3 | {{pattern.name}} {{tag}}
4 |
5 | Description
6 |
7 |
8 |
9 |
10 |
11 | Use cases
12 |
13 | Noise_NX is a fitting handshake pattern if:
14 |
15 | - clients talk to several servers with no prior knowledge of their public keys
16 | - clients do not authenticate themselves
17 |
18 | if a server's public key is known prior to starting the handshake, refer to Noise_NK. If clients need to authenticate themselves, refer to Noise_XX.
19 |
20 | Example of configuration
21 |
22 | Noise_NX requires the server to authenticate itself as part of the handshake. For this to work:
23 |
24 | - the server needs to have its public static key signed by an authoritative key pair
25 | - the client needs to be aware of the authoritative public key
26 |
27 |
28 | Both of these requirements can be achieved using libdisco's key helper functions.
29 |
30 | You can play with the full example here. The root signing key process is illustrated here.
31 |
32 | root key:
33 |
34 | The authoritative root signing key can be generated using libdisco's GenerateAndSaveDiscoRootKeyPair helper function
35 |
36 | // generating the root signing key
37 | if err := libdisco.GenerateAndSaveDiscoRootKeyPair("./privateRoot", "./publicRoot"); err != nil {
38 | panic("cannot generate and save a root key")
39 | }
40 |
41 | This function (documented here) will create two files, a "privateRoot" (resp. "publicRoot") file containing the private (resp. public) part of the root signing key pair.
42 |
43 | The public part can then be retrieved via the LoadDiscoRootPublicKey function.
44 |
45 | // loading the public part
46 | pubkey, err := libdisco.LoadDiscoRootPublicKey("./publicRoot")
47 | if err != nil {
48 | // cannot load the disco root pubkey
49 | }
50 |
51 | // displaying the public part
52 | fmt.Println(hex.EncodeToString(pubkey))
53 |
54 | To sign a peer's static public key, the CreateStaticPublicKeyProof function can be used.
55 |
56 | // load the private root key
57 | privkey, err := libdisco.LoadDiscoRootPrivateKey("./privateRoot")
58 | if err != nil {
59 | // couldn't load the private root key
60 | }
61 |
62 | // create proof where toSign is a peer's static public key
63 | proof := libdisco.CreateStaticPublicKeyProof(privkey, toSign)
64 |
65 | // display the proof
66 | fmt.Println(hex.EncodeToString(proof))
67 |
68 | server:
69 |
70 | As part of Noise_NX, the server needs to be configured with a static public key, as well as a signature over that key.
71 |
72 | To keep things simple, libdisco provides utility functions to avoid X.509 certificates. These utility functions simply allow you to construct a signature over a public static key and to verify such signatures:
73 |
74 | // CreateStaticPublicKeyProof helps in creating a signature over the peer's static public key
75 | // for that, it needs the private part of a signing root key pair that is trusted by the client.
76 | proof := CreateStaticPublicKeyProof(rootPrivateKey, peerPublicKey)
77 |
78 |
79 |
80 |
83 |
84 | Similarly to our typical browser ↔ HTTPS webserver scenario, a proof could also be an X.509 certificate containing the serverKeyPair as well as a signature of the certificate from a certificate authority's public key. If such a complex public key infrastructure is required, you can construct the PublicKeyVerifier and StaticPublicKeyProof yourself to verify a certificate's signature and accept certificates as proofs. See the Configuration section of the protocol Overview.
85 |
86 |
87 |
88 | Once the proof has been computed, it can be passed to the server which will be able to configure itself for a Noise_NX setup:
89 |
90 | serverConfig := libdisco.Config{
91 | HandshakePattern: libdisco.NoiseNX,
92 | KeyPair: serverKeyPair,
93 | StaticPublicKeyProof: proof,
94 | }
95 |
96 | // listen on port 6666
97 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig)
98 | if err != nil {
99 | // cannot setup a listener on localhost
100 | }
101 | addr := listener.Addr().String()
102 | fmt.Println("listening on:", addr)
103 |
104 | client:
105 |
106 | the client needs to be configured with a function capable of acting on the static public key the server will send (as part of the handshake). Without this, there are no guarantees that the static public key the server sends is "legit".
107 |
108 | clientConfig := libdisco.Config{
109 | HandshakePattern: libdisco.NoiseNX,
110 | PublicKeyVerifier: verifier,
111 | }
112 |
113 | Again, libdisco provides utility functions to create a useful PublicKeyVerifier callback:
114 |
115 | // CreatePublicKeyVerifier helps in creating a callback function that will verify a signature
116 | // for this it needs the public part of the signing root public key that we trust.
117 | verifier := CreatePublicKeyVerifier(rootPublicKey)
118 |
119 | Finally the full example for a client:
120 |
121 | // create a verifier for when we will receive the server's public key
122 | verifier := libdisco.CreatePublicKeyVerifier(rootPublicKey)
123 |
124 | // configure the Disco connection
125 | clientConfig := libdisco.Config{
126 | HandshakePattern: libdisco.NoiseNX,
127 | PublicKeyVerifier: verifier,
128 | }
129 |
130 | // Dial the port 6666 of localhost
131 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig)
132 | if err != nil {
133 | // client can't connect to server
134 | }
135 | defer client.Close()
136 | fmt.Println("connected to", client.RemoteAddr())
137 |
138 | Security Considerations
139 |
140 | The same security discussed in the Noise specification for the relevant handshake pattern apply.
141 |
142 |
143 |
144 |