├── AUTHORS
├── CONTRIBUTING.md
├── LICENCE
├── README.md
├── clean.sh
├── client
├── client.go
└── client_test.go
├── clientCore
├── mixClient.go
└── mixClient_test.go
├── config
├── config.go
└── structs.proto
├── helpers
├── utilities.go
└── utilities_test.go
├── logging
└── logger.go
├── main
├── main.go
├── networker
├── NetworkClient.go
└── NetworkServer.go
├── node
├── .DS_Store
├── mix.go
└── mix_test.go
├── pki
├── database.go
└── database_test.go
├── run_clients.sh
├── run_network.sh
├── server
├── mixServer.go
├── providerServer.go
└── server_test.go
├── sphinx
├── crypto.go
├── sphinx.go
├── sphinx_structs.proto
├── sphinx_test.go
└── utils.go
└── vendor
└── vendor.json
/AUTHORS:
--------------------------------------------------------------------------------
1 | # This is the list of Loopix-Messaging authors for copyright purposes.
2 | #
3 | # This does not necessarily list everyone who has contributed code, since in
4 | # some cases, their employer may be the copyright holder. To see the full list
5 | # of contributors, see the revision history in source control.
6 |
7 | DeepMind Technologies Ltd.
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Anonymous messaging using mix networks
2 |
3 | This is an experimental implementation of an anonymous messaging system based on
4 | Ania Piotrowska's PhD research.
5 |
6 | Note that this is not an officially supported DeepMind product.
7 |
8 | ## Setup
9 |
10 | To build and test the code you need:
11 |
12 | * Go 1.9 or later
13 |
14 | Before running or testing the code run:
15 |
16 | ```shell
17 | govendor install +local
18 | govendor test +local
19 | ```
20 |
21 | To perform the unit tests run:
22 |
23 | ```shell
24 | go test ./...
25 | ```
26 |
27 | Before first fresh run of the system run:
28 |
29 | ```shell
30 | bash clean.sh
31 | ```
32 |
33 | This removes all log files and database
34 |
35 | ## Usage
36 |
37 | To run the network, i.e., mixnodes and providers run
38 |
39 | ```shell
40 | bash run_network.sh
41 | ```
42 |
43 | This spins up 3 mixnodes and 1 provider
44 |
45 | To simulate the clients run
46 |
47 | ```shell
48 | bash run_clients.sh
49 | ```
50 |
--------------------------------------------------------------------------------
/clean.sh:
--------------------------------------------------------------------------------
1 | #// Copyright 2018 The Loopix-Messaging Authors
2 | #//
3 | #// Licensed under the Apache License, Version 2.0 (the "License");
4 | #// you may not use this file except in compliance with the License.
5 | #// You may obtain a copy of the License at
6 | #//
7 | #// http://www.apache.org/licenses/LICENSE-2.0
8 | #//
9 | #// Unless required by applicable law or agreed to in writing, software
10 | #// distributed under the License is distributed on an "AS IS" BASIS,
11 | #// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | #// See the License for the specific language governing permissions and
13 | #// limitations under the License.
14 |
15 | #!/usr/bin/env bash
16 |
17 | # This script should be run from inside the anonymous-messaging package directory
18 |
19 | logDir="$(PWD)/logs"
20 | pkiDir="$(PWD)/pki/database.db"
21 |
22 | if [ -d $pkiDir ]
23 | then
24 | echo "Removing the following directory" $pkiDir
25 | rm -f $pkiDir
26 | echo "Removed existing PKI files"
27 | else
28 | echo "Nothing to remove. The PKI directory does not exists"
29 | fi
30 |
31 |
32 | if [ -d $logDir ]
33 | then
34 | echo "Removing existing logs in the following directory" $logDir
35 | rm -rf $logDir
36 | echo "Creating a new log folder in directory" $logDir
37 | mkdir $logDir
38 | else
39 | echo "Nothing to remove. The logs directory does not exist."
40 | fi
41 |
42 | function kill_port() {
43 | PID=$(lsof -t -i:$1)
44 | echo "Killing process: $PID"
45 | # kill -TERM ${PID}
46 | kill -KILL ${PID}
47 | # kill -TSTP ${PID}
48 | # kill -CONT ${PID}
49 | }
50 |
51 | for var in "$@"
52 | do
53 | kill_port ${var}
54 | done
55 |
--------------------------------------------------------------------------------
/client/client.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | /*
15 | Package client implements the class of a network client which can interact with a mix network.
16 | */
17 |
18 | package client
19 |
20 | import (
21 | "anonymous-messaging/clientCore"
22 | "anonymous-messaging/config"
23 | "anonymous-messaging/helpers"
24 | "anonymous-messaging/logging"
25 | "anonymous-messaging/networker"
26 |
27 | "github.com/protobuf/proto"
28 |
29 | "crypto/elliptic"
30 | "crypto/rand"
31 | "math"
32 | "math/big"
33 | "net"
34 | "time"
35 | )
36 |
37 | var (
38 | logLocal = logging.PackageLogger()
39 | loopCoverTrafficEnabled = true
40 | dropCoverTrafficEnabled = true
41 | )
42 |
43 | const (
44 | // the parameter of the exponential distribution which defines the rate of sending by client
45 | // the desiredRateParameter is the reciprocal of the expected value of the exponential distribution
46 | desiredRateParameter = 0.2
47 | loopRate = 0.1
48 | dropRate = 0.1
49 | // the rate at which clients are querying the provider for received packets. fetchRate value is the
50 | // parameter of an exponential distribution, and is the reciprocal of the expected value of the exp. distribution
51 | fetchRate = 0.01
52 | assignFlag = "\xA2"
53 | commFlag = "\xc6"
54 | tokenFlag = "xa9"
55 | pullFlag = "\xff"
56 | )
57 |
58 | type Client interface {
59 | networker.NetworkClient
60 | networker.NetworkServer
61 |
62 | Start() error
63 | SendMessage(message string, recipient config.ClientConfig) error
64 | ReadInNetworkFromPKI(pkiName string) error
65 | }
66 |
67 | type client struct {
68 | id string
69 | host string
70 | port string
71 |
72 | listener *net.TCPListener
73 | pkiDir string
74 |
75 | config config.ClientConfig
76 | token []byte
77 |
78 | outQueue chan []byte
79 | registrationDone chan bool
80 |
81 | *clientCore.CryptoClient
82 | }
83 |
84 | // Start function creates the loggers for capturing the info and error logs;
85 | // it reads the network and users information from the PKI database
86 | // and starts the listening server. Function returns an error
87 | // signaling whenever any operation was unsuccessful.
88 | func (c *client) Start() error {
89 |
90 | c.resolveAddressAndStartListening()
91 |
92 | c.outQueue = make(chan []byte)
93 | c.registrationDone = make(chan bool)
94 |
95 | err := c.ReadInNetworkFromPKI(c.pkiDir)
96 | if err != nil {
97 | logLocal.WithError(err).Error("Error during reading in network PKI")
98 | return err
99 | }
100 |
101 | go func() {
102 | for {
103 | select {
104 | case <-c.registrationDone:
105 | return
106 | default:
107 | err = c.sendRegisterMessageToProvider()
108 | if err != nil {
109 | logLocal.WithError(err).Error("Error during registration to provider", err)
110 | }
111 | time.Sleep(60 * time.Second)
112 | }
113 | }
114 | }()
115 |
116 | c.startListenerInNewRoutine()
117 | return nil
118 | }
119 |
120 | func (c *client) resolveAddressAndStartListening() error {
121 | addr, err := helpers.ResolveTCPAddress(c.host, c.port)
122 | if err != nil {
123 | return err
124 | }
125 |
126 | c.listener, err = net.ListenTCP("tcp", addr)
127 | if err != nil {
128 | return err
129 | }
130 | return nil
131 | }
132 |
133 | // SendMessage responsible for sending a real message. Takes as input the message string
134 | // and the public information about the destination.
135 | func (c *client) SendMessage(message string, recipient config.ClientConfig) error {
136 | packet, err := c.encodeMessage(message, recipient)
137 | if err != nil {
138 | logLocal.WithError(err).Error("Error in sending message - encode message returned error")
139 | return err
140 | }
141 | c.outQueue <- packet
142 | return nil
143 | }
144 |
145 | // encodeMessage encapsulates the given message into a sphinx packet destinated for recipient
146 | // and wraps with the flag pointing that it is the communication packet
147 | func (c *client) encodeMessage(message string, recipient config.ClientConfig) ([]byte, error) {
148 | sphinxPacket, err := c.EncodeMessage(message, recipient)
149 | if err != nil {
150 | logLocal.WithError(err).Error("Error in sending message - create sphinx packet returned an error")
151 | return nil, err
152 | }
153 |
154 | packetBytes, err := config.WrapWithFlag(commFlag, sphinxPacket)
155 | if err != nil {
156 | logLocal.WithError(err).Error("Error in sending message - wrap with flag returned an error")
157 | return nil, err
158 | }
159 | return packetBytes, nil
160 | }
161 |
162 | // Send opens a connection with selected network address
163 | // and send the passed packet. If connection failed or
164 | // the packet could not be send, an error is returned
165 | func (c *client) send(packet []byte, host string, port string) error {
166 |
167 | conn, err := net.Dial("tcp", host+":"+port)
168 |
169 | if err != nil {
170 | logLocal.WithError(err).Error("Error in send - dial returned an error")
171 | return err
172 | }
173 | defer conn.Close()
174 |
175 | _, err = conn.Write(packet)
176 | return err
177 | }
178 |
179 | // run opens the listener to start listening on clients host and port
180 | func (c *client) startListenerInNewRoutine() {
181 | defer c.listener.Close()
182 | finish := make(chan bool)
183 |
184 | go func() {
185 | logLocal.Infof("Listening on address %s", c.host+":"+c.port)
186 | c.listenForIncomingConnections()
187 | }()
188 |
189 | <-finish
190 | }
191 |
192 | // ListenForIncomingConnections responsible for running the listening process of the server;
193 | // The clients listener accepts incoming connections and
194 | // passes the incoming packets to the packet handler.
195 | // If the connection could not be accepted an error
196 | // is logged into the log files, but the function is not stopped
197 | func (c *client) listenForIncomingConnections() {
198 | for {
199 | conn, err := c.listener.Accept()
200 |
201 | if err != nil {
202 | logLocal.WithError(err).Error(err)
203 | } else {
204 | go c.handleConnection(conn)
205 | }
206 | }
207 | }
208 |
209 | // HandleConnection handles the received packets; it checks the flag of the
210 | // packet and schedules a corresponding process function;
211 | // The potential errors are logged into the log files.
212 | func (c *client) handleConnection(conn net.Conn) {
213 |
214 | buff := make([]byte, 1024)
215 | defer conn.Close()
216 |
217 | reqLen, err := conn.Read(buff)
218 | if err != nil {
219 | logLocal.WithError(err).Error("Error while reading incoming connection")
220 | panic(err)
221 | }
222 | var packet config.GeneralPacket
223 | err = proto.Unmarshal(buff[:reqLen], &packet)
224 | if err != nil {
225 | logLocal.WithError(err).Error("Error in unmarshal incoming packet")
226 | }
227 |
228 | switch packet.Flag {
229 | case tokenFlag:
230 | c.registerToken(packet.Data)
231 | go func() {
232 | err := c.controlOutQueue()
233 | if err != nil {
234 | logLocal.WithError(err).Panic("Error in the controller of the outgoing packets queue. Possible security threat.")
235 | }
236 | }()
237 |
238 | if loopCoverTrafficEnabled {
239 | c.turnOnLoopCoverTraffic()
240 | }
241 |
242 | if dropCoverTrafficEnabled {
243 | c.turnOnDropCoverTraffic()
244 | }
245 |
246 | go func() {
247 | c.controlMessagingFetching()
248 | }()
249 |
250 | case commFlag:
251 | _, err := c.processPacket(packet.Data)
252 | if err != nil {
253 | logLocal.WithError(err).Error("Error in processing received packet")
254 | }
255 | logLocal.Info("Received new message")
256 | default:
257 | logLocal.Info("Packet flag not recognised. Packet dropped.")
258 | }
259 | }
260 |
261 | // RegisterToken stores the authentication token received from the provider
262 | func (c *client) registerToken(token []byte) {
263 | c.token = token
264 | logLocal.Infof(" Registered token %s", c.token)
265 | c.registrationDone <- true
266 | }
267 |
268 | // ProcessPacket processes the received sphinx packet and returns the
269 | // encapsulated message or error in case the processing
270 | // was unsuccessful.
271 | func (c *client) processPacket(packet []byte) ([]byte, error) {
272 | logLocal.Info(" Processing packet")
273 | return packet, nil
274 | }
275 |
276 | // SendRegisterMessageToProvider allows the client to register with the selected provider.
277 | // The client sends a special assignment packet, with its public information, to the provider
278 | // or returns an error.
279 | func (c *client) sendRegisterMessageToProvider() error {
280 |
281 | logLocal.Info("Sending request to provider to register")
282 |
283 | confBytes, err := proto.Marshal(&c.config)
284 | if err != nil {
285 | logLocal.WithError(err).Error("Error in register provider - marshal of provider config returned an error")
286 | return err
287 | }
288 |
289 | pktBytes, err := config.WrapWithFlag(assignFlag, confBytes)
290 | if err != nil {
291 | logLocal.WithError(err).Error("Error in register provider - wrap with flag returned an error")
292 | return err
293 | }
294 |
295 | err = c.send(pktBytes, c.Provider.Host, c.Provider.Port)
296 | if err != nil {
297 | logLocal.WithError(err).Error("Error in register provider - send registration packet returned an error")
298 | return err
299 | }
300 | return nil
301 | }
302 |
303 | // GetMessagesFromProvider allows to fetch messages from the inbox stored by the
304 | // provider. The client sends a pull packet to the provider, along with
305 | // the authentication token. An error is returned if occurred.
306 | func (c *client) getMessagesFromProvider() error {
307 | pullRqs := config.PullRequest{ClientId: c.id, Token: c.token}
308 | pullRqsBytes, err := proto.Marshal(&pullRqs)
309 | if err != nil {
310 | logLocal.WithError(err).Error("Error in register provider - marshal of pull request returned an error")
311 | return err
312 | }
313 |
314 | pktBytes, err := config.WrapWithFlag(pullFlag, pullRqsBytes)
315 | if err != nil {
316 | logLocal.WithError(err).Error("Error in register provider - marshal of provider config returned an error")
317 | return err
318 | }
319 |
320 | err = c.send(pktBytes, c.Provider.Host, c.Provider.Port)
321 | if err != nil {
322 | return err
323 | }
324 |
325 | return nil
326 | }
327 |
328 | // controlOutQueue controls the outgoing queue of the client.
329 | // If a message awaits in the queue, it is sent. Otherwise a
330 | // drop cover message is sent instead.
331 | func (c *client) controlOutQueue() error {
332 | logLocal.Info("Queue controller started")
333 | for {
334 | select {
335 | case realPacket := <-c.outQueue:
336 | c.send(realPacket, c.Provider.Host, c.Provider.Port)
337 | logLocal.Info("Real packet was sent")
338 | default:
339 | dummyPacket, err := c.createDropCoverMessage()
340 | if err != nil {
341 | return err
342 | }
343 | c.send(dummyPacket, c.Provider.Host, c.Provider.Port)
344 | logLocal.Info("OutQueue empty. Dummy packet sent.")
345 | }
346 | err := delayBeforeContinute(desiredRateParameter)
347 | if err != nil {
348 | return err
349 | }
350 | }
351 | return nil
352 | }
353 |
354 | // controlMessagingFetching periodically at random sends a query to the provider
355 | // to fetch received messages
356 | func (c *client) controlMessagingFetching() {
357 | for {
358 | c.getMessagesFromProvider()
359 | logLocal.Info("Sent request to provider to fetch messages")
360 | err := delayBeforeContinute(fetchRate)
361 | if err != nil {
362 | logLocal.Error("Error in ControlMessagingFetching - generating random exp. value failed")
363 | }
364 | }
365 | }
366 |
367 | // CreateCoverMessage packs a dummy message into a Sphinx packet.
368 | // The dummy message is a loop message.
369 | func (c *client) createDropCoverMessage() ([]byte, error) {
370 | dummyLoad := "DummyPayloadMessage"
371 | randomRecipient, err := c.getRandomRecipient(c.Network.Clients)
372 | if err != nil {
373 | return nil, err
374 | }
375 | sphinxPacket, err := c.EncodeMessage(dummyLoad, randomRecipient)
376 | if err != nil {
377 | return nil, err
378 | }
379 |
380 | packetBytes, err := config.WrapWithFlag(commFlag, sphinxPacket)
381 | if err != nil {
382 | return nil, err
383 | }
384 | return packetBytes, nil
385 | }
386 |
387 | // getRandomRecipient picks a random client from the list of all available clients (stored by the client).
388 | // getRandomRecipient returns the selected client public configuration and an error
389 | func (c *client) getRandomRecipient(slice []config.ClientConfig) (config.ClientConfig, error) {
390 | randIdx, err := rand.Int(rand.Reader, big.NewInt(int64(len(slice))))
391 | if err != nil {
392 | return config.ClientConfig{}, err
393 | }
394 | return slice[randIdx.Int64()], nil
395 | }
396 |
397 | // createLoopCoverMessage packs a dummy loop message into
398 | // a sphinx packet. The loop message is destinated back to the sender
399 | // createLoopCoverMessage returns a byte representation of the encapsulated packet and an error
400 | func (c *client) createLoopCoverMessage() ([]byte, error) {
401 | loopLoad := "LoopCoverMessage"
402 | sphinxPacket, err := c.EncodeMessage(loopLoad, c.config)
403 | if err != nil {
404 | return nil, err
405 | }
406 | packetBytes, err := config.WrapWithFlag(commFlag, sphinxPacket)
407 | if err != nil {
408 | return nil, err
409 | }
410 | return packetBytes, nil
411 | }
412 |
413 | // runLoopCoverTrafficStream manages the stream of loop cover traffic.
414 | // In each stream iteration it sends a freshly created loop packet and
415 | // waits a random time before scheduling the next loop packet.
416 | func (c *client) runLoopCoverTrafficStream() error {
417 | logLocal.Info("Stream of loop cover traffic started")
418 | for {
419 | loopPacket, err := c.createLoopCoverMessage()
420 | if err != nil {
421 | return err
422 | }
423 | c.send(loopPacket, c.Provider.Host, c.Provider.Port)
424 | logLocal.Info("Loop message sent")
425 | err = delayBeforeContinute(loopRate)
426 | if err != nil {
427 | return err
428 | }
429 |
430 | }
431 | return nil
432 | }
433 |
434 | // runDropCoverTrafficStream manages the stream of drop cover traffic.
435 | // In each stream iteration it creates a fresh drop cover message destinated
436 | // to a randomly selected user in the network. The drop packet is sent
437 | // and the next stream call is scheduled after random time.
438 | func (c *client) runDropCoverTrafficStream() error {
439 | logLocal.Info("Stream of drop cover traffic started")
440 | for {
441 | dropPacket, err := c.createDropCoverMessage()
442 | if err != nil {
443 | return err
444 | }
445 | c.send(dropPacket, c.Provider.Host, c.Provider.Port)
446 | logLocal.Info("Drop packet sent")
447 | err = delayBeforeContinute(dropRate)
448 | if err != nil {
449 | return err
450 | }
451 | }
452 | return nil
453 | }
454 |
455 | func delayBeforeContinute(rateParam float64) error {
456 | delaySec, err := helpers.RandomExponential(rateParam)
457 | if err != nil {
458 | return err
459 | }
460 | time.Sleep(time.Duration(int64(delaySec*math.Pow10(9))) * time.Nanosecond)
461 | return nil
462 | }
463 |
464 | // turnOnLoopCoverTraffic starts the stream of loop cover traffic
465 | func (c *client) turnOnLoopCoverTraffic() {
466 | go func() {
467 | err := c.runLoopCoverTrafficStream()
468 | if err != nil {
469 | logLocal.WithError(err).Panic("Error in the controller of the loop cover traffic. Possible security threat.")
470 | }
471 | }()
472 | }
473 |
474 | // turnOnDropCoverTraffic starts the stream of drop cover traffic
475 | func (c *client) turnOnDropCoverTraffic() {
476 | go func() {
477 | err := c.runDropCoverTrafficStream()
478 | if err != nil {
479 | logLocal.WithError(err).Panic("Error in the controller of the drop cover traffic. Possible security threat.")
480 | }
481 | }()
482 | }
483 |
484 | // ReadInNetworkFromPKI reads in the public information about active mixes
485 | // from the PKI database and stores them locally. In case
486 | // the connection or fetching data from the PKI went wrong,
487 | // an error is returned.
488 | func (c *client) ReadInNetworkFromPKI(pkiName string) error {
489 | logLocal.Infof("Reading network information from the PKI: %s", pkiName)
490 |
491 | mixes, err := helpers.GetMixesPKI(pkiName)
492 | if err != nil {
493 | logLocal.WithError(err).Error("Error while reading mixes from PKI")
494 | return err
495 | }
496 | c.Network.Mixes = mixes
497 |
498 | clients, err := helpers.GetClientPKI(pkiName)
499 | if err != nil {
500 | logLocal.WithError(err).Error("Error while reading clients from PKI")
501 | return err
502 | }
503 | c.Network.Clients = clients
504 |
505 | logLocal.Info("Network information uploaded")
506 | return nil
507 | }
508 |
509 | // The constructor function to create an new client object.
510 | // Function returns a new client object or an error, if occurred.
511 | func NewClient(id, host, port string, pubKey []byte, prvKey []byte, pkiDir string, provider config.MixConfig) (*client, error) {
512 | core := clientCore.NewCryptoClient(pubKey, prvKey, elliptic.P224(), provider, clientCore.NetworkPKI{})
513 | c := client{id: id, host: host, port: port, CryptoClient: core, pkiDir: pkiDir}
514 | c.config = config.ClientConfig{Id: c.id, Host: c.host, Port: c.port, PubKey: c.GetPublicKey(), Provider: &c.Provider}
515 |
516 | configBytes, err := proto.Marshal(&c.config)
517 |
518 | if err != nil {
519 | return nil, err
520 | }
521 | err = helpers.AddToDatabase(pkiDir, "Pki", c.id, "Client", configBytes)
522 | if err != nil {
523 | return nil, err
524 | }
525 |
526 | return &c, nil
527 | }
528 |
529 | // NewTestClient constructs a client object, which can be used for testing. The object contains the crypto core
530 | // and the top-level of client, but does not involve networking and starting a listener.
531 | func NewTestClient(id, host, port string, pubKey []byte, prvKey []byte, pkiDir string, provider config.MixConfig) (*client, error) {
532 | core := clientCore.NewCryptoClient(pubKey, prvKey, elliptic.P224(), provider, clientCore.NetworkPKI{})
533 | c := client{id: id, host: host, port: port, CryptoClient: core, pkiDir: pkiDir}
534 | c.config = config.ClientConfig{Id: c.id, Host: c.host, Port: c.port, PubKey: c.GetPublicKey(), Provider: &c.Provider}
535 |
536 | return &c, nil
537 | }
538 |
--------------------------------------------------------------------------------
/client/client_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package client
16 |
17 | import (
18 | "anonymous-messaging/config"
19 | sphinx "anonymous-messaging/sphinx"
20 |
21 | "github.com/jmoiron/sqlx"
22 | "github.com/protobuf/proto"
23 | "github.com/stretchr/testify/assert"
24 |
25 | "fmt"
26 | "os"
27 | "strconv"
28 | "testing"
29 | )
30 |
31 | var providerPubs config.MixConfig
32 | var testPacket sphinx.SphinxPacket
33 | var testMixSet []config.MixConfig
34 | var testClientSet []config.ClientConfig
35 |
36 | const (
37 | pkiDir = "testDatabase.db"
38 | )
39 |
40 | func setupTestDatabase() (*sqlx.DB, error) {
41 |
42 | db, err := sqlx.Connect("sqlite3", pkiDir)
43 | if err != nil {
44 | return nil, err
45 | }
46 |
47 | query := `CREATE TABLE Pki (
48 | idx INTEGER PRIMARY KEY,
49 | Id TEXT,
50 | Typ TEXT,
51 | Config BLOB);`
52 |
53 | _, err = db.Exec(query)
54 | if err != nil {
55 | return nil, err
56 | }
57 |
58 | return db, err
59 | }
60 |
61 | func SetupTestMixesInDatabase(t *testing.T) error {
62 | clean()
63 |
64 | db, err := setupTestDatabase()
65 | if err != nil {
66 | t.Fatal(err)
67 | }
68 |
69 | insertQuery := `INSERT INTO Pki (Id, Typ, Config) VALUES (?, ?, ?)`
70 |
71 | for i := 0; i < 10; i++ {
72 | pub, _, err := sphinx.GenerateKeyPair()
73 | if err != nil {
74 | return err
75 | }
76 | m := config.MixConfig{Id: fmt.Sprintf("Mix%d", i),
77 | Host: "localhost",
78 | Port: strconv.Itoa(9980 + i),
79 | PubKey: pub}
80 | mBytes, err := proto.Marshal(&m)
81 | if err != nil {
82 | return err
83 | }
84 | _, err = db.Exec(insertQuery, m.Id, "Mix", mBytes)
85 | if err != nil {
86 | return err
87 | }
88 | testMixSet = append(testMixSet, m)
89 | }
90 | return nil
91 | }
92 |
93 | func SetupTestClientsInDatabase(t *testing.T) {
94 | clean()
95 |
96 | db, err := setupTestDatabase()
97 | if err != nil {
98 | t.Fatal(err)
99 | }
100 |
101 | insertQuery := `INSERT INTO Pki (Id, Typ, Config) VALUES (?, ?, ?)`
102 |
103 | for i := 0; i < 10; i++ {
104 | pub, _, err := sphinx.GenerateKeyPair()
105 | if err != nil {
106 | t.Fatal(err)
107 | }
108 | c := config.ClientConfig{Id: fmt.Sprintf("Client%d", i),
109 | Host: "localhost",
110 | Port: strconv.Itoa(9980 + i),
111 | PubKey: pub}
112 | cBytes, err := proto.Marshal(&c)
113 | if err != nil {
114 | t.Fatal(err)
115 | }
116 | _, err = db.Exec(insertQuery, c.Id, "Client", cBytes)
117 | if err != nil {
118 | t.Fatal(err)
119 | }
120 | testClientSet = append(testClientSet, c)
121 | }
122 | }
123 |
124 | func SetupTestClient(t *testing.T) *client {
125 | pubP, _, err := sphinx.GenerateKeyPair()
126 | if err != nil {
127 | t.Fatal(err)
128 | }
129 | providerPubs = config.MixConfig{Id: "Provider", Host: "localhost", Port: "9995", PubKey: pubP}
130 |
131 | pubC, privC, err := sphinx.GenerateKeyPair()
132 | if err != nil {
133 | t.Fatal(err)
134 | }
135 | client, err := NewTestClient("Client", "localhost", "3332", pubC, privC, pkiDir, providerPubs)
136 | if err != nil {
137 | t.Fatal(err)
138 | }
139 |
140 | return client
141 | }
142 |
143 | func clean() error {
144 | if _, err := os.Stat(pkiDir); err == nil {
145 | err := os.Remove(pkiDir)
146 | if err != nil {
147 | return err
148 | }
149 | }
150 | return nil
151 | }
152 |
153 | func TestMain(m *testing.M) {
154 |
155 | defer clean()
156 |
157 | code := m.Run()
158 | clean()
159 | os.Exit(code)
160 |
161 | }
162 |
163 | func TestClient_GetMessagesFromProvider(t *testing.T) {
164 |
165 | }
166 |
167 | // TODO: Fix this test
168 | //func TestClient_RegisterToken_Pass(t *testing.T) {
169 | //client := SetupTestClient(t)
170 | //client.RegisterToken([]byte("TestToken"))
171 | //r := <- client.registrationDone
172 | //assert.True(t, r)
173 | //assert.Equal(t, []byte("TestToken"), client.token, "Client should register only given token")
174 | //}
175 |
176 | //func TestClient_RegisterToken_Fail(t *testing.T) {
177 | // client := SetupTestClient(t)
178 | // client.RegisterToken([]byte("TestToken"))
179 | // assert.NotEqual(t, []byte("WrongToken"), client.token, "Client should register only the given token")
180 | //}
181 |
182 | // TODO: Fix this test
183 | func TestClient_RegisterToProvider(t *testing.T) {
184 |
185 | }
186 |
187 | // TODO: Fix this test
188 | //func TestClient_SendMessage(t *testing.T) {
189 | // pubP, _, err := sphinx.GenerateKeyPair()
190 | // if err != nil{
191 | // t.Fatal(err)
192 | // }
193 | // providerPubs = config.MixConfig{Id: "Provider", Host: "localhost", Port: "9995", PubKey: pubP}
194 | //
195 | // pubR, _, err := sphinx.GenerateKeyPair()
196 | // if err != nil{
197 | // t.Fatal(err)
198 | // }
199 | // recipient := config.ClientConfig{Id:"Recipient", Host:"localhost", Port:"9999", PubKey: pubR, Provider: &providerPubs}
200 | // fmt.Println(recipient)
201 | // pubM1, _, err := sphinx.GenerateKeyPair()
202 | // if err != nil{
203 | // t.Fatal(err)
204 | // }
205 | // pubM2, _, err := sphinx.GenerateKeyPair()
206 | // if err != nil{
207 | // t.Fatal(err)
208 | // }
209 | // m1 := config.MixConfig{Id: "Mix1", Host: "localhost", Port: strconv.Itoa(9980), PubKey: pubM1}
210 | // m2 := config.MixConfig{Id: "Mix2", Host: "localhost", Port: strconv.Itoa(9981), PubKey: pubM2}
211 | //
212 | // client := SetupTestClient(t)
213 | // client.ActiveMixes = []config.MixConfig{m1, m2}
214 | //
215 | // addr, err := helpers.ResolveTCPAddress(client.Host, client.Port)
216 | // if err != nil {
217 | // t.Fatal(err)
218 | // }
219 | //
220 | // client.Listener, err = net.ListenTCP("tcp", addr)
221 | // if err != nil {
222 | // t.Fatal(err)
223 | // }
224 | //
225 | // err = client.SendMessage("TestMessage", recipient)
226 | // if err != nil{
227 | // t.Fatal(err)
228 | // }
229 | // err = client.Listener.Close()
230 | // if err != nil{
231 | // t.Fatal(err)
232 | // }
233 | //}
234 |
235 | // TODO: Fix this test
236 | func TestClient_ProcessPacket(t *testing.T) {
237 |
238 | }
239 |
240 | func TestClient_ReadInMixnetPKI(t *testing.T) {
241 | clean()
242 | SetupTestMixesInDatabase(t)
243 |
244 | client := SetupTestClient(t)
245 | err := client.ReadInNetworkFromPKI("testDatabase.db")
246 | if err != nil {
247 | t.Fatal(err)
248 | }
249 |
250 | assert.Equal(t, len(testMixSet), len(client.Network.Mixes))
251 | assert.Equal(t, testMixSet, client.Network.Mixes)
252 |
253 | }
254 |
--------------------------------------------------------------------------------
/clientCore/mixClient.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package clientCore implements all the necessary functions for the mix client, i.e., the core of the client
17 | which allows to process the received cryptographic packets.
18 | */
19 |
20 | package clientCore
21 |
22 | import (
23 | "anonymous-messaging/config"
24 | "anonymous-messaging/helpers"
25 | "anonymous-messaging/logging"
26 | sphinx "anonymous-messaging/sphinx"
27 |
28 | "github.com/protobuf/proto"
29 |
30 | "crypto/elliptic"
31 | "errors"
32 | )
33 |
34 | var logLocal = logging.PackageLogger()
35 |
36 | type NetworkPKI struct {
37 | Mixes []config.MixConfig
38 | Clients []config.ClientConfig
39 | }
40 |
41 | type MixClient interface {
42 | EncodeIntoSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error)
43 | DecodeSphinxPacket(packet sphinx.SphinxPacket) (sphinx.SphinxPacket, error)
44 | GetPublicKey() []byte
45 | }
46 |
47 | type CryptoClient struct {
48 | pubKey []byte
49 | prvKey []byte
50 | curve elliptic.Curve
51 | Provider config.MixConfig
52 | Network NetworkPKI
53 | }
54 |
55 | const (
56 | desiredRateParameter = 5
57 | pathLength = 2
58 | )
59 |
60 | // CreateSphinxPacket responsible for sending a real message. Takes as input the message string
61 | // and the public information about the destination.
62 | // The function generates a random path and a set of random values from exponential distribution.
63 | // Given those values it triggers the encode function, which packs the message into the
64 | // sphinx cryptographic packet format. Next, the encoded packet is combined with a
65 | // flag signaling that this is a usual network packet, and passed to be send.
66 | // The function returns an error if any issues occurred.
67 | func (c *CryptoClient) createSphinxPacket(message string, recipient config.ClientConfig) ([]byte, error) {
68 |
69 | path, err := c.buildPath(recipient)
70 | if err != nil {
71 | logLocal.WithError(err).Error("Error in CreateSphinxPacket - generating random path failed")
72 | return nil, err
73 | }
74 |
75 | delays, err := c.generateDelaySequence(desiredRateParameter, path.Len())
76 | if err != nil {
77 | logLocal.WithError(err).Error("Error in CreateSphinxPacket - generating sequence of delays failed")
78 | return nil, err
79 | }
80 |
81 | sphinxPacket, err := sphinx.PackForwardMessage(c.curve, path, delays, message)
82 | if err != nil {
83 | logLocal.WithError(err).Error("Error in CreateSphinxPacket - the pack procedure failed")
84 | return nil, err
85 | }
86 |
87 | return proto.Marshal(&sphinxPacket)
88 | }
89 |
90 | // buildPath builds a path containing the sender's provider,
91 | // a sequence (of length pre-defined in a config file) of randomly
92 | // selected mixes and the recipient's provider
93 | func (c *CryptoClient) buildPath(recipient config.ClientConfig) (config.E2EPath, error) {
94 | mixSeq, err := c.getRandomMixSequence(c.Network.Mixes, pathLength)
95 | if err != nil {
96 | logLocal.WithError(err).Error("Error in buildPath - generating random mix path failed")
97 | return config.E2EPath{}, err
98 | }
99 | path := config.E2EPath{IngressProvider: c.Provider, Mixes: mixSeq, EgressProvider: *recipient.Provider, Recipient: recipient}
100 | return path, nil
101 | }
102 |
103 | // getRandomMixSequence generates a random sequence of given length from all possible mixes.
104 | // If the list of all active mixes is empty or the given length is larger than the set of active mixes,
105 | // an error is returned.
106 | func (c *CryptoClient) getRandomMixSequence(mixes []config.MixConfig, length int) ([]config.MixConfig, error) {
107 | if len(mixes) == 0 || mixes == nil {
108 | return nil, errors.New("cannot take a mix sequence from an empty list")
109 | }
110 | if length > len(mixes) {
111 | return mixes, nil
112 | } else {
113 | randomSeq, err := helpers.RandomSample(mixes, length)
114 | if err != nil {
115 | logLocal.WithError(err).Error("Error in getRandomMixSequence - sampling procedure failed")
116 | return nil, err
117 | }
118 | return randomSeq, nil
119 | }
120 | }
121 |
122 | // generateDelaySequence generates a given length sequence of float64 values. Values are generated
123 | // following the exponential distribution. generateDelaySequence returnes a sequence or an error
124 | // if any of the values could not be generate.
125 | func (c *CryptoClient) generateDelaySequence(desiredRateParameter float64, length int) ([]float64, error) {
126 | var delays []float64
127 | for i := 0; i < length; i++ {
128 | d, err := helpers.RandomExponential(desiredRateParameter)
129 | if err != nil {
130 | logLocal.WithError(err).Error("Error in generateDelaySequence - generating random exponential sample failed")
131 | return nil, err
132 | }
133 | delays = append(delays, d)
134 | }
135 | return delays, nil
136 | }
137 |
138 | // EncodeMessage encodes given message into the Sphinx packet format. EncodeMessage takes as inputs
139 | // the message and the recipient's public configuration.
140 | // EncodeMessage returns the byte representation of the packet or an error if the packet could not be created.
141 | func (c *CryptoClient) EncodeMessage(message string, recipient config.ClientConfig) ([]byte, error) {
142 |
143 | packet, err := c.createSphinxPacket(message, recipient)
144 | if err != nil {
145 | logLocal.WithError(err).Error("Error in EncodeMessage - the pack procedure failed")
146 | return nil, err
147 | }
148 | return packet, err
149 | }
150 |
151 | // DecodeMessage decodes the received sphinx packet.
152 | // TODO: this function is finished yet.
153 | func (c *CryptoClient) DecodeMessage(packet sphinx.SphinxPacket) (sphinx.SphinxPacket, error) {
154 | return packet, nil
155 | }
156 |
157 | func (c *CryptoClient) GetPublicKey() []byte {
158 | return c.pubKey
159 | }
160 |
161 | func NewCryptoClient(pubKey, privKey []byte, curve elliptic.Curve, provider config.MixConfig, network NetworkPKI) *CryptoClient {
162 | return &CryptoClient{pubKey: pubKey, prvKey: privKey, curve: curve, Provider: provider, Network: network}
163 | }
164 |
--------------------------------------------------------------------------------
/clientCore/mixClient_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package clientCore
16 |
17 | import (
18 | "anonymous-messaging/config"
19 | sphinx "anonymous-messaging/sphinx"
20 |
21 | "github.com/stretchr/testify/assert"
22 |
23 | "crypto/elliptic"
24 | "errors"
25 | "fmt"
26 | "os"
27 | "reflect"
28 | "strconv"
29 | "testing"
30 | )
31 |
32 | var client *CryptoClient
33 | var path config.E2EPath
34 | var mixes []config.MixConfig
35 |
36 | func Setup() error {
37 | for i := 0; i < 10; i++ {
38 | pub, _, err := sphinx.GenerateKeyPair()
39 | if err != nil {
40 | return err
41 | }
42 | mixes = append(mixes, config.NewMixConfig(fmt.Sprintf("Mix%d", i), "localhost", strconv.Itoa(3330+i), pub))
43 | }
44 |
45 | // Create a mixClient
46 | pubC, privC, err := sphinx.GenerateKeyPair()
47 | if err != nil {
48 | return err
49 | }
50 | client = NewCryptoClient(pubC, privC, elliptic.P224(), config.MixConfig{}, NetworkPKI{})
51 |
52 | //Client a pair of mix configs, a single provider and a recipient
53 | pub1, _, err := sphinx.GenerateKeyPair()
54 | if err != nil {
55 | return err
56 | }
57 |
58 | pub2, _, err := sphinx.GenerateKeyPair()
59 | if err != nil {
60 | return err
61 | }
62 |
63 | m1 := config.MixConfig{Id: "Mix1", Host: "localhost", Port: "3330", PubKey: pub1}
64 | m2 := config.MixConfig{Id: "Mix2", Host: "localhost", Port: "3331", PubKey: pub2}
65 |
66 | client.Network = NetworkPKI{}
67 | client.Network.Mixes = []config.MixConfig{m1, m2}
68 |
69 | return nil
70 | }
71 |
72 | func TestMain(m *testing.M) {
73 |
74 | err := Setup()
75 | if err != nil {
76 | panic(m)
77 |
78 | }
79 | os.Exit(m.Run())
80 | }
81 |
82 | func TestCryptoClient_EncodeMessage(t *testing.T) {
83 |
84 | pubP, _, err := sphinx.GenerateKeyPair()
85 | if err != nil {
86 | t.Fatal(err)
87 | }
88 | provider := config.MixConfig{Id: "Provider", Host: "localhost", Port: "3331", PubKey: pubP}
89 |
90 | pubD, _, err := sphinx.GenerateKeyPair()
91 | if err != nil {
92 | t.Fatal(err)
93 | }
94 | recipient := config.ClientConfig{Id: "Recipient", Host: "localhost", Port: "9999", PubKey: pubD, Provider: &provider}
95 | client.Provider = provider
96 |
97 | encoded, err := client.EncodeMessage("Hello world", recipient)
98 | if err != nil {
99 | t.Fatal(err)
100 | }
101 |
102 | assert.Equal(t, reflect.TypeOf([]byte{}), reflect.TypeOf(encoded))
103 |
104 | }
105 |
106 | func TestCryptoClient_DecodeMessage(t *testing.T) {
107 | packet := sphinx.SphinxPacket{Hdr: &sphinx.Header{}, Pld: []byte("Message")}
108 |
109 | decoded, err := client.DecodeMessage(packet)
110 | if err != nil {
111 | t.Fatal(err)
112 | }
113 | expected := packet
114 | assert.Equal(t, expected, decoded)
115 | }
116 |
117 | func TestCryptoClient_GenerateDelaySequence_Pass(t *testing.T) {
118 | delays, err := client.generateDelaySequence(100, 5)
119 | if err != nil {
120 | t.Fatal(err)
121 | }
122 | assert.Equal(t, len(delays), 5, "The length of returned delays should be equal to theinput length")
123 | assert.Equal(t, reflect.TypeOf([]float64{}), reflect.TypeOf(delays), "The delays should be in float64 type")
124 | }
125 |
126 | func TestCryptoClient_GenerateDelaySequence_Fail(t *testing.T) {
127 | _, err := client.generateDelaySequence(0, 5)
128 | assert.EqualError(t, errors.New("the parameter of exponential distribution has to be larger than zero"), err.Error(), "")
129 | }
130 |
131 | func Test_GetRandomMixSequence_TooFewMixes(t *testing.T) {
132 |
133 | sequence, err := client.getRandomMixSequence(mixes, 20)
134 | if err != nil {
135 | t.Fatal(err)
136 | }
137 | assert.Equal(t, 10, len(sequence), "When the given length is larger than the number of active nodes, the path should be "+
138 | "the sequence of all active mixes")
139 | }
140 |
141 | func Test_GetRandomMixSequence_MoreMixes(t *testing.T) {
142 |
143 | sequence, err := client.getRandomMixSequence(mixes, 3)
144 | if err != nil {
145 | t.Fatal(err)
146 | }
147 | assert.Equal(t, 3, len(sequence), "When the given length is larger than the number of active nodes, the path should be "+
148 | "the sequence of all active mixes")
149 |
150 | }
151 |
152 | func Test_GetRandomMixSequence_FailEmptyList(t *testing.T) {
153 | _, err := client.getRandomMixSequence([]config.MixConfig{}, 6)
154 | assert.EqualError(t, errors.New("cannot take a mix sequence from an empty list"), err.Error(), "")
155 | }
156 |
157 | func Test_GetRandomMixSequence_FailNonList(t *testing.T) {
158 | _, err := client.getRandomMixSequence(nil, 6)
159 | assert.EqualError(t, errors.New("cannot take a mix sequence from an empty list"), err.Error(), "")
160 | }
161 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package config implements struct for easy processing and storing of all public information
17 | of the network participants.
18 | */
19 |
20 | package config
21 |
22 | import (
23 | "github.com/protobuf/proto"
24 | )
25 |
26 | func NewMixConfig(mixId, host, port string, pubKey []byte) MixConfig {
27 | MixConfig := MixConfig{Id: mixId, Host: host, Port: port, PubKey: pubKey}
28 | return MixConfig
29 | }
30 |
31 | func NewClientConfig(clientId, host, port string, pubKey []byte, providerInfo MixConfig) ClientConfig {
32 | client := ClientConfig{Id: clientId, Host: host, Port: port, PubKey: pubKey, Provider: &providerInfo}
33 | return client
34 | }
35 |
36 | /*
37 | WrapWithFlag packs the given byte information together with a specified flag into the
38 | packet.
39 | */
40 | func WrapWithFlag(flag string, data []byte) ([]byte, error) {
41 | m := GeneralPacket{Flag: flag, Data: data}
42 | mBytes, err := proto.Marshal(&m)
43 | if err != nil {
44 | return nil, err
45 | }
46 | return mBytes, nil
47 | }
48 |
49 | type E2EPath struct {
50 | IngressProvider MixConfig
51 | Mixes []MixConfig
52 | EgressProvider MixConfig
53 | Recipient ClientConfig
54 | }
55 |
56 | func (p *E2EPath) Len() int {
57 | return 3 + len(p.Mixes)
58 | }
59 |
--------------------------------------------------------------------------------
/config/structs.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package config;
3 |
4 | message MixConfig {
5 | string Id = 1;
6 | string Host = 2;
7 | string Port = 3;
8 | bytes PubKey = 4;
9 | }
10 |
11 | message ClientConfig {
12 | string Id = 1;
13 | string Host = 2;
14 | string Port = 3;
15 | bytes PubKey = 4;
16 | MixConfig Provider = 5;
17 | }
18 |
19 | message GeneralPacket {
20 | string Flag = 1;
21 | bytes Data = 2;
22 | }
23 |
24 | message PullRequest {
25 | string ClientId = 1;
26 | bytes Token = 2;
27 | }
28 |
--------------------------------------------------------------------------------
/helpers/utilities.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package helpers implements all useful functions which are used in the code of anonymous messaging system.
17 | */
18 |
19 | package helpers
20 |
21 | import (
22 | "anonymous-messaging/config"
23 | "anonymous-messaging/pki"
24 |
25 | "github.com/protobuf/proto"
26 |
27 | "crypto/sha256"
28 | "errors"
29 | "math/rand"
30 | "net"
31 | "os"
32 | "time"
33 | )
34 |
35 | func Permute(slice []config.MixConfig) ([]config.MixConfig, error) {
36 | if len(slice) == 0 {
37 | return nil, errors.New(" cannot permute an empty list of mixes")
38 | }
39 |
40 | rand.Seed(time.Now().UTC().UnixNano())
41 | permutedData := make([]config.MixConfig, len(slice))
42 | permutation := rand.Perm(len(slice))
43 | for i, v := range permutation {
44 | permutedData[v] = slice[i]
45 | }
46 | return permutedData, nil
47 | }
48 |
49 | func RandomSample(slice []config.MixConfig, length int) ([]config.MixConfig, error) {
50 | if len(slice) < length {
51 | return nil, errors.New(" cannot take a sample larger than the given list")
52 | }
53 |
54 | permuted, err := Permute(slice)
55 | if err != nil {
56 | return nil, err
57 | }
58 |
59 | return permuted[:length], err
60 | }
61 |
62 | func RandomExponential(expParam float64) (float64, error) {
63 | rand.Seed(time.Now().UTC().UnixNano())
64 | if expParam <= 0.0 {
65 | return 0.0, errors.New("the parameter of exponential distribution has to be larger than zero")
66 | }
67 | return rand.ExpFloat64() / expParam, nil
68 | }
69 |
70 | func ResolveTCPAddress(host, port string) (*net.TCPAddr, error) {
71 | addr, err := net.ResolveTCPAddr("tcp", host+":"+port)
72 | if err != nil {
73 | return nil, err
74 | }
75 | return addr, nil
76 | }
77 |
78 | // TO DO: This function is useless; remove it and change the code
79 |
80 | func AddToDatabase(pkiPath string, tableName, id, typ string, config []byte) error {
81 | db, err := pki.OpenDatabase(pkiPath, "sqlite3")
82 | if err != nil {
83 | return err
84 | }
85 | defer db.Close()
86 |
87 | err = pki.InsertIntoTable(db, tableName, id, typ, config)
88 | if err != nil {
89 | return err
90 | }
91 | return nil
92 | }
93 |
94 | func DirExists(path string) (bool, error) {
95 | _, err := os.Stat(path)
96 | if os.IsNotExist(err) {
97 | return false, nil
98 | }
99 | if err == nil {
100 | return true, nil
101 | }
102 | return false, err
103 | }
104 |
105 | // SHA256 computes the hash value of a given argument using SHA256 algorithm.
106 | func SHA256(arg []byte) []byte {
107 | h := sha256.New()
108 | h.Write([]byte(arg))
109 | return h.Sum(nil)
110 | }
111 |
112 | func GetMixesPKI(pkiDir string) ([]config.MixConfig, error) {
113 | var mixes []config.MixConfig
114 |
115 | db, err := pki.OpenDatabase(pkiDir, "sqlite3")
116 | if err != nil {
117 | return nil, err
118 | }
119 |
120 | recordsMixes, err := pki.QueryDatabase(db, "Pki", "Mix")
121 | if err != nil {
122 | return nil, err
123 | }
124 |
125 | for recordsMixes.Next() {
126 | result := make(map[string]interface{})
127 | err := recordsMixes.MapScan(result)
128 | if err != nil {
129 | return nil, err
130 | }
131 |
132 | var mixConfig config.MixConfig
133 | err = proto.Unmarshal(result["Config"].([]byte), &mixConfig)
134 | if err != nil {
135 | return nil, err
136 | }
137 | mixes = append(mixes, mixConfig)
138 | }
139 |
140 | return mixes, nil
141 | }
142 |
143 | func GetClientPKI(pkiDir string) ([]config.ClientConfig, error) {
144 | var clients []config.ClientConfig
145 |
146 | db, err := pki.OpenDatabase(pkiDir, "sqlite3")
147 | if err != nil {
148 | return nil, err
149 | }
150 |
151 | recordsClients, err := pki.QueryDatabase(db, "Pki", "Client")
152 | if err != nil {
153 | return nil, err
154 | }
155 | for recordsClients.Next() {
156 | result := make(map[string]interface{})
157 | err := recordsClients.MapScan(result)
158 |
159 | if err != nil {
160 | return nil, err
161 | }
162 |
163 | var clientConfig config.ClientConfig
164 | err = proto.Unmarshal(result["Config"].([]byte), &clientConfig)
165 | if err != nil {
166 | return nil, err
167 | }
168 |
169 | clients = append(clients, clientConfig)
170 | }
171 | return clients, nil
172 | }
173 |
174 | func GetLocalIP() (string, error) {
175 | ifaces, err := net.Interfaces()
176 | if err != nil {
177 | return "", err
178 | }
179 |
180 | for _, iface := range ifaces {
181 | addrs, err := iface.Addrs()
182 | if err != nil {
183 | return "", err
184 | }
185 | for _, addr := range addrs {
186 | var ip net.IP
187 | switch v := addr.(type) {
188 | case *net.IPNet:
189 | ip = v.IP
190 | case *net.IPAddr:
191 | ip = v.IP
192 | }
193 | if ip == nil || ip.IsLoopback() {
194 | continue
195 | }
196 | ip = ip.To4()
197 | if ip == nil {
198 | continue // not an ipv4 address
199 | }
200 | return ip.String(), nil
201 | }
202 | }
203 |
204 | return "", errors.New("Couldn't find the valid IP. Check internet connection.")
205 | }
206 |
--------------------------------------------------------------------------------
/helpers/utilities_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package helpers
16 |
17 | import (
18 | "anonymous-messaging/config"
19 |
20 | "github.com/stretchr/testify/assert"
21 |
22 | "errors"
23 | "fmt"
24 | "os"
25 | "reflect"
26 | "sort"
27 | "testing"
28 | )
29 |
30 | var mixes []config.MixConfig
31 | var testDir string
32 |
33 | // ById implements the sort interface and sorts based on the id of the nodes
34 | type ById []config.MixConfig
35 |
36 | func (v ById) Len() int { return len(v) }
37 | func (v ById) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
38 | func (v ById) Less(i, j int) bool { return v[i].Id < v[j].Id }
39 |
40 | func Setup() error {
41 | for i := 0; i < 10; i++ {
42 | mixes = append(mixes, config.MixConfig{Id: fmt.Sprintf("Mix%d", i),
43 | Host: fmt.Sprintf("Host%d", i),
44 | Port: fmt.Sprintf("Port%d", i),
45 | PubKey: nil})
46 | }
47 |
48 | currDir, err := os.Getwd()
49 | if err != nil {
50 | return err
51 | }
52 | testDir = currDir + "/test_path"
53 | return nil
54 | }
55 |
56 | func Clean() error {
57 | err := os.RemoveAll(testDir)
58 | if err != nil {
59 | return err
60 | }
61 | return nil
62 | }
63 |
64 | func TestMain(m *testing.M) {
65 | err := Setup()
66 | if err != nil {
67 | panic(m)
68 | }
69 |
70 | code := m.Run()
71 |
72 | err = Clean()
73 | if err != nil {
74 | panic(m)
75 | }
76 | os.Exit(code)
77 | }
78 |
79 | func TestDirExists_Pass(t *testing.T) {
80 |
81 | err := os.Mkdir(testDir, 755)
82 | if err != nil {
83 | t.Fatal(err)
84 | }
85 |
86 | exists, err := DirExists(testDir)
87 | if err != nil {
88 | t.Fatal(err)
89 | }
90 | assert.Equal(t, true, exists, " DirExists should return false for a non existing directory")
91 | }
92 |
93 | func TestDirExists_Fail(t *testing.T) {
94 | exists, err := DirExists("completely_random_directory/xxx")
95 | if err != nil {
96 | t.Fatal(err)
97 | }
98 | assert.Equal(t, false, exists, " DirExists should return false for a non existing directory")
99 | }
100 |
101 | func TestPermute_Pass(t *testing.T) {
102 | permuted, err := Permute(mixes)
103 | if err != nil {
104 | t.Fatal(err)
105 | }
106 | assert.Equal(t, len(mixes), len(permuted), " Permute should return a permutation of a given slice, hence the lengths should be equal")
107 | sort.Sort(ById(mixes))
108 | sort.Sort(ById(permuted))
109 | assert.True(t, reflect.DeepEqual(mixes, permuted))
110 |
111 | }
112 |
113 | func TestPermute_Fail(t *testing.T) {
114 | _, err := Permute([]config.MixConfig{})
115 | assert.EqualError(t, errors.New(" cannot permute an empty list of mixes"), err.Error(), " Permute should return an error for an empty slice")
116 | }
117 |
118 | func TestRandomExponential_Pass(t *testing.T) {
119 | val, err := RandomExponential(5.0)
120 | if err != nil {
121 | t.Fatal(err)
122 | }
123 | assert.Equal(t, reflect.Float64, reflect.TypeOf(val).Kind(), " RandomExponential should return a single float64 value")
124 | }
125 |
126 | func TestRandomExponential_Fail_ZeroParam(t *testing.T) {
127 | _, err := RandomExponential(0.0)
128 | assert.EqualError(t, errors.New("the parameter of exponential distribution has to be larger than zero"), err.Error(), " RandomExponential should return an error if the given parameter is non-positive")
129 |
130 | }
131 |
132 | func TestRandomExponential_Fail_NegativeParam(t *testing.T) {
133 | _, err := RandomExponential(-1.0)
134 | assert.EqualError(t, errors.New("the parameter of exponential distribution has to be larger than zero"), err.Error(), " RandomExponential should return an error if the given parameter is non-positive")
135 | }
136 |
137 | func TestRandomSample_Pass_SmallerLen(t *testing.T) {
138 | sample, err := RandomSample(mixes, 5)
139 | if err != nil {
140 | t.Fatal(err)
141 | }
142 | assert.Equal(t, 5, len(sample), " RandomSample should return a sample of given size")
143 | }
144 |
145 | func TestRandomSample_Pass_EqualLen(t *testing.T) {
146 | sample, err := RandomSample(mixes, 5)
147 | if err != nil {
148 | t.Fatal(err)
149 | }
150 | assert.Equal(t, 5, len(sample), " RandomSample should return a sample of given size")
151 | }
152 |
153 | func TestRandomSample_Fail(t *testing.T) {
154 | _, err := RandomSample(mixes, 20)
155 | assert.EqualError(t, errors.New(" cannot take a sample larger than the given list"), err.Error(), " RandomSample cannot take a sample larger than the given slice")
156 | }
157 |
158 | func TestResolveTCPAddress(t *testing.T) {
159 | // TO DO: How this should be tested ? And should it even be tested it if it uses a build in function?
160 | }
161 |
--------------------------------------------------------------------------------
/logging/logger.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package logging
16 |
17 | import (
18 | "github.com/sirupsen/logrus"
19 | "runtime"
20 | )
21 |
22 | var baseLogger = logrus.New()
23 |
24 | type AnonymousMessagingLogger struct {
25 | *logrus.Entry
26 | }
27 |
28 | func (l *AnonymousMessagingLogger) WithField(key string, value interface{}) *AnonymousMessagingLogger {
29 | return &AnonymousMessagingLogger{l.Entry.WithField(key, value)}
30 | }
31 |
32 | func (l *AnonymousMessagingLogger) WithFields(fields logrus.Fields) *AnonymousMessagingLogger {
33 | return &AnonymousMessagingLogger{l.Entry.WithFields(fields)}
34 | }
35 |
36 | func PackageLogger() *AnonymousMessagingLogger {
37 | _, filename, _, _ := runtime.Caller(1)
38 | return &AnonymousMessagingLogger{baseLogger.WithField("prefix", filename)}
39 | }
40 |
41 | func PackageLoggerWithField(key, value string) *AnonymousMessagingLogger {
42 | return &AnonymousMessagingLogger{baseLogger.WithField(key, value)}
43 | }
44 |
--------------------------------------------------------------------------------
/main:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google-deepmind/loopix-messaging/f65740edfb190da8a10dcc3f27290b19cfca17c3/main
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "anonymous-messaging/client"
19 | "anonymous-messaging/config"
20 | "anonymous-messaging/logging"
21 | "anonymous-messaging/pki"
22 | "anonymous-messaging/server"
23 | "anonymous-messaging/sphinx"
24 |
25 | "flag"
26 | "fmt"
27 |
28 | "anonymous-messaging/helpers"
29 | "github.com/protobuf/proto"
30 | )
31 |
32 | var logLocal = logging.PackageLogger()
33 |
34 | const (
35 | PKI_DIR = "pki/database.db"
36 | )
37 |
38 | func pkiPreSetting(pkiDir string) error {
39 | db, err := pki.OpenDatabase(pkiDir, "sqlite3")
40 | if err != nil {
41 | return err
42 | }
43 | defer db.Close()
44 |
45 | params := make(map[string]string)
46 | params["Id"] = "TEXT"
47 | params["Typ"] = "TEXT"
48 | params["Config"] = "BLOB"
49 |
50 | err = pki.CreateTable(db, "Pki", params)
51 | if err != nil {
52 | return err
53 | }
54 |
55 | return nil
56 | }
57 |
58 | //func FakeAdding(c *client.Client) {
59 | // logLocal.Info("Adding simulated traffic of a client")
60 | // for {
61 | // sphinxPacket, err := c.EncodeMessage("hello world", c.Config)
62 | // if err != nil {
63 | // }
64 | // packet, err := config.WrapWithFlag("\xc6", sphinxPacket)
65 | // if err != nil {
66 | // logLocal.Info("Something went wrong")
67 | // }
68 | // c.OutQueue <- packet
69 | // time.Sleep(10 * time.Second)
70 | // }
71 | //}
72 |
73 | // ReadInClientsPKI reads in the public information about users
74 | // from the PKI database and stores them locally. In case
75 | // the connection or fetching data from the PKI went wrong,
76 | // an error is returned.
77 | func ReadInClientsPKI(pkiName string) error {
78 | logLocal.Info(fmt.Sprintf(" Reading network users information from the PKI: %s", pkiName))
79 | var users []config.ClientConfig
80 |
81 | db, err := pki.OpenDatabase(pkiName, "sqlite3")
82 |
83 | if err != nil {
84 | return err
85 | }
86 |
87 | records, err := pki.QueryDatabase(db, "Pki", "Client")
88 |
89 | if err != nil {
90 | logLocal.WithError(err).Error("Error during Querying the Clients PKI")
91 | return err
92 | }
93 |
94 | for records.Next() {
95 | result := make(map[string]interface{})
96 | err := records.MapScan(result)
97 |
98 | if err != nil {
99 | logLocal.WithError(err).Error("Error in scanning table PKI record")
100 | return err
101 | }
102 |
103 | var pubs config.ClientConfig
104 | err = proto.Unmarshal(result["Config"].([]byte), &pubs)
105 | if err != nil {
106 | logLocal.WithError(err).Error(" Error during unmarshal function for client config")
107 | return err
108 | }
109 | users = append(users, pubs)
110 | }
111 | logLocal.Info(" Information about other users uploaded")
112 | return nil
113 | }
114 |
115 | func main() {
116 |
117 | typ := flag.String("typ", "", "A type of entity we want to run")
118 | id := flag.String("id", "", "Id of the entity we want to run")
119 | host := flag.String("host", "", "The host on which the entity is running")
120 | port := flag.String("port", "", "The port on which the entity is running")
121 | providerId := flag.String("provider", "", "The port on which the entity is running")
122 | flag.Parse()
123 |
124 | err := pkiPreSetting(PKI_DIR)
125 | if err != nil {
126 | panic(err)
127 | }
128 |
129 | ip, err := helpers.GetLocalIP()
130 | if err != nil {
131 | panic(err)
132 | }
133 |
134 | host = &ip
135 |
136 | switch *typ {
137 | case "client":
138 | db, err := pki.OpenDatabase(PKI_DIR, "sqlite3")
139 |
140 | if err != nil {
141 | panic(err)
142 | }
143 |
144 | row := db.QueryRow("SELECT Config FROM Pki WHERE Id = ? AND Typ = ?", providerId, "Provider")
145 |
146 | var results []byte
147 | err = row.Scan(&results)
148 | if err != nil {
149 | fmt.Println(err)
150 | }
151 | var providerInfo config.MixConfig
152 | err = proto.Unmarshal(results, &providerInfo)
153 |
154 | pubC, privC, err := sphinx.GenerateKeyPair()
155 | if err != nil {
156 | panic(err)
157 | }
158 |
159 | client, err := client.NewClient(*id, *host, *port, pubC, privC, PKI_DIR, providerInfo)
160 | if err != nil {
161 | panic(err)
162 | }
163 |
164 | err = client.Start()
165 | if err != nil {
166 | panic(err)
167 | }
168 |
169 | case "mix":
170 | pubM, privM, err := sphinx.GenerateKeyPair()
171 | if err != nil {
172 | panic(err)
173 | }
174 |
175 | mixServer, err := server.NewMixServer(*id, *host, *port, pubM, privM, PKI_DIR)
176 | if err != nil {
177 | panic(err)
178 | }
179 |
180 | err = mixServer.Start()
181 | if err != nil {
182 | panic(err)
183 | }
184 | case "provider":
185 | pubP, privP, err := sphinx.GenerateKeyPair()
186 | if err != nil {
187 | panic(err)
188 | }
189 |
190 | providerServer, err := server.NewProviderServer(*id, *host, *port, pubP, privP, PKI_DIR)
191 | if err != nil {
192 | panic(err)
193 | }
194 |
195 | err = providerServer.Start()
196 | if err != nil {
197 | panic(err)
198 | }
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/networker/NetworkClient.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package networker implements the interfaces for the Network (TCP) Client and Server.
17 | */
18 |
19 | package networker
20 |
21 | type NetworkClient interface {
22 | Send(packet string, host string, port string)
23 | }
24 |
--------------------------------------------------------------------------------
/networker/NetworkServer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package networker implements the interfaces for the Network (TCP) Client and Server.
17 | */
18 |
19 | package networker
20 |
21 | import "net"
22 |
23 | type NetworkServer interface {
24 | ListenForIncomingConnections()
25 | HandleConnection(conn net.Conn)
26 | Run()
27 | }
28 |
--------------------------------------------------------------------------------
/node/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google-deepmind/loopix-messaging/f65740edfb190da8a10dcc3f27290b19cfca17c3/node/.DS_Store
--------------------------------------------------------------------------------
/node/mix.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package node implements the core functions for a mix node, which allow to process the received cryptographic packets.
17 | */
18 | package node
19 |
20 | import (
21 | "anonymous-messaging/sphinx"
22 | "time"
23 | )
24 |
25 | type Mix struct {
26 | pubKey []byte
27 | prvKey []byte
28 | }
29 |
30 | // ProcessPacket performs the processing operation on the received packet, including cryptographic operations and
31 | // extraction of the meta information.
32 | func (m *Mix) ProcessPacket(packet []byte, c chan<- []byte, cAdr chan<- sphinx.Hop, cFlag chan<- string, errCh chan<- error) {
33 |
34 | nextHop, commands, newPacket, err := sphinx.ProcessSphinxPacket(packet, m.prvKey)
35 | if err != nil {
36 | errCh <- err
37 | }
38 |
39 | timeoutCh := make(chan []byte, 1)
40 |
41 | go func(p []byte, delay float64) {
42 | time.Sleep(time.Second * time.Duration(delay))
43 | timeoutCh <- p
44 | }(newPacket, commands.Delay)
45 |
46 | c <- <-timeoutCh
47 | cAdr <- nextHop
48 | cFlag <- commands.Flag
49 | errCh <- nil
50 |
51 | }
52 |
53 | // GetPublicKey returns the public key of the mixnode.
54 | func (m *Mix) GetPublicKey() []byte {
55 | return m.pubKey
56 | }
57 |
58 | // NewMix creates a new instance of Mix struct with given public and private key
59 | func NewMix(pubKey []byte, prvKey []byte) *Mix {
60 | return &Mix{pubKey: pubKey, prvKey: prvKey}
61 | }
62 |
--------------------------------------------------------------------------------
/node/mix_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package node
16 |
17 | import (
18 | "anonymous-messaging/config"
19 | sphinx "anonymous-messaging/sphinx"
20 |
21 | "github.com/protobuf/proto"
22 | "github.com/stretchr/testify/assert"
23 |
24 | "crypto/elliptic"
25 | "os"
26 | "reflect"
27 | "testing"
28 | )
29 |
30 | var nodes []config.MixConfig
31 |
32 | func createProviderWorker() (*Mix, error) {
33 | pubP, privP, err := sphinx.GenerateKeyPair()
34 | if err != nil {
35 | return nil, err
36 | }
37 | providerWorker := NewMix(pubP, privP)
38 | return providerWorker, nil
39 | }
40 |
41 | func createTestPacket(curve elliptic.Curve, mixes []config.MixConfig, provider config.MixConfig, recipient config.ClientConfig) (*sphinx.SphinxPacket, error) {
42 | path := config.E2EPath{IngressProvider: provider, Mixes: mixes, EgressProvider: provider, Recipient: recipient}
43 | testPacket, err := sphinx.PackForwardMessage(curve, path, []float64{1.4, 2.5, 2.3, 3.2, 7.4}, "Test Message")
44 | if err != nil {
45 | return nil, err
46 | }
47 | return &testPacket, nil
48 | }
49 |
50 | func createTestMixes() ([]config.MixConfig, error) {
51 | pub1, _, err := sphinx.GenerateKeyPair()
52 | if err != nil {
53 | return nil, err
54 | }
55 | pub2, _, err := sphinx.GenerateKeyPair()
56 | if err != nil {
57 | return nil, err
58 | }
59 | pub3, _, err := sphinx.GenerateKeyPair()
60 | if err != nil {
61 | return nil, err
62 | }
63 | m1 := config.MixConfig{Id: "Mix1", Host: "localhost", Port: "3330", PubKey: pub1}
64 | m2 := config.MixConfig{Id: "Mix2", Host: "localhost", Port: "3331", PubKey: pub2}
65 | m3 := config.MixConfig{Id: "Mix2", Host: "localhost", Port: "3332", PubKey: pub3}
66 | nodes = []config.MixConfig{m1, m2, m3}
67 |
68 | return nodes, nil
69 | }
70 |
71 | func TestMain(m *testing.M) {
72 |
73 | os.Exit(m.Run())
74 | }
75 |
76 | func TestMixProcessPacket(t *testing.T) {
77 | ch := make(chan []byte, 1)
78 | chHop := make(chan sphinx.Hop, 1)
79 | cAdr := make(chan string, 1)
80 | errCh := make(chan error, 1)
81 |
82 | pubD, _, err := sphinx.GenerateKeyPair()
83 | if err != nil {
84 | t.Fatal(err)
85 | }
86 |
87 | providerWorker, err := createProviderWorker()
88 | if err != nil {
89 | t.Fatal(err)
90 | }
91 | provider := config.MixConfig{Id: "Provider", Host: "localhost", Port: "3333", PubKey: providerWorker.pubKey}
92 | dest := config.ClientConfig{Id: "Destination", Host: "localhost", Port: "3334", PubKey: pubD, Provider: &provider}
93 | mixes, err := createTestMixes()
94 | if err != nil {
95 | t.Fatal(err)
96 | }
97 |
98 | testPacket, err := createTestPacket(elliptic.P224(), mixes, provider, dest)
99 | if err != nil {
100 | t.Fatal(err)
101 | }
102 |
103 | testPacketBytes, err := proto.Marshal(testPacket)
104 | if err != nil {
105 | t.Fatal(err)
106 | }
107 |
108 | providerWorker.ProcessPacket(testPacketBytes, ch, chHop, cAdr, errCh)
109 | dePacket := <-ch
110 | nextHop := <-chHop
111 | flag := <-cAdr
112 | err = <-errCh
113 | if err != nil {
114 | t.Fatal(err)
115 | }
116 |
117 | assert.Equal(t, sphinx.Hop{Id: "Mix1", Address: "localhost:3330", PubKey: nodes[0].PubKey}, nextHop, "Next hop does not match")
118 | assert.Equal(t, reflect.TypeOf([]byte{}), reflect.TypeOf(dePacket))
119 | assert.Equal(t, "\xF1", flag, reflect.TypeOf(dePacket))
120 | }
121 |
--------------------------------------------------------------------------------
/pki/database.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package pki implements basic functions for managing the pki
17 | represented as a SQL database.
18 | */
19 |
20 | package pki
21 |
22 | import (
23 | "github.com/jmoiron/sqlx"
24 | _ "github.com/mattn/go-sqlite3"
25 |
26 | "database/sql"
27 | "errors"
28 | "fmt"
29 | "strings"
30 | )
31 |
32 | // OpenDatabase opens a connection with a specified database.
33 | // OpenDatabase returns the database object and an error.
34 | func OpenDatabase(dataSourceName, dbDriver string) (*sqlx.DB, error) {
35 |
36 | var db *sqlx.DB
37 | db, err := sqlx.Connect(dbDriver, dataSourceName)
38 |
39 | if err != nil {
40 | return nil, err
41 | }
42 |
43 | return db, err
44 | }
45 |
46 | // CreateTable creates a new table defined by a given name and specified
47 | // column fields. CreateTable returns an error if a table could not be
48 | // correctly created or when an SQL injection attacks was detected.
49 | func CreateTable(db *sqlx.DB, tableName string, params map[string]string) error {
50 | paramsAndTypes := make([]string, 0, len(params))
51 |
52 | for key := range params {
53 | paramsAndTypes = append(paramsAndTypes, key+" "+params[key])
54 | }
55 |
56 | paramsText := "idx INTEGER PRIMARY KEY, " + strings.Join(paramsAndTypes[:], ", ")
57 | err := detectSQLInjection(tableName, paramsText)
58 | if err != nil {
59 | return err
60 | }
61 |
62 | query := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s ( %s )", tableName, paramsText)
63 |
64 | statement, err := db.Prepare(query)
65 |
66 | if err != nil {
67 | return err
68 | }
69 | _, err = statement.Exec()
70 | if err != nil {
71 | return err
72 | }
73 |
74 | return nil
75 |
76 | }
77 |
78 | // InsertIntoTable allows to insert a new record into the specified table.
79 | // The table name is checked for SQL injection attacks. The given input values
80 | // are not explicitly checked, since the Exec build-in function should do this.
81 | // The function returns an error if an SQL injection attack is detected or when
82 | // insertion fails.
83 | func InsertIntoTable(db *sqlx.DB, tableName string, id, typ string, config []byte) error {
84 | err := detectSQLInjection(tableName)
85 | if err != nil {
86 | return err
87 | }
88 |
89 | query := "INSERT INTO " + tableName + " (Id, Typ, Config) VALUES (?, ?, ?)"
90 | stmt, err := db.Prepare(query)
91 | if err != nil {
92 | return err
93 | }
94 | _, err = stmt.Exec(id, typ, config)
95 | if err != nil {
96 | return err
97 | }
98 |
99 | return nil
100 | }
101 |
102 | // QueryDatabase allows to query for records from a specified table, which
103 | // Typ column satisfies a given condition. QueryDatabase checks for SQL injection
104 | // in the tableName argument or condition argument. QueryDatabase returns a
105 | // set of rows and an error.
106 | func QueryDatabase(db *sqlx.DB, tableName string, condition string) (*sqlx.Rows, error) {
107 | err := detectSQLInjection(tableName, condition)
108 | if err != nil {
109 | return nil, err
110 | }
111 | query := fmt.Sprintf("SELECT * FROM %s WHERE Typ = ?", tableName)
112 | rows, err := db.Queryx(query, condition)
113 |
114 | if err != nil {
115 | return nil, err
116 | }
117 | return rows, nil
118 | }
119 |
120 | // rowExists checks whether a particular row, extracted using a given query, exists.
121 | // rowExists is used only in the unit tests, hence doesn't have to contain the SQL injection attacks detection.
122 | // If rowExists will become a public function, it should have SQL injection detection implemented.
123 | func rowExists(db *sqlx.DB, query string, args ...interface{}) (bool, error) {
124 | var exists bool
125 | query = fmt.Sprintf("SELECT exists (%s)", query)
126 | err := db.QueryRow(query, args...).Scan(&exists)
127 | if err != nil && err != sql.ErrNoRows {
128 | return false, err
129 | }
130 | return exists, nil
131 | }
132 |
133 | // detectSQLInjection checks whether the value passed to the query might allow for
134 | // SQL injection attacks by checking for ' and ; characters. If the error is detected
135 | // detectSQLInjection returns an error
136 | func detectSQLInjection(values ...string) error {
137 | for _, v := range values {
138 | if strings.ContainsAny(v, "'") || strings.ContainsAny(v, ";") {
139 | return errors.New("detected possible SQL injection")
140 | }
141 | }
142 | return nil
143 | }
144 |
--------------------------------------------------------------------------------
/pki/database_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package pki
16 |
17 | import (
18 | "github.com/jmoiron/sqlx"
19 | "github.com/stretchr/testify/assert"
20 |
21 | "database/sql"
22 | "errors"
23 | "fmt"
24 | "os"
25 | "testing"
26 | )
27 |
28 | const (
29 | testDatabase = "./TESTDATABASE.DB"
30 | )
31 |
32 | var dbDir string
33 | var db *sqlx.DB
34 |
35 | func Setup() (*sqlx.DB, error) {
36 |
37 | Clean()
38 |
39 | db, err := sqlx.Connect("sqlite3", testDatabase)
40 | if err != nil {
41 | return nil, err
42 | }
43 |
44 | query := `CREATE TABLE TableXX (
45 | idx INTEGER PRIMARY KEY,
46 | Id TEXT,
47 | Typ TEXT,
48 | Config BLOB);`
49 |
50 | _, err = db.Exec(query)
51 | if err != nil {
52 | return nil, err
53 | }
54 |
55 | insertQuery := `INSERT INTO TableXX (Id, Typ, Config) VALUES (?, ?, ?)`
56 |
57 | _, err = db.Exec(insertQuery, "ABC", "DEF", []byte("GHI"))
58 | if err != nil {
59 | return nil, err
60 | }
61 | return db, nil
62 | }
63 |
64 | func Clean() {
65 |
66 | if _, err := os.Stat(testDatabase); err == nil {
67 | errRemove := os.Remove(testDatabase)
68 | if errRemove != nil {
69 | panic(err)
70 | }
71 | }
72 | }
73 |
74 | func TestMain(m *testing.M) {
75 |
76 | var err error
77 | db, err = Setup()
78 | if err != nil {
79 | fmt.Println(err)
80 | panic(m)
81 | }
82 | defer db.Close()
83 |
84 | code := m.Run()
85 | Clean()
86 | os.Exit(code)
87 | }
88 |
89 | func TestCreateTable(t *testing.T) {
90 |
91 | params := map[string]string{"Id": "TEXT", "Typ": "TEXT", "Config": "BLOB"}
92 | err := CreateTable(db, "TestTable", params)
93 | if err != nil {
94 | t.Error(err)
95 | }
96 |
97 | var exists bool
98 | err = db.QueryRow("SELECT CASE WHEN exists (SELECT * from sqlite_master WHERE type = 'table' AND name = 'TestTable') then 1 else 0 end").Scan(&exists)
99 | if err != nil && err != sql.ErrNoRows {
100 | panic(fmt.Sprintf("Error while checking if table exists %v \n", err))
101 | }
102 | assert.True(t, exists, "Table was not added to the database")
103 | }
104 |
105 | func TestCreateTable_SQLInjection(t *testing.T) {
106 | err := CreateTable(db, "TestTable;", nil)
107 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
108 |
109 | err = CreateTable(db, "TestTable'", nil)
110 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
111 | }
112 |
113 | func TestQueryDatabase(t *testing.T) {
114 | rows, err := QueryDatabase(db, "TableXX", "DEF")
115 |
116 | if err != nil {
117 | t.Error(err)
118 | }
119 |
120 | results := make(map[string]interface{})
121 | for rows.Next() {
122 | err := rows.MapScan(results)
123 |
124 | if err != nil {
125 | fmt.Printf("Error %v \n", err)
126 | }
127 | }
128 | assert.Equal(t, "ABC", string(results["Id"].([]byte)), "Should be equal")
129 | assert.Equal(t, "DEF", string(results["Typ"].([]byte)), "Should be equal")
130 | assert.Equal(t, []byte("GHI"), results["Config"].([]byte), "Should be equal")
131 | }
132 |
133 | func TestQueryDatabase_SQLInjection(t *testing.T) {
134 | _, err := QueryDatabase(db, "TableXX;", "DEF")
135 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
136 |
137 | _, err = QueryDatabase(db, "TableXX", "DEF;")
138 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
139 |
140 | _, err = QueryDatabase(db, "TableXX'", "DEF")
141 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
142 |
143 | _, err = QueryDatabase(db, "TableXX", "DEF'")
144 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
145 | }
146 |
147 | func TestInsertIntoTable(t *testing.T) {
148 |
149 | err := InsertIntoTable(db, "TableXX", "TestInsertId", "TestInsertTyp", []byte("TestInsertBytes"))
150 | if err != nil {
151 | t.Error(err)
152 | }
153 |
154 | exists, err := rowExists(db, "SELECT * FROM TableXX WHERE Id=$1 AND Typ=$2 AND Config=$3", "TestInsertId", "TestInsertTyp", []byte("TestInsertBytes"))
155 | if err != nil {
156 | t.Error(err)
157 | }
158 | assert.True(t, exists, "The inserted row was not found in the database")
159 | }
160 |
161 | func TestInsertIntoTable_SQLInjection(t *testing.T) {
162 | err := InsertIntoTable(db, "TableXX;", "TestInsertId", "TestInsertTyp", []byte("TestInsertBytes"))
163 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
164 |
165 | err = InsertIntoTable(db, "TableXX'", "TestInsertId", "TestInsertTyp", []byte("TestInsertBytes"))
166 | assert.EqualError(t, errors.New("detected possible SQL injection"), err.Error())
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/run_clients.sh:
--------------------------------------------------------------------------------
1 | #// Copyright 2018 The Loopix-Messaging Authors
2 | #//
3 | #// Licensed under the Apache License, Version 2.0 (the "License");
4 | #// you may not use this file except in compliance with the License.
5 | #// You may obtain a copy of the License at
6 | #//
7 | #// http://www.apache.org/licenses/LICENSE-2.0
8 | #//
9 | #// Unless required by applicable law or agreed to in writing, software
10 | #// distributed under the License is distributed on an "AS IS" BASIS,
11 | #// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | #// See the License for the specific language governing permissions and
13 | #// limitations under the License.
14 |
15 | #!bin/sh
16 |
17 | echo "Press CTRL-C to stop."
18 |
19 | logDir="$(PWD)/logs"
20 |
21 | if [ -d $logDir ]
22 | then
23 | echo "Logging directory already exists"
24 | else
25 | mkdir $logDir
26 | echo "Created logging directory"
27 | fi
28 |
29 | go run main.go -typ=client -id=Client1 -host=localhost -port=9996 -provider=Provider >> logs/bash.log ;
30 |
31 | #NUMCLIENTS=$1
32 | #
33 | #for (( j=0; j logs/bash.log &
36 | # sleep 1
37 | #done
38 | #
39 | #sleep 1
40 |
41 |
42 | trap ctrl_c SIGINT SIGTERM SIGTSTP
43 | function ctrl_c() {
44 | echo "** Trapped SIGINT, SIGTERM and SIGTSTP"
45 | kill_port 9996
46 | # for (( j=0; j> logs/bash.log &
35 | sleep 1
36 | done
37 |
38 | sleep 1
39 | go run main.go -typ=provider -id=Provider -host=localhost -port=9997 >> logs/bash.log
40 |
41 | # read -p "Press CTRL-C to stop."
42 |
43 | # In case the loop is not working, we can use the following command
44 | #go run main.go -typ=mix -id=Mix1 -host=localhost -port=9998 > logs/bash.log &
45 |
46 |
47 | # trap call ctrl_c()
48 | trap ctrl_c SIGINT SIGTERM SIGTSTP
49 | function ctrl_c() {
50 | echo "** Trapped SIGINT, SIGTERM and SIGTSTP"
51 | for (( j=0; j<$NUMMIXES; j++ ));
52 | do
53 | kill_port $((9980+$j))
54 | done
55 | }
56 |
57 | function kill_port() {
58 | PID=$(lsof -t -i:$1)
59 | echo "$PID"
60 | kill -TERM $PID || kill -KILL $PID
61 | }
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/server/mixServer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /*
16 | Package server implements the mix server.
17 | */
18 | package server
19 |
20 | import (
21 | "anonymous-messaging/config"
22 | "anonymous-messaging/helpers"
23 | "anonymous-messaging/logging"
24 | "anonymous-messaging/networker"
25 | "anonymous-messaging/node"
26 | "anonymous-messaging/sphinx"
27 |
28 | "github.com/protobuf/proto"
29 | "net"
30 | )
31 |
32 | var logLocal = logging.PackageLogger()
33 |
34 | type MixServerIt interface {
35 | networker.NetworkServer
36 | networker.NetworkClient
37 | GetConfig() config.MixConfig
38 | Start() error
39 | }
40 |
41 | type MixServer struct {
42 | id string
43 | host string
44 | port string
45 | listener *net.TCPListener
46 | *node.Mix
47 |
48 | config config.MixConfig
49 | }
50 |
51 | func (m *MixServer) Start() error {
52 | defer m.run()
53 | return nil
54 | }
55 |
56 | func (m *MixServer) GetConfig() config.MixConfig {
57 | return m.config
58 | }
59 |
60 | func (m *MixServer) receivedPacket(packet []byte) error {
61 | logLocal.Info("Received new sphinx packet")
62 |
63 | c := make(chan []byte)
64 | cAdr := make(chan sphinx.Hop)
65 | cFlag := make(chan string)
66 | errCh := make(chan error)
67 |
68 | go m.ProcessPacket(packet, c, cAdr, cFlag, errCh)
69 | dePacket := <-c
70 | nextHop := <-cAdr
71 | flag := <-cFlag
72 | err := <-errCh
73 |
74 | if err != nil {
75 | return err
76 | }
77 |
78 | if flag == "\xF1" {
79 | m.forwardPacket(dePacket, nextHop.Address)
80 | } else {
81 | logLocal.Info("Packet has non-forward flag. Packet dropped")
82 | }
83 | return nil
84 | }
85 |
86 | func (m *MixServer) forwardPacket(sphinxPacket []byte, address string) error {
87 | packetBytes, err := config.WrapWithFlag(commFlag, sphinxPacket)
88 | if err != nil {
89 | return err
90 | }
91 | err = m.send(packetBytes, address)
92 | if err != nil {
93 | return err
94 | }
95 |
96 | return nil
97 | }
98 |
99 | func (m *MixServer) send(packet []byte, address string) error {
100 |
101 | conn, err := net.Dial("tcp", address)
102 | if err != nil {
103 | return err
104 | }
105 | defer conn.Close()
106 |
107 | _, err = conn.Write(packet)
108 | if err != nil {
109 | return err
110 | }
111 | return nil
112 | }
113 |
114 | func (m *MixServer) run() {
115 |
116 | defer m.listener.Close()
117 | finish := make(chan bool)
118 |
119 | go func() {
120 | logLocal.Infof("Listening on %s", m.host+":"+m.port)
121 | m.listenForIncomingConnections()
122 | }()
123 |
124 | <-finish
125 | }
126 |
127 | func (m *MixServer) listenForIncomingConnections() {
128 | for {
129 | conn, err := m.listener.Accept()
130 |
131 | if err != nil {
132 | logLocal.WithError(err).Error(err)
133 | } else {
134 | logLocal.Infof("Received connection from %s", conn.RemoteAddr())
135 | errs := make(chan error, 1)
136 | go m.handleConnection(conn, errs)
137 | err = <-errs
138 | if err != nil {
139 | logLocal.WithError(err).Error(err)
140 | }
141 | }
142 | }
143 | }
144 |
145 | func (m *MixServer) handleConnection(conn net.Conn, errs chan<- error) {
146 | defer conn.Close()
147 |
148 | buff := make([]byte, 1024)
149 | reqLen, err := conn.Read(buff)
150 | if err != nil {
151 | errs <- err
152 | }
153 |
154 | var packet config.GeneralPacket
155 | err = proto.Unmarshal(buff[:reqLen], &packet)
156 | if err != nil {
157 | errs <- err
158 | }
159 |
160 | switch packet.Flag {
161 | case commFlag:
162 | err = m.receivedPacket(packet.Data)
163 | if err != nil {
164 | errs <- err
165 | }
166 | default:
167 | logLocal.Infof("Packet flag %s not recognised. Packet dropped", packet.Flag)
168 | errs <- nil
169 | }
170 | errs <- nil
171 | }
172 |
173 | func NewMixServer(id, host, port string, pubKey []byte, prvKey []byte, pkiPath string) (*MixServer, error) {
174 | mix := node.NewMix(pubKey, prvKey)
175 | mixServer := MixServer{id: id, host: host, port: port, Mix: mix, listener: nil}
176 | mixServer.config = config.MixConfig{Id: mixServer.id, Host: mixServer.host, Port: mixServer.port, PubKey: mixServer.GetPublicKey()}
177 |
178 | configBytes, err := proto.Marshal(&mixServer.config)
179 | if err != nil {
180 | return nil, err
181 | }
182 | err = helpers.AddToDatabase(pkiPath, "Pki", mixServer.id, "Mix", configBytes)
183 | if err != nil {
184 | return nil, err
185 | }
186 |
187 | addr, err := helpers.ResolveTCPAddress(mixServer.host, mixServer.port)
188 |
189 | if err != nil {
190 | return nil, err
191 | }
192 | mixServer.listener, err = net.ListenTCP("tcp", addr)
193 |
194 | if err != nil {
195 | return nil, err
196 | }
197 |
198 | return &mixServer, nil
199 | }
200 |
--------------------------------------------------------------------------------
/server/providerServer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package server
16 |
17 | import (
18 | "anonymous-messaging/config"
19 | "anonymous-messaging/helpers"
20 | "anonymous-messaging/networker"
21 | "anonymous-messaging/node"
22 | "anonymous-messaging/sphinx"
23 |
24 | "github.com/protobuf/proto"
25 |
26 | "bytes"
27 | "errors"
28 | "fmt"
29 | "io/ioutil"
30 | "net"
31 | "os"
32 | )
33 |
34 | const (
35 | assigneFlag = "\xa2"
36 | commFlag = "\xc6"
37 | tokenFlag = "xa9"
38 | pullFlag = "\xff"
39 | )
40 |
41 | type ProviderIt interface {
42 | networker.NetworkServer
43 | networker.NetworkClient
44 | Start() error
45 | GetConfig() config.MixConfig
46 | }
47 |
48 | type ProviderServer struct {
49 | id string
50 | host string
51 | port string
52 | *node.Mix
53 | listener *net.TCPListener
54 |
55 | assignedClients map[string]ClientRecord
56 | config config.MixConfig
57 | }
58 |
59 | type ClientRecord struct {
60 | id string
61 | host string
62 | port string
63 | pubKey []byte
64 | token []byte
65 | }
66 |
67 | // Start function creates the loggers for capturing the info and error logs
68 | // and starts the listening server. Function returns an error
69 | // signaling whether any operation was unsuccessful
70 | func (p *ProviderServer) Start() error {
71 | p.run()
72 |
73 | return nil
74 | }
75 |
76 | func (p *ProviderServer) GetConfig() config.MixConfig {
77 | return p.config
78 | }
79 |
80 | // Function opens the listener to start listening on provider's host and port
81 | func (p *ProviderServer) run() {
82 |
83 | defer p.listener.Close()
84 | finish := make(chan bool)
85 |
86 | go func() {
87 | logLocal.Infof("Listening on %s", p.host+":"+p.port)
88 | p.listenForIncomingConnections()
89 | }()
90 |
91 | <-finish
92 | }
93 |
94 | // Function processes the received sphinx packet, performs the
95 | // unwrapping operation and checks whether the packet should be
96 | // forwarded or stored. If the processing was unsuccessful and error is returned.
97 | func (p *ProviderServer) receivedPacket(packet []byte) error {
98 | logLocal.Info("Received new sphinx packet")
99 |
100 | c := make(chan []byte)
101 | cAdr := make(chan sphinx.Hop)
102 | cFlag := make(chan string)
103 | errCh := make(chan error)
104 |
105 | go p.ProcessPacket(packet, c, cAdr, cFlag, errCh)
106 | dePacket := <-c
107 | nextHop := <-cAdr
108 | flag := <-cFlag
109 | err := <-errCh
110 |
111 | if err != nil {
112 | return err
113 | }
114 |
115 | switch flag {
116 | case "\xF1":
117 | err = p.forwardPacket(dePacket, nextHop.Address)
118 | if err != nil {
119 | return err
120 | }
121 | case "\xF0":
122 | err = p.storeMessage(dePacket, nextHop.Id, "TMP_MESSAGE_ID")
123 | if err != nil {
124 | return err
125 | }
126 | default:
127 | logLocal.Info("Sphinx packet flag not recognised")
128 | }
129 |
130 | return nil
131 | }
132 |
133 | func (p *ProviderServer) forwardPacket(sphinxPacket []byte, address string) error {
134 | packetBytes, err := config.WrapWithFlag(commFlag, sphinxPacket)
135 | if err != nil {
136 | return err
137 | }
138 |
139 | err = p.send(packetBytes, address)
140 | if err != nil {
141 | return err
142 | }
143 | logLocal.Info("Forwarded sphinx packet")
144 | return nil
145 | }
146 |
147 | // Function opens a connection with selected network address
148 | // and send the passed packet. If connection failed or
149 | // the packet could not be send, an error is returned
150 | func (p *ProviderServer) send(packet []byte, address string) error {
151 |
152 | conn, err := net.Dial("tcp", address)
153 | if err != nil {
154 | return err
155 | }
156 | defer conn.Close()
157 |
158 | conn.Write(packet)
159 | return nil
160 | }
161 |
162 | // Function responsible for running the listening process of the server;
163 | // The providers listener accepts incoming connections and
164 | // passes the incoming packets to the packet handler.
165 | // If the connection could not be accepted an error
166 | // is logged into the log files, but the function is not stopped
167 | func (p *ProviderServer) listenForIncomingConnections() {
168 | for {
169 | conn, err := p.listener.Accept()
170 |
171 | if err != nil {
172 | logLocal.WithError(err).Error(err)
173 | } else {
174 | logLocal.Infof("Received new connection from %s", conn.RemoteAddr())
175 | errs := make(chan error, 1)
176 | go p.handleConnection(conn, errs)
177 | err = <-errs
178 | if err != nil {
179 | logLocal.WithError(err).Error(err)
180 | }
181 | }
182 | }
183 | }
184 |
185 | // HandleConnection handles the received packets; it checks the flag of the
186 | // packet and schedules a corresponding process function and returns an error.
187 | func (p *ProviderServer) handleConnection(conn net.Conn, errs chan<- error) {
188 |
189 | buff := make([]byte, 1024)
190 | reqLen, err := conn.Read(buff)
191 | defer conn.Close()
192 |
193 | if err != nil {
194 | errs <- err
195 | }
196 |
197 | var packet config.GeneralPacket
198 | err = proto.Unmarshal(buff[:reqLen], &packet)
199 | if err != nil {
200 | errs <- err
201 | }
202 |
203 | switch packet.Flag {
204 | case assigneFlag:
205 | err = p.handleAssignRequest(packet.Data)
206 | if err != nil {
207 | errs <- err
208 | }
209 | case commFlag:
210 | err = p.receivedPacket(packet.Data)
211 | if err != nil {
212 | errs <- err
213 | }
214 | case pullFlag:
215 | err = p.handlePullRequest(packet.Data)
216 | if err != nil {
217 | errs <- err
218 | }
219 | default:
220 | logLocal.Info(packet.Flag)
221 | logLocal.Info("Packet flag not recognised. Packet dropped")
222 | errs <- nil
223 | }
224 | errs <- nil
225 | }
226 |
227 | // RegisterNewClient generates a fresh authentication token and saves it together with client's public configuration data
228 | // in the list of all registered clients. After the client is registered the function creates an inbox directory
229 | // for the client's inbox, in which clients messages will be stored.
230 | func (p *ProviderServer) registerNewClient(clientBytes []byte) ([]byte, string, error) {
231 | var clientConf config.ClientConfig
232 | err := proto.Unmarshal(clientBytes, &clientConf)
233 | if err != nil {
234 | return nil, "", err
235 | }
236 |
237 | token := helpers.SHA256([]byte("TMP_Token" + clientConf.Id))
238 | record := ClientRecord{id: clientConf.Id, host: clientConf.Host, port: clientConf.Port, pubKey: clientConf.PubKey, token: token}
239 | p.assignedClients[clientConf.Id] = record
240 | address := clientConf.Host + ":" + clientConf.Port
241 |
242 | path := fmt.Sprintf("./inboxes/%s", clientConf.Id)
243 | exists, err := helpers.DirExists(path)
244 | if err != nil {
245 | return nil, "", err
246 | }
247 | if exists == false {
248 | if err := os.MkdirAll(path, 0775); err != nil {
249 | return nil, "", err
250 | }
251 | }
252 |
253 | return token, address, nil
254 | }
255 |
256 | // Function is responsible for handling the registration request from the client.
257 | // it registers the client in the list of all registered clients and send
258 | // an authentication token back to the client.
259 | func (p *ProviderServer) handleAssignRequest(packet []byte) error {
260 | logLocal.Info("Received assign request from the client")
261 |
262 | token, adr, err := p.registerNewClient(packet)
263 | if err != nil {
264 | return err
265 | }
266 |
267 | tokenBytes, err := config.WrapWithFlag(tokenFlag, token)
268 | if err != nil {
269 | return err
270 | }
271 | err = p.send(tokenBytes, adr)
272 | if err != nil {
273 | return err
274 | }
275 | return nil
276 | }
277 |
278 | // Function is responsible for handling the pull request received from the client.
279 | // It first authenticates the client, by checking if the received token is valid.
280 | // If yes, the function triggers the function for checking client's inbox
281 | // and sending buffered messages. Otherwise, an error is returned.
282 | func (p *ProviderServer) handlePullRequest(rqsBytes []byte) error {
283 | var request config.PullRequest
284 | err := proto.Unmarshal(rqsBytes, &request)
285 | if err != nil {
286 | return err
287 | }
288 |
289 | logLocal.Infof("Processing pull request: %s %s", request.ClientId, string(request.Token))
290 |
291 | if p.authenticateUser(request.ClientId, request.Token) == true {
292 | signal, err := p.fetchMessages(request.ClientId)
293 | if err != nil {
294 | return err
295 | }
296 | switch signal {
297 | case "NI":
298 | logLocal.Info("Inbox does not exist. Sending signal to client.")
299 | case "EI":
300 | logLocal.Info("Inbox is empty. Sending info to the client.")
301 | case "SI":
302 | logLocal.Info("All messages from the inbox succesfuly sent to the client.")
303 | }
304 | } else {
305 | logLocal.Warning("Authentication went wrong")
306 | return errors.New("authentication went wrong")
307 | }
308 | return nil
309 | }
310 |
311 | // AuthenticateUser compares the authentication token received from the client with
312 | // the one stored by the provider. If tokens are the same, it returns true
313 | // and false otherwise.
314 | func (p *ProviderServer) authenticateUser(clientId string, clientToken []byte) bool {
315 |
316 | if bytes.Compare(p.assignedClients[clientId].token, clientToken) == 0 {
317 | return true
318 | }
319 | logLocal.Warningf("Non matching token: %s, %s", p.assignedClients[clientId].token, clientToken)
320 | return false
321 | }
322 |
323 | // FetchMessages fetches messages from the requested inbox.
324 | // FetchMessages checks whether an inbox exists and if it contains
325 | // stored messages. If inbox contains any stored messages, all of them
326 | // are send to the client one by one. FetchMessages returns a code
327 | // signaling whether (NI) inbox does not exist, (EI) inbox is empty,
328 | // (SI) messages were send to the client; and an error.
329 | func (p *ProviderServer) fetchMessages(clientId string) (string, error) {
330 |
331 | path := fmt.Sprintf("./inboxes/%s", clientId)
332 | exist, err := helpers.DirExists(path)
333 | if err != nil {
334 | return "", err
335 | }
336 | if exist == false {
337 | return "NI", nil
338 | }
339 | files, err := ioutil.ReadDir(path)
340 | if err != nil {
341 | return "", err
342 | }
343 | if len(files) == 0 {
344 | return "EI", nil
345 | }
346 |
347 | for _, f := range files {
348 | dat, err := ioutil.ReadFile(path + "/" + f.Name())
349 | if err != nil {
350 | return "", err
351 | }
352 |
353 | address := p.assignedClients[clientId].host + ":" + p.assignedClients[clientId].port
354 | logLocal.Infof("Found stored message for address %s", address)
355 | msgBytes, err := config.WrapWithFlag(commFlag, dat)
356 | if err != nil {
357 | return "", err
358 | }
359 | err = p.send(msgBytes, address)
360 | if err != nil {
361 | return "", err
362 | }
363 | }
364 | return "SI", nil
365 | }
366 |
367 | // StoreMessage saves the given message in the inbox defined by the given id.
368 | // If the inbox address does not exist or writing into the inbox was unsuccessful
369 | // the function returns an error
370 | func (p *ProviderServer) storeMessage(message []byte, inboxId string, messageId string) error {
371 | path := fmt.Sprintf("./inboxes/%s", inboxId)
372 | fileName := path + "/" + messageId + ".txt"
373 |
374 | file, err := os.Create(fileName)
375 | if err != nil {
376 | return err
377 | }
378 | defer file.Close()
379 |
380 | _, err = file.Write(message)
381 | if err != nil {
382 | return err
383 | }
384 |
385 | logLocal.Infof("Stored message for %s", inboxId)
386 | return nil
387 | }
388 |
389 | // NewProviderServer constructs a new provider object.
390 | // NewProviderServer returns a new provider object and an error.
391 | func NewProviderServer(id string, host string, port string, pubKey []byte, prvKey []byte, pkiPath string) (*ProviderServer, error) {
392 | node := node.NewMix(pubKey, prvKey)
393 | providerServer := ProviderServer{id: id, host: host, port: port, Mix: node, listener: nil}
394 | providerServer.config = config.MixConfig{Id: providerServer.id, Host: providerServer.host, Port: providerServer.port, PubKey: providerServer.GetPublicKey()}
395 | providerServer.assignedClients = make(map[string]ClientRecord)
396 |
397 | configBytes, err := proto.Marshal(&providerServer.config)
398 | if err != nil {
399 | return nil, err
400 | }
401 | err = helpers.AddToDatabase(pkiPath, "Pki", providerServer.id, "Provider", configBytes)
402 | if err != nil {
403 | return nil, err
404 | }
405 |
406 | addr, err := helpers.ResolveTCPAddress(providerServer.host, providerServer.port)
407 | if err != nil {
408 | return nil, err
409 | }
410 | providerServer.listener, err = net.ListenTCP("tcp", addr)
411 |
412 | if err != nil {
413 | return nil, err
414 | }
415 |
416 | return &providerServer, nil
417 | }
418 |
--------------------------------------------------------------------------------
/server/server_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package server
16 |
17 | import (
18 | "anonymous-messaging/config"
19 | "anonymous-messaging/helpers"
20 | "anonymous-messaging/node"
21 | "anonymous-messaging/sphinx"
22 |
23 | "github.com/protobuf/proto"
24 | "github.com/stretchr/testify/assert"
25 |
26 | "crypto/elliptic"
27 | "errors"
28 | "fmt"
29 | "io/ioutil"
30 | "net"
31 | "os"
32 | "path/filepath"
33 | "testing"
34 | )
35 |
36 | var mixServer *MixServer
37 | var providerServer *ProviderServer
38 |
39 | const (
40 | testDatabase = "testDatabase.db"
41 | )
42 |
43 | func createTestProvider() (*ProviderServer, error) {
44 | pub, priv, err := sphinx.GenerateKeyPair()
45 | if err != nil {
46 | return nil, err
47 | }
48 | node := node.NewMix(pub, priv)
49 | provider := ProviderServer{host: "localhost", port: "9999", Mix: node}
50 | provider.config = config.MixConfig{Id: provider.id, Host: provider.host, Port: provider.port, PubKey: provider.GetPublicKey()}
51 | provider.assignedClients = make(map[string]ClientRecord)
52 | return &provider, nil
53 | }
54 |
55 | func createTestMixnode() (*MixServer, error) {
56 | pub, priv, err := sphinx.GenerateKeyPair()
57 | if err != nil {
58 | return nil, err
59 | }
60 | node := node.NewMix(pub, priv)
61 | mix := MixServer{host: "localhost", port: "9995", Mix: node}
62 | mix.config = config.MixConfig{Id: mix.id, Host: mix.host, Port: mix.port, PubKey: mix.GetPublicKey()}
63 | addr, err := helpers.ResolveTCPAddress(mix.host, mix.port)
64 | if err != nil {
65 | return nil, err
66 | }
67 |
68 | mix.listener, err = net.ListenTCP("tcp", addr)
69 | if err != nil {
70 | return nil, err
71 | }
72 | return &mix, nil
73 | }
74 |
75 | func createFakeClientListener(host, port string) (*net.TCPListener, error) {
76 | addr, err := helpers.ResolveTCPAddress(host, port)
77 | if err != nil {
78 | return nil, err
79 | }
80 |
81 | listener, err := net.ListenTCP("tcp", addr)
82 | if err != nil {
83 | return nil, err
84 | }
85 | return listener, nil
86 | }
87 |
88 | func clean() {
89 | os.RemoveAll("./inboxes")
90 | }
91 |
92 | func TestMain(m *testing.M) {
93 | var err error
94 | mixServer, err = createTestMixnode()
95 | if err != nil {
96 | fmt.Println(err)
97 | panic(m)
98 | }
99 |
100 | providerServer, err = createTestProvider()
101 | if err != nil {
102 | fmt.Println(err)
103 | panic(m)
104 | }
105 |
106 | code := m.Run()
107 | clean()
108 | os.Exit(code)
109 | }
110 |
111 | func TestProviderServer_AuthenticateUser_Pass(t *testing.T) {
112 | testToken := []byte("AuthenticationToken")
113 | record := ClientRecord{id: "Alice", host: "localhost", port: "1111", pubKey: nil, token: testToken}
114 | providerServer.assignedClients["Alice"] = record
115 | assert.True(t, providerServer.authenticateUser("Alice", []byte("AuthenticationToken")), " Authentication should be successful")
116 | }
117 |
118 | func TestProviderServer_AuthenticateUser_Fail(t *testing.T) {
119 | record := ClientRecord{id: "Alice", host: "localhost", port: "1111", pubKey: nil, token: []byte("AuthenticationToken")}
120 | providerServer.assignedClients["Alice"] = record
121 | assert.False(t, providerServer.authenticateUser("Alice", []byte("WrongAuthToken")), " Authentication should not be successful")
122 | }
123 |
124 | func createInbox(id string, t *testing.T) {
125 | path := filepath.Join("./inboxes", id)
126 | exists, err := helpers.DirExists(path)
127 | if err != nil {
128 | t.Fatal(err)
129 | }
130 | if exists {
131 | os.RemoveAll(path)
132 | os.MkdirAll(path, 0755)
133 | } else {
134 | os.MkdirAll(path, 0755)
135 | }
136 | }
137 |
138 | func createTestMessage(id string, t *testing.T) {
139 |
140 | file, err := os.Create(filepath.Join("./inboxes", id, "TestMessage.txt"))
141 | if err != nil {
142 | t.Fatal(err)
143 | }
144 | defer file.Close()
145 | _, err = file.Write([]byte("This is a test message"))
146 | if err != nil {
147 | t.Fatal(err)
148 | }
149 |
150 | }
151 |
152 | func TestProviderServer_FetchMessages_FullInbox(t *testing.T) {
153 | clientListener, err := createFakeClientListener("localhost", "9999")
154 | defer clientListener.Close()
155 |
156 | providerServer.assignedClients["FakeClient"] = ClientRecord{"FakeClient",
157 | "localhost",
158 | "9999",
159 | []byte("FakePublicKey"),
160 | []byte("TestToken")}
161 |
162 | createInbox("FakeClient", t)
163 | createTestMessage("FakeClient", t)
164 |
165 | signal, err := providerServer.fetchMessages("FakeClient")
166 | if err != nil {
167 | t.Error(err)
168 | }
169 | assert.Equal(t, "SI", signal, " For inbox containing messages the signal should be SI")
170 | }
171 |
172 | func TestProviderServer_FetchMessages_EmptyInbox(t *testing.T) {
173 | createInbox("EmptyInbox", t)
174 | signal, err := providerServer.fetchMessages("EmptyInbox")
175 | if err != nil {
176 | t.Error(err)
177 | }
178 | assert.Equal(t, "EI", signal, " For an empty inbox id the function should return signal EI")
179 | }
180 |
181 | func TestProviderServer_FetchMessages_NoInbox(t *testing.T) {
182 | signal, err := providerServer.fetchMessages("NonExistingInbox")
183 | if err != nil {
184 | t.Error(err)
185 | }
186 | assert.Equal(t, "NI", signal, " For a non-existing inbox id the function should return signal NI")
187 | }
188 |
189 | func TestProviderServer_StoreMessage(t *testing.T) {
190 |
191 | inboxId := "ClientInbox"
192 | fileId := "12345"
193 | inboxDir := "./inboxes/" + inboxId
194 | filePath := inboxDir + "/" + fileId + ".txt"
195 |
196 | err := os.MkdirAll(inboxDir, 0755)
197 | if err != nil {
198 | t.Fatal(err)
199 | }
200 |
201 | message := []byte("Hello world message")
202 | providerServer.storeMessage(message, inboxId, fileId)
203 |
204 | _, err = os.Stat(filePath)
205 | if err != nil {
206 | t.Fatal(err)
207 | }
208 | assert.Nil(t, err, "The file with the message should be created")
209 |
210 | dat, err := ioutil.ReadFile(filePath)
211 | if err != nil {
212 | t.Fatal(err)
213 | }
214 | assert.Equal(t, message, dat, "Messages should be the same")
215 |
216 | }
217 |
218 | func TestProviderServer_HandlePullRequest_Pass(t *testing.T) {
219 | testPullRequest := config.PullRequest{ClientId: "PassTestId", Token: []byte("TestToken")}
220 | providerServer.assignedClients["PassTestId"] = ClientRecord{id: "TestId", host: "localhost", port: "1111", pubKey: nil, token: []byte("TestToken")}
221 | bTestPullRequest, err := proto.Marshal(&testPullRequest)
222 | if err != nil {
223 | t.Error(err)
224 | }
225 | err = providerServer.handlePullRequest(bTestPullRequest)
226 | if err != nil {
227 | t.Error(err)
228 | }
229 | }
230 |
231 | func TestProviderServer_HandlePullRequest_Fail(t *testing.T) {
232 | testPullRequest := config.PullRequest{ClientId: "FailTestId", Token: []byte("TestToken")}
233 | providerServer.assignedClients = map[string]ClientRecord{}
234 | bTestPullRequest, err := proto.Marshal(&testPullRequest)
235 | if err != nil {
236 | t.Error(err)
237 | }
238 | err = providerServer.handlePullRequest(bTestPullRequest)
239 | assert.EqualError(t, errors.New("authentication went wrong"), err.Error(), "HandlePullRequest should return an error if authentication failed")
240 | }
241 |
242 | func TestProviderServer_RegisterNewClient(t *testing.T) {
243 | newClient := config.ClientConfig{Id: "NewClient", Host: "localhost", Port: "9998", PubKey: nil}
244 | bNewClient, err := proto.Marshal(&newClient)
245 | if err != nil {
246 | t.Fatal(err)
247 | }
248 | token, addr, err := providerServer.registerNewClient(bNewClient)
249 | if err != nil {
250 | t.Fatal(err)
251 | }
252 | assert.Equal(t, "localhost:9998", addr, "Returned address should be the same as registered client address")
253 | assert.Equal(t, helpers.SHA256([]byte("TMP_Token"+"NewClient")), token, "Returned token should be equal to the hash of clients id")
254 |
255 | path := fmt.Sprintf("./inboxes/%s", "NewClient")
256 | exists, err := helpers.DirExists(path)
257 | if err != nil {
258 | t.Fatal(err)
259 | }
260 | assert.True(t, exists, "When a new client is registered an inbox should be created")
261 | }
262 |
263 | func TestProviderServer_HandleAssignRequest(t *testing.T) {
264 | clientListener, err := createFakeClientListener("localhost", "9999")
265 | defer clientListener.Close()
266 |
267 | newClient := config.ClientConfig{Id: "ClientXYZ", Host: "localhost", Port: "9999", PubKey: nil}
268 | bNewClient, err := proto.Marshal(&newClient)
269 | if err != nil {
270 | t.Fatal(err)
271 | }
272 | err = providerServer.handleAssignRequest(bNewClient)
273 | if err != nil {
274 | t.Fatal(err)
275 | }
276 | }
277 |
278 | func createTestPacket(t *testing.T) *sphinx.SphinxPacket {
279 | path := config.E2EPath{IngressProvider: providerServer.config, Mixes: []config.MixConfig{mixServer.config}, EgressProvider: providerServer.config}
280 | sphinxPacket, err := sphinx.PackForwardMessage(elliptic.P224(), path, []float64{0.1, 0.2, 0.3}, "Hello world")
281 | if err != nil {
282 | t.Fatal(err)
283 | return nil
284 | }
285 | return &sphinxPacket
286 | }
287 |
288 | func TestProviderServer_ReceivedPacket(t *testing.T) {
289 | sphinxPacket := createTestPacket(t)
290 | bSphinxPacket, err := proto.Marshal(sphinxPacket)
291 | if err != nil {
292 | t.Fatal(err)
293 | }
294 | err = providerServer.receivedPacket(bSphinxPacket)
295 | if err != nil {
296 | t.Fatal(err)
297 | }
298 | }
299 |
300 | func TestProviderServer_HandleConnection(t *testing.T) {
301 | serverConn, _ := net.Pipe()
302 | errs := make(chan error, 1)
303 | // serverConn.Write([]byte("test"))
304 | go func() {
305 | providerServer.handleConnection(serverConn, errs)
306 | err := <-errs
307 | if err != nil {
308 | t.Fatal(err)
309 | }
310 | serverConn.Close()
311 | }()
312 | serverConn.Close()
313 |
314 | }
315 |
--------------------------------------------------------------------------------
/sphinx/crypto.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package sphinx
16 |
17 | import (
18 | "crypto/aes"
19 | "crypto/cipher"
20 | "crypto/elliptic"
21 | "crypto/hmac"
22 | "crypto/rand"
23 | "crypto/sha256"
24 |
25 | "math/big"
26 | )
27 |
28 | func AES_CTR(key, plaintext []byte) ([]byte, error) {
29 |
30 | ciphertext := make([]byte, len(plaintext))
31 |
32 | iv := []byte("0000000000000000")
33 | //if _, err := io.ReadFull(crand.Reader, iv); err != nil {
34 | // panic(err)
35 | //}
36 |
37 | block, err := aes.NewCipher(key)
38 | if err != nil {
39 | return nil, err
40 | }
41 |
42 | stream := cipher.NewCTR(block, iv)
43 | stream.XORKeyStream(ciphertext, plaintext)
44 |
45 | return ciphertext, nil
46 | }
47 |
48 | func hash(arg []byte) []byte {
49 |
50 | h := sha256.New()
51 | h.Write(arg)
52 |
53 | return h.Sum(nil)
54 | }
55 |
56 | func Hmac(key, message []byte) []byte {
57 | mac := hmac.New(sha256.New, key)
58 | mac.Write(message)
59 | return mac.Sum(nil)
60 | }
61 |
62 | func GenerateKeyPair() ([]byte, []byte, error) {
63 | priv, x, y, err := elliptic.GenerateKey(elliptic.P224(), rand.Reader)
64 |
65 | if err != nil {
66 | return nil, nil, err
67 | }
68 |
69 | return elliptic.Marshal(elliptic.P224(), x, y), priv, nil
70 | }
71 |
72 | func KDF(key []byte) []byte {
73 | return hash(key)[:K]
74 | }
75 |
76 | func bytesToBigNum(curve elliptic.Curve, value []byte) *big.Int {
77 | nBig := new(big.Int)
78 | nBig.SetBytes(value)
79 |
80 | return new(big.Int).Mod(nBig, curve.Params().P)
81 | }
82 |
83 | func randomBigInt(curve *elliptic.CurveParams) (big.Int, error) {
84 | nBig, err := rand.Int(rand.Reader, curve.Params().P)
85 | if err != nil {
86 | return big.Int{}, err
87 | }
88 | return *nBig, nil
89 | }
90 |
91 | func expo(base []byte, exp []big.Int) []byte {
92 | x := exp[0]
93 | for _, val := range exp[1:] {
94 | x = *big.NewInt(0).Mul(&x, &val)
95 | }
96 |
97 | baseX, baseY := elliptic.Unmarshal(elliptic.P224(), base)
98 | resultX, resultY := curve.Params().ScalarMult(baseX, baseY, x.Bytes())
99 | return elliptic.Marshal(curve, resultX, resultY)
100 | }
101 |
102 | func expoGroupBase(curve elliptic.Curve, exp []big.Int) []byte {
103 | x := exp[0]
104 |
105 | for _, val := range exp[1:] {
106 | x = *big.NewInt(0).Mul(&x, &val)
107 | }
108 |
109 | resultX, resultY := curve.Params().ScalarBaseMult(x.Bytes())
110 | return elliptic.Marshal(curve, resultX, resultY)
111 |
112 | }
113 |
114 | func computeMac(key, data []byte) []byte {
115 | return Hmac(key, data)
116 | }
117 |
--------------------------------------------------------------------------------
/sphinx/sphinx.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /* Package sphinx implements the library of a cryptographic packet format,
16 | which can be used to secure the content as well as the metadata of the transported
17 | messages.
18 | */
19 |
20 | package sphinx
21 |
22 | import (
23 | "anonymous-messaging/config"
24 | "anonymous-messaging/logging"
25 |
26 | "crypto/aes"
27 | "crypto/cipher"
28 | "crypto/elliptic"
29 |
30 | "github.com/protobuf/proto"
31 |
32 | "bytes"
33 | "errors"
34 | "math/big"
35 | "strings"
36 | )
37 |
38 | var curve = elliptic.P224()
39 | var logLocal = logging.PackageLogger()
40 |
41 | const (
42 | K = 16
43 | R = 5
44 | headerLength = 192
45 | lastHopFlag = "\xf0"
46 | relayFlag = "\xf1"
47 | )
48 |
49 | // PackForwardMessage encapsulates the given message into the cryptographic Sphinx packet format.
50 | // As arguments the function takes the path, consisting of the sequence of nodes the packet should traverse
51 | // and the destination of the message, a set of delays and the information about the curve used to perform cryptographic
52 | // operations.
53 | // In order to encapsulate the message PackForwardMessage computes two parts of the packet - the header and
54 | // the encrypted payload. If creating of any of the packet block failed, an error is returned. Otherwise,
55 | // a Sphinx packet format is returned.
56 | func PackForwardMessage(curve elliptic.Curve, path config.E2EPath, delays []float64, message string) (SphinxPacket, error) {
57 | nodes := []config.MixConfig{path.IngressProvider}
58 | nodes = append(nodes, path.Mixes...)
59 | nodes = append(nodes, path.EgressProvider)
60 | dest := path.Recipient
61 |
62 | asb, header, err := createHeader(curve, nodes, delays, dest)
63 | if err != nil {
64 | logLocal.WithError(err).Error("Error in PackForwardMessage - createHeader failed")
65 | return SphinxPacket{}, err
66 | }
67 |
68 | payload, err := encapsulateContent(asb, message)
69 | if err != nil {
70 | logLocal.WithError(err).Error("Error in PackForwardMessage - encapsulateContent failed")
71 | return SphinxPacket{}, err
72 | }
73 | return SphinxPacket{Hdr: &header, Pld: payload}, nil
74 | }
75 |
76 | // createHeader builds the Sphinx packet header, consisting of three parts: the public element, the encapsulated routing information
77 | // and the message authentication code. createHeader layer encapsulates the routing information for each given node. The routing information
78 | // contains information where the packet should be forwarded next, how long it should be delayed by the node, and if relevant additional
79 | // auxiliary information. The message authentication code allows to detect tagging attacks.
80 | // createHeader computes the secret shared key between sender and the nodes and destination, which are used as keys for encryption.
81 | // createHeader returns the header and a list of the initial elements, used for creating the header. If any operation was unsuccessful
82 | // createHeader returns an error.
83 | func createHeader(curve elliptic.Curve, nodes []config.MixConfig, delays []float64, dest config.ClientConfig) ([]HeaderInitials, Header, error) {
84 |
85 | x, err := randomBigInt(curve.Params())
86 |
87 | if err != nil {
88 | logLocal.WithError(err).Error("Error in createHeader - randomBigInt failed")
89 | return nil, Header{}, err
90 | }
91 |
92 | asb, err := getSharedSecrets(curve, nodes, x)
93 | if err != nil {
94 | logLocal.WithError(err).Error("Error in createHeader - getSharedSecrets failed")
95 | return nil, Header{}, err
96 | }
97 |
98 | if len(asb) != len(nodes) {
99 | logLocal.WithError(err).Error("Error in createHeader - wrong number of shared secrets failed")
100 | return nil, Header{}, errors.New(" the number of shared secrets should be the same as the number of traversed nodes")
101 | }
102 |
103 | var commands []Commands
104 | for i, _ := range nodes {
105 | var c Commands
106 | if i == len(nodes)-1 {
107 | c = Commands{Delay: delays[i], Flag: lastHopFlag}
108 | } else {
109 | c = Commands{Delay: delays[i], Flag: relayFlag}
110 | }
111 | commands = append(commands, c)
112 | }
113 |
114 | header, err := encapsulateHeader(asb, nodes, commands, dest)
115 | if err != nil {
116 | logLocal.WithError(err).Error("Error in createHeader - encapsulateHeader failed")
117 | return nil, Header{}, err
118 | }
119 | return asb, header, nil
120 |
121 | }
122 |
123 | // encapsulateHeader layer encrypts the meta-data of the packet, containing information about the
124 | // sequence of nodes the packet should traverse before reaching the destination, and message authentication codes,
125 | // given the pre-computed shared keys which are used for encryption.
126 | // encapsulateHeader returns the Header, or an error if any internal cryptographic of parsing operation failed.
127 | func encapsulateHeader(asb []HeaderInitials, nodes []config.MixConfig, commands []Commands, destination config.ClientConfig) (Header, error) {
128 | finalHop := RoutingInfo{NextHop: &Hop{Id: destination.Id, Address: destination.Host + ":" + destination.Port, PubKey: []byte{}}, RoutingCommands: &commands[len(commands)-1], NextHopMetaData: []byte{}, Mac: []byte{}}
129 |
130 | finalHopBytes, err := proto.Marshal(&finalHop)
131 | if err != nil {
132 | return Header{}, err
133 | }
134 |
135 | encFinalHop, err := AES_CTR(KDF(asb[len(asb)-1].SecretHash), finalHopBytes)
136 | if err != nil {
137 | logLocal.WithError(err).Error("Error in encapsulateHeader - AES_CTR encryption failed")
138 | return Header{}, err
139 | }
140 |
141 | mac := computeMac(KDF(asb[len(asb)-1].SecretHash), encFinalHop)
142 |
143 | routingCommands := [][]byte{encFinalHop}
144 |
145 | var encRouting []byte
146 | for i := len(nodes) - 2; i >= 0; i-- {
147 | nextNode := nodes[i+1]
148 | routing := RoutingInfo{NextHop: &Hop{Id: nextNode.Id, Address: nextNode.Host + ":" + nextNode.Port, PubKey: nodes[i+1].PubKey}, RoutingCommands: &commands[i], NextHopMetaData: routingCommands[len(routingCommands)-1], Mac: mac}
149 |
150 | encKey := KDF(asb[i].SecretHash)
151 | routingBytes, err := proto.Marshal(&routing)
152 |
153 | if err != nil {
154 | return Header{}, err
155 | }
156 |
157 | encRouting, err = AES_CTR(encKey, routingBytes)
158 | if err != nil {
159 | return Header{}, err
160 | }
161 |
162 | routingCommands = append(routingCommands, encRouting)
163 | mac = computeMac(KDF(asb[i].SecretHash), encRouting)
164 |
165 | }
166 | return Header{Alpha: asb[0].Alpha, Beta: encRouting, Mac: mac}, nil
167 |
168 | }
169 |
170 | // encapsulateContent layer encrypts the given messages using a set of shared keys
171 | // and the AES_CTR encryption.
172 | // encapsulateContent returns the encrypted payload in byte representation. If the AES_CTR
173 | // encryption failed encapsulateContent returns an error.
174 | func encapsulateContent(asb []HeaderInitials, message string) ([]byte, error) {
175 |
176 | enc := []byte(message)
177 | err := error(nil)
178 | for i := len(asb) - 1; i >= 0; i-- {
179 | sharedKey := KDF(asb[i].SecretHash)
180 | enc, err = AES_CTR(sharedKey, enc)
181 | if err != nil {
182 | logLocal.WithError(err).Error("Error in encapsulateContent - AES_CTR encryption failed")
183 | return nil, err
184 | }
185 |
186 | }
187 | return enc, nil
188 | }
189 |
190 | // getSharedSecrets computes a sequence of HeaderInitial values, containing the initial elements,
191 | // shared secrets and blinding factors for each node on the path. As input getSharedSecrets takes the initial
192 | // secret value, the list of nodes, and the curve in which the cryptographic operations are performed.
193 | // getSharedSecrets returns the list of computed HeaderInitials or an error.
194 | func getSharedSecrets(curve elliptic.Curve, nodes []config.MixConfig, initialVal big.Int) ([]HeaderInitials, error) {
195 |
196 | blindFactors := []big.Int{initialVal}
197 | var tuples []HeaderInitials
198 |
199 | for _, n := range nodes {
200 |
201 | alpha := expoGroupBase(curve, blindFactors)
202 |
203 | s := expo(n.PubKey, blindFactors)
204 | aes_s := KDF(s)
205 |
206 | blinder, err := computeBlindingFactor(curve, aes_s)
207 | if err != nil {
208 | logLocal.WithError(err).Error("Error in getSharedSecrets - computeBlindingFactor failed")
209 | return nil, err
210 | }
211 |
212 | blindFactors = append(blindFactors, *blinder)
213 | tuples = append(tuples, HeaderInitials{Alpha: alpha, Secret: s, Blinder: blinder.Bytes(), SecretHash: aes_s})
214 | }
215 | return tuples, nil
216 |
217 | }
218 |
219 | // TODO: computeFillers needs to be fixed
220 | func computeFillers(nodes []config.MixConfig, tuples []HeaderInitials) (string, error) {
221 |
222 | filler := ""
223 | minLen := headerLength - 32
224 | for i := 1; i < len(nodes); i++ {
225 | base := filler + strings.Repeat("\x00", K)
226 | kx, err := computeSharedSecretHash(tuples[i-1].SecretHash, []byte("hrhohrhohrhohrho"))
227 | if err != nil {
228 | return "", err
229 | }
230 | mx := strings.Repeat("\x00", minLen) + base
231 |
232 | xorVal, err := AES_CTR([]byte(kx), []byte(mx))
233 | if err != nil {
234 | logLocal.WithError(err).Error("Error in computeFillers - AES_CTR failed")
235 | return "", err
236 | }
237 |
238 | filler = BytesToString(xorVal)
239 | filler = filler[minLen:]
240 |
241 | minLen = minLen - K
242 | }
243 |
244 | return filler, nil
245 |
246 | }
247 |
248 | // computeBlindingFactor computes the blinding factor extracted from the
249 | // shared secrets. Blinding factors allow both the sender and intermediate nodes
250 | // recompute the shared keys used at each hop of the message processing.
251 | // computeBlindingFactor returns a value of a blinding factor or an error.
252 | func computeBlindingFactor(curve elliptic.Curve, key []byte) (*big.Int, error) {
253 | iv := []byte("initialvector000")
254 | blinderBytes, err := computeSharedSecretHash(key, iv)
255 |
256 | if err != nil {
257 | logLocal.WithError(err).Error("Error in computeBlindingFactor - computeSharedSecretHash failed")
258 | return &big.Int{}, err
259 | }
260 |
261 | return bytesToBigNum(curve, blinderBytes), nil
262 | }
263 |
264 | // computeSharedSecretHash computes the hash value of the shared secret key
265 | // using AES_CTR.
266 | func computeSharedSecretHash(key []byte, iv []byte) ([]byte, error) {
267 | aesCipher, err := aes.NewCipher(key)
268 |
269 | if err != nil {
270 | logLocal.WithError(err).Error("Error in computeSharedSecretHash - creating new AES cipher failed")
271 | return nil, err
272 | }
273 |
274 | stream := cipher.NewCTR(aesCipher, iv)
275 | plaintext := []byte("0000000000000000")
276 |
277 | ciphertext := make([]byte, len(plaintext))
278 | stream.XORKeyStream(ciphertext, plaintext)
279 |
280 | return ciphertext, nil
281 | }
282 |
283 | // ProcessSphinxPacket processes the sphinx packet using the given private key.
284 | // ProcessSphinxPacket unwraps one layer of both the header and the payload encryption.
285 | // ProcessSphinxPacket returns a new packet and the routing information which should
286 | // be used by the processing node. If any cryptographic or parsing operation failed ProcessSphinxPacket
287 | // returns an error.
288 | func ProcessSphinxPacket(packetBytes []byte, privKey []byte) (Hop, Commands, []byte, error) {
289 |
290 | var packet SphinxPacket
291 | err := proto.Unmarshal(packetBytes, &packet)
292 |
293 | if err != nil {
294 | logLocal.WithError(err).Error("Error in ProcessSphinxPacket - unmarshal of packet failed")
295 | return Hop{}, Commands{}, nil, err
296 | }
297 |
298 | hop, commands, newHeader, err := ProcessSphinxHeader(*packet.Hdr, privKey)
299 | if err != nil {
300 | logLocal.WithError(err).Error("Error in ProcessSphinxPacket - ProcessSphinxHeader failed")
301 | return Hop{}, Commands{}, nil, err
302 | }
303 |
304 | newPayload, err := ProcessSphinxPayload(packet.Hdr.Alpha, packet.Pld, privKey)
305 | if err != nil {
306 | logLocal.WithError(err).Error("Error in ProcessSphinxPacket - ProcessSphinxPayload failed")
307 | return Hop{}, Commands{}, nil, err
308 | }
309 |
310 | newPacket := SphinxPacket{Hdr: &newHeader, Pld: newPayload}
311 | newPacketBytes, err := proto.Marshal(&newPacket)
312 | if err != nil {
313 | logLocal.WithError(err).Error("Error in ProcessSphinxPacket - marshal of packet failed")
314 | return Hop{}, Commands{}, nil, err
315 | }
316 |
317 | return hop, commands, newPacketBytes, nil
318 | }
319 |
320 | // ProcessSphinxHeader unwraps one layer of encryption from the header of a sphinx packet.
321 | // ProcessSphinxHeader recomputes the shared key and checks whether the message authentication code is valid.
322 | // If not, the packet is dropped and error is returned. If MAC checking was passed successfully ProcessSphinxHeader
323 | // performs the AES_CTR decryption, recomputes the blinding factor and updates the init public element from the header.
324 | // Next, ProcessSphinxHeader extracts the routing information from the decrypted packet and returns it, together with the
325 | // updated init public element. If any crypto or parsing operation failed ProcessSphinxHeader returns an error.
326 | func ProcessSphinxHeader(packet Header, privKey []byte) (Hop, Commands, Header, error) {
327 |
328 | alpha := packet.Alpha
329 | beta := packet.Beta
330 | mac := packet.Mac
331 |
332 | curve := elliptic.P224()
333 | alphaX, alphaY := elliptic.Unmarshal(curve, alpha)
334 | sharedSecretX, sharedSecretY := curve.Params().ScalarMult(alphaX, alphaY, privKey)
335 | sharedSecret := elliptic.Marshal(curve, sharedSecretX, sharedSecretY)
336 |
337 | aes_s := KDF(sharedSecret)
338 | encKey := KDF(aes_s)
339 |
340 | recomputedMac := computeMac(KDF(aes_s), beta)
341 |
342 | if bytes.Compare(recomputedMac, mac) != 0 {
343 | return Hop{}, Commands{}, Header{}, errors.New("packet processing error: MACs are not matching")
344 | }
345 |
346 | blinder, err := computeBlindingFactor(curve, aes_s)
347 | if err != nil {
348 | logLocal.WithError(err).Error("Error in ProcessSphinxHeader - computeBlindingFactor failed")
349 | return Hop{}, Commands{}, Header{}, err
350 | }
351 |
352 | newAlphaX, newAlphaY := curve.Params().ScalarMult(alphaX, alphaY, blinder.Bytes())
353 | newAlpha := elliptic.Marshal(curve, newAlphaX, newAlphaY)
354 |
355 | decBeta, err := AES_CTR(encKey, beta)
356 | if err != nil {
357 | logLocal.WithError(err).Error("Error in ProcessSphinxHeader - AES_CTR failed")
358 | return Hop{}, Commands{}, Header{}, err
359 | }
360 |
361 | var routingInfo RoutingInfo
362 | err = proto.Unmarshal(decBeta, &routingInfo)
363 | if err != nil {
364 | logLocal.WithError(err).Error("Error in ProcessSphinxHeader - unmarshal of beta failed")
365 | return Hop{}, Commands{}, Header{}, err
366 | }
367 | nextHop, commands, nextBeta, nextMac := readBeta(routingInfo)
368 |
369 | return nextHop, commands, Header{Alpha: newAlpha, Beta: nextBeta, Mac: nextMac}, nil
370 | }
371 |
372 | // readBeta extracts all the fields from the RoutingInfo structure
373 | func readBeta(beta RoutingInfo) (Hop, Commands, []byte, []byte) {
374 | nextHop := *beta.NextHop
375 | commands := *beta.RoutingCommands
376 | nextBeta := beta.NextHopMetaData
377 | nextMac := beta.Mac
378 |
379 | return nextHop, commands, nextBeta, nextMac
380 | }
381 |
382 | // ProcessSphinxPayload unwraps a single layer of the encryption from the sphinx packet payload.
383 | // ProcessSphinxPayload first recomputes the shared secret which is used to perform the AES_CTR decryption.
384 | // ProcessSphinxPayload returns the new packet payload or an error if the decryption failed.
385 | func ProcessSphinxPayload(alpha []byte, payload []byte, privKey []byte) ([]byte, error) {
386 |
387 | curve := elliptic.P224()
388 | alphaX, alphaY := elliptic.Unmarshal(curve, alpha)
389 | sharedSecretX, sharedSecretY := curve.Params().ScalarMult(alphaX, alphaY, privKey)
390 | sharedSecret := elliptic.Marshal(curve, sharedSecretX, sharedSecretY)
391 |
392 | aes_s := KDF(sharedSecret)
393 | decKey := KDF(aes_s)
394 |
395 | decPayload, err := AES_CTR(decKey, payload)
396 | if err != nil {
397 | logLocal.WithError(err).Error("Error in ProcessSphinxPayload - AES_CTR decryption failed")
398 | return nil, err
399 | }
400 |
401 | return decPayload, nil
402 | }
403 |
--------------------------------------------------------------------------------
/sphinx/sphinx_structs.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package sphinx;
3 |
4 |
5 | message SphinxPacket {
6 | Header Hdr = 1;
7 | bytes Pld = 2;
8 | }
9 |
10 | message Header {
11 | bytes Alpha = 1;
12 | bytes Beta = 2;
13 | bytes Mac = 3;
14 | }
15 |
16 | message Hop {
17 | string Id = 1;
18 | string Address = 2;
19 | bytes PubKey = 3;
20 | }
21 |
22 | message RoutingInfo {
23 | Hop NextHop = 1;
24 | Commands RoutingCommands = 2;
25 | bytes NextHopMetaData = 3;
26 | bytes Mac = 4;
27 | }
28 |
29 | message Commands {
30 | double Delay = 1;
31 | string Flag = 2;
32 | }
33 |
34 | message HeaderInitials {
35 | bytes Alpha = 1;
36 | bytes Secret = 2;
37 | bytes Blinder = 3;
38 | bytes SecretHash = 4;
39 | }
--------------------------------------------------------------------------------
/sphinx/sphinx_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package sphinx
16 |
17 | import (
18 | "anonymous-messaging/config"
19 |
20 | "crypto/aes"
21 | "crypto/elliptic"
22 | "crypto/rand"
23 |
24 | "github.com/protobuf/proto"
25 | "github.com/stretchr/testify/assert"
26 |
27 | "fmt"
28 | "math/big"
29 | "os"
30 | "testing"
31 | )
32 |
33 | func TestMain(m *testing.M) {
34 | curve = elliptic.P224()
35 |
36 | os.Exit(m.Run())
37 | }
38 |
39 | func TestExpoSingleValue(t *testing.T) {
40 | _, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
41 |
42 | if err != nil {
43 | t.Error(err)
44 | }
45 |
46 | randomPoint := elliptic.Marshal(curve, x, y)
47 | nBig := *big.NewInt(2)
48 | exp := []big.Int{nBig}
49 |
50 | result := expo(randomPoint, exp)
51 | expectedX, expectedY := curve.ScalarMult(x, y, nBig.Bytes())
52 | assert.Equal(t, elliptic.Marshal(curve, expectedX, expectedY), result)
53 |
54 | }
55 |
56 | func TestExpoMultipleValue(t *testing.T) {
57 | _, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
58 |
59 | if err != nil {
60 | t.Error(err)
61 | }
62 | randomPoint := elliptic.Marshal(curve, x, y)
63 |
64 | var exp []big.Int
65 | for i := 1; i <= 5; i++ {
66 | exp = append(exp, *big.NewInt(int64(i)))
67 | }
68 |
69 | result := expo(randomPoint, exp)
70 | expectedX, expectedY := curve.ScalarMult(x, y, big.NewInt(120).Bytes())
71 | assert.Equal(t, elliptic.Marshal(curve, expectedX, expectedY), result)
72 | }
73 |
74 | func TestExpoBaseSingleValue(t *testing.T) {
75 | nBig := *big.NewInt(2)
76 | exp := []big.Int{nBig}
77 |
78 | result := expoGroupBase(curve, exp)
79 | expectedX, expectedY := curve.ScalarBaseMult(nBig.Bytes())
80 |
81 | assert.Equal(t, elliptic.Marshal(curve, expectedX, expectedY), result)
82 | }
83 |
84 | func TestExpoBaseMultipleValue(t *testing.T) {
85 | var exp []big.Int
86 | for i := 1; i <= 3; i++ {
87 | exp = append(exp, *big.NewInt(int64(i)))
88 | }
89 | result := expoGroupBase(curve, exp)
90 | expectedX, expectedY := curve.ScalarBaseMult(big.NewInt(6).Bytes())
91 | assert.Equal(t, elliptic.Marshal(curve, expectedX, expectedY), result)
92 |
93 | }
94 |
95 | func TestHash(t *testing.T) {
96 | _, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
97 |
98 | if err != nil {
99 | t.Error(err)
100 | }
101 |
102 | randomPoint := elliptic.Marshal(curve, x, y)
103 | hVal := hash(randomPoint)
104 |
105 | assert.Equal(t, 32, len(hVal))
106 |
107 | }
108 |
109 | func TestBytesToBigNum(t *testing.T) {
110 | bytes := big.NewInt(100).Bytes()
111 | result := *bytesToBigNum(curve, bytes)
112 | assert.Equal(t, *big.NewInt(100), result)
113 | }
114 |
115 | func TestGetAESKey(t *testing.T) {
116 | _, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
117 |
118 | if err != nil {
119 | t.Error(err)
120 | }
121 |
122 | randomPoint := elliptic.Marshal(curve, x, y)
123 | aesKey := KDF(randomPoint)
124 | assert.Equal(t, aes.BlockSize, len(aesKey))
125 |
126 | }
127 |
128 | func TestComputeBlindingFactor(t *testing.T) {
129 | generator := elliptic.Marshal(curve, curve.Params().Gx, curve.Params().Gy)
130 |
131 | key := hash(generator)
132 | b, err := computeBlindingFactor(curve, key)
133 | if err != nil {
134 | t.Error(err)
135 | }
136 |
137 | expected := new(big.Int)
138 | expected.SetString("252286146058081748716688845275111486959", 10)
139 |
140 | assert.Equal(t, expected, b)
141 | }
142 |
143 | func TestGetSharedSecrets(t *testing.T) {
144 |
145 | pub1, _, err := GenerateKeyPair()
146 | pub2, _, err := GenerateKeyPair()
147 | pub3, _, err := GenerateKeyPair()
148 | if err != nil {
149 | t.Error(err)
150 | }
151 |
152 | pubs := [][]byte{pub1, pub2, pub3}
153 |
154 | m1 := config.MixConfig{Id: "", Host: "", Port: "", PubKey: pub1}
155 | m2 := config.MixConfig{Id: "", Host: "", Port: "", PubKey: pub2}
156 | m3 := config.MixConfig{Id: "", Host: "", Port: "", PubKey: pub3}
157 |
158 | nodes := []config.MixConfig{m1, m2, m3}
159 |
160 | x := big.NewInt(100)
161 |
162 | result, err := getSharedSecrets(curve, nodes, *x)
163 | if err != nil {
164 | t.Error(err)
165 | }
166 |
167 | var expected []HeaderInitials
168 | blindFactors := []big.Int{*x}
169 |
170 | v := x
171 | alpha0X, alpha0Y := curve.Params().ScalarMult(curve.Params().Gx, curve.Params().Gy, v.Bytes())
172 | alpha0 := elliptic.Marshal(curve, alpha0X, alpha0Y)
173 | s0 := expo(pubs[0], blindFactors)
174 | aesS0 := KDF(s0)
175 | b0, err := computeBlindingFactor(curve, aesS0)
176 | if err != nil {
177 | t.Error(err)
178 | }
179 |
180 | expected = append(expected, HeaderInitials{Alpha: alpha0, Secret: s0, Blinder: b0.Bytes(), SecretHash: aesS0})
181 | blindFactors = append(blindFactors, *b0)
182 |
183 | v = big.NewInt(0).Mul(v, b0)
184 | alpha1X, alpha1Y := curve.Params().ScalarMult(curve.Params().Gx, curve.Params().Gy, v.Bytes())
185 | alpha1 := elliptic.Marshal(curve, alpha1X, alpha1Y)
186 | s1 := expo(pubs[1], blindFactors)
187 | aesS1 := KDF(s1)
188 | b1, err := computeBlindingFactor(curve, aesS1)
189 | if err != nil {
190 | t.Error(err)
191 | }
192 |
193 | expected = append(expected, HeaderInitials{Alpha: alpha1, Secret: s1, Blinder: b1.Bytes(), SecretHash: aesS1})
194 | blindFactors = append(blindFactors, *b1)
195 |
196 | v = big.NewInt(0).Mul(v, b1)
197 | alpha2X, alpha2Y := curve.Params().ScalarMult(curve.Params().Gx, curve.Params().Gy, v.Bytes())
198 | alpha2 := elliptic.Marshal(curve, alpha2X, alpha2Y)
199 | s2 := expo(pubs[2], blindFactors)
200 | aesS2 := KDF(s2)
201 | b2, err := computeBlindingFactor(curve, aesS2)
202 | if err != nil {
203 | t.Error(err)
204 | }
205 |
206 | expected = append(expected, HeaderInitials{Alpha: alpha2, Secret: s2, Blinder: b2.Bytes(), SecretHash: aesS2})
207 | blindFactors = append(blindFactors, *b2)
208 |
209 | assert.Equal(t, expected, result)
210 | }
211 |
212 | func TestComputeFillers(t *testing.T) {
213 |
214 | g := elliptic.Marshal(curve, curve.Params().Gx, curve.Params().Gy)
215 | h1 := HeaderInitials{Alpha: []byte{}, Secret: g, Blinder: []byte{}, SecretHash: []byte("1111111111111111")}
216 | h2 := HeaderInitials{Alpha: []byte{}, Secret: g, Blinder: []byte{}, SecretHash: []byte("1111111111111111")}
217 | h3 := HeaderInitials{Alpha: []byte{}, Secret: g, Blinder: []byte{}, SecretHash: []byte("1111111111111111")}
218 | tuples := []HeaderInitials{h1, h2, h3}
219 |
220 | pub1, _, err := GenerateKeyPair()
221 | pub2, _, err := GenerateKeyPair()
222 | pub3, _, err := GenerateKeyPair()
223 | if err != nil {
224 | t.Error(err)
225 | }
226 |
227 | m1 := config.MixConfig{Id: "", Host: "", Port: "", PubKey: pub1}
228 | m2 := config.MixConfig{Id: "", Host: "", Port: "", PubKey: pub2}
229 | m3 := config.MixConfig{Id: "", Host: "", Port: "", PubKey: pub3}
230 |
231 | fillers, err := computeFillers([]config.MixConfig{m1, m2, m3}, tuples)
232 | if err != nil {
233 | t.Error(err)
234 | }
235 |
236 | fmt.Println("FILLER: ", fillers)
237 |
238 | }
239 |
240 | func TestXorBytesPass(t *testing.T) {
241 | result := XorBytes([]byte("00101"), []byte("10110"))
242 | assert.Equal(t, []byte{1, 0, 0, 1, 1}, result)
243 | }
244 |
245 | func TestXorBytesFail(t *testing.T) {
246 | result := XorBytes([]byte("00101"), []byte("10110"))
247 | assert.NotEqual(t, []byte("00000"), result)
248 | }
249 |
250 | func TestEncapsulateHeader(t *testing.T) {
251 |
252 | pub1, _, err := GenerateKeyPair()
253 | pub2, _, err := GenerateKeyPair()
254 | pub3, _, err := GenerateKeyPair()
255 | pubD, _, err := GenerateKeyPair()
256 | if err != nil {
257 | t.Error(err)
258 | }
259 |
260 | m1 := config.NewMixConfig("Node1", "localhost", "3331", pub1)
261 | m2 := config.NewMixConfig("Node2", "localhost", "3332", pub2)
262 | m3 := config.NewMixConfig("Node3", "localhost", "3333", pub3)
263 |
264 | nodes := []config.MixConfig{m1, m2, m3}
265 |
266 | c1 := Commands{Delay: 0.34, Flag: "0"}
267 | c2 := Commands{Delay: 0.25, Flag: "1"}
268 | c3 := Commands{Delay: 1.10, Flag: "1"}
269 | commands := []Commands{c1, c2, c3}
270 |
271 | x := big.NewInt(100)
272 | sharedSecrets, err := getSharedSecrets(curve, nodes, *x)
273 | if err != nil {
274 | t.Error(err)
275 | }
276 |
277 | actualHeader, err := encapsulateHeader(sharedSecrets, nodes, commands,
278 | config.ClientConfig{Id: "DestinationId", Host: "DestinationAddress", Port: "9998", PubKey: pubD})
279 | if err != nil {
280 | t.Error(err)
281 | }
282 |
283 | routing1 := RoutingInfo{NextHop: &Hop{"DestinationId", "DestinationAddress:9998", []byte{}}, RoutingCommands: &c3,
284 | NextHopMetaData: []byte{}, Mac: []byte{}}
285 |
286 | routing1Bytes, err := proto.Marshal(&routing1)
287 | if err != nil {
288 | t.Error(err)
289 | }
290 |
291 | enc_routing1, err := AES_CTR(KDF(sharedSecrets[2].SecretHash), routing1Bytes)
292 | if err != nil {
293 | t.Error(err)
294 | }
295 |
296 | mac1 := computeMac(KDF(sharedSecrets[2].SecretHash), enc_routing1)
297 |
298 | routing2 := RoutingInfo{NextHop: &Hop{"Node3", "localhost:3333", pub3}, RoutingCommands: &c2,
299 | NextHopMetaData: enc_routing1, Mac: mac1}
300 |
301 | routing2Bytes, err := proto.Marshal(&routing2)
302 | if err != nil {
303 | t.Error(err)
304 | }
305 |
306 | enc_routing2, err := AES_CTR(KDF(sharedSecrets[1].SecretHash), routing2Bytes)
307 | if err != nil {
308 | t.Error(err)
309 | }
310 |
311 | mac2 := computeMac(KDF(sharedSecrets[1].SecretHash), enc_routing2)
312 |
313 | expectedRouting := RoutingInfo{NextHop: &Hop{"Node2", "localhost:3332", pub2}, RoutingCommands: &c1,
314 | NextHopMetaData: enc_routing2, Mac: mac2}
315 |
316 | expectedRoutingBytes, err := proto.Marshal(&expectedRouting)
317 | if err != nil {
318 | t.Error(err)
319 | }
320 |
321 | enc_expectedRouting, err := AES_CTR(KDF(sharedSecrets[0].SecretHash), expectedRoutingBytes)
322 | if err != nil {
323 | t.Error(err)
324 | }
325 |
326 | mac3 := computeMac(KDF(sharedSecrets[0].SecretHash), enc_expectedRouting)
327 |
328 | expectedHeader := Header{sharedSecrets[0].Alpha, enc_expectedRouting, mac3}
329 |
330 | assert.Equal(t, expectedHeader, actualHeader)
331 | }
332 |
333 | func TestProcessSphinxHeader(t *testing.T) {
334 |
335 | pub1, priv1, err := GenerateKeyPair()
336 | pub2, _, err := GenerateKeyPair()
337 | pub3, _, err := GenerateKeyPair()
338 | if err != nil {
339 | t.Error(err)
340 | }
341 |
342 | c1 := Commands{Delay: 0.34}
343 | c2 := Commands{Delay: 0.25}
344 | c3 := Commands{Delay: 1.10}
345 |
346 | m1 := config.NewMixConfig("Node1", "localhost", "3331", pub1)
347 | m2 := config.NewMixConfig("Node2", "localhost", "3332", pub2)
348 | m3 := config.NewMixConfig("Node3", "localhost", "3333", pub3)
349 |
350 | nodes := []config.MixConfig{m1, m2, m3}
351 |
352 | x := big.NewInt(100)
353 | sharedSecrets, err := getSharedSecrets(curve, nodes, *x)
354 | if err != nil {
355 | t.Error(err)
356 | }
357 |
358 | // Intermediate steps, which are needed to check whether the processing of the header was correct
359 | routing1 := RoutingInfo{NextHop: &Hop{"DestinationId", "DestinationAddress", []byte{}}, RoutingCommands: &c3,
360 | NextHopMetaData: []byte{}, Mac: []byte{}}
361 |
362 | routing1Bytes, err := proto.Marshal(&routing1)
363 | if err != nil {
364 | t.Error(err)
365 | }
366 |
367 | enc_routing1, err := AES_CTR(KDF(sharedSecrets[2].SecretHash), routing1Bytes)
368 | if err != nil {
369 | t.Error(err)
370 | }
371 |
372 | mac1 := computeMac(KDF(sharedSecrets[2].SecretHash), enc_routing1)
373 |
374 | routing2 := RoutingInfo{NextHop: &Hop{"Node3", "localhost:3333", pub3}, RoutingCommands: &c2,
375 | NextHopMetaData: enc_routing1, Mac: mac1}
376 |
377 | routing2Bytes, err := proto.Marshal(&routing2)
378 | if err != nil {
379 | t.Error(err)
380 | }
381 |
382 | enc_routing2, err := AES_CTR(KDF(sharedSecrets[1].SecretHash), routing2Bytes)
383 | if err != nil {
384 | t.Error(err)
385 | }
386 |
387 | mac2 := computeMac(KDF(sharedSecrets[1].SecretHash), enc_routing2)
388 |
389 | routing3 := RoutingInfo{NextHop: &Hop{"Node2", "localhost:3332", pub2}, RoutingCommands: &c1,
390 | NextHopMetaData: enc_routing2, Mac: mac2}
391 |
392 | routing3Bytes, err := proto.Marshal(&routing3)
393 | if err != nil {
394 | t.Error(err)
395 | }
396 |
397 | enc_expectedRouting, err := AES_CTR(KDF(sharedSecrets[0].SecretHash), routing3Bytes)
398 | if err != nil {
399 | t.Error(err)
400 | }
401 |
402 | mac3 := computeMac(KDF(sharedSecrets[0].SecretHash), enc_expectedRouting)
403 |
404 | header := Header{sharedSecrets[0].Alpha, enc_expectedRouting, mac3}
405 |
406 | nextHop, newCommands, newHeader, err := ProcessSphinxHeader(header, priv1)
407 |
408 | if err != nil {
409 | t.Error(err)
410 | }
411 |
412 | assert.Equal(t, nextHop, Hop{Id: "Node2", Address: "localhost:3332", PubKey: pub2})
413 | assert.Equal(t, newCommands, c1)
414 | assert.Equal(t, newHeader, Header{Alpha: sharedSecrets[1].Alpha, Beta: enc_routing2, Mac: mac2})
415 |
416 | }
417 |
418 | func TestProcessSphinxPayload(t *testing.T) {
419 |
420 | message := "Plaintext message"
421 |
422 | pub1, priv1, err := GenerateKeyPair()
423 | pub2, priv2, err := GenerateKeyPair()
424 | pub3, priv3, err := GenerateKeyPair()
425 | if err != nil {
426 | t.Error(err)
427 | }
428 |
429 | m1 := config.NewMixConfig("Node1", "localhost", "3331", pub1)
430 | m2 := config.NewMixConfig("Node2", "localhost", "3332", pub2)
431 | m3 := config.NewMixConfig("Node3", "localhost", "3333", pub3)
432 |
433 | nodes := []config.MixConfig{m1, m2, m3}
434 |
435 | x := big.NewInt(100)
436 | asb, err := getSharedSecrets(curve, nodes, *x)
437 | if err != nil {
438 | t.Error(err)
439 | }
440 |
441 | encMsg, err := encapsulateContent(asb, message)
442 | if err != nil {
443 | t.Error(err)
444 | }
445 |
446 | var decMsg []byte
447 |
448 | decMsg = encMsg
449 | privs := [][]byte{priv1, priv2, priv3}
450 | for i, v := range privs {
451 | decMsg, err = ProcessSphinxPayload(asb[i].Alpha, decMsg, v)
452 | if err != nil {
453 | t.Error(err)
454 | }
455 | }
456 | assert.Equal(t, []byte(message), decMsg)
457 | }
458 |
--------------------------------------------------------------------------------
/sphinx/utils.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Loopix-Messaging Authors
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package sphinx
16 |
17 | import "fmt"
18 |
19 | func XorBytes(b1, b2 []byte) []byte {
20 |
21 | if len(b1) != len(b2) {
22 | panic("String cannot be xored if their length is different")
23 | }
24 |
25 | b := make([]byte, len(b1))
26 | for i, _ := range b {
27 | b[i] = b1[i] ^ b2[i]
28 | }
29 | return b
30 | }
31 |
32 | func BytesToString(b []byte) string {
33 | result := ""
34 | for _, v := range b {
35 | s := fmt.Sprintf("%v", v)
36 | result = result + s
37 | }
38 | return result
39 | }
40 |
--------------------------------------------------------------------------------
/vendor/vendor.json:
--------------------------------------------------------------------------------
1 | {
2 | "comment": "",
3 | "ignore": "test",
4 | "package": [
5 | {
6 | "checksumSHA1": "OFu4xJEIjiI8Suu+j/gabfp+y6Q=",
7 | "origin": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew",
8 | "path": "github.com/davecgh/go-spew/spew",
9 | "revision": "2aa2c176b9dab406a6970f6a55f513e8a8c8b18f",
10 | "revisionTime": "2017-08-14T20:04:35Z"
11 | },
12 | {
13 | "checksumSHA1": "/5JpULDw51Em+1+OJHcfDkbw/e0=",
14 | "path": "github.com/golang/mock/gomock",
15 | "revision": "b3e60bcdc577185fce3cf625fc96b62857ce5574",
16 | "revisionTime": "2017-12-16T01:26:12Z"
17 | },
18 | {
19 | "checksumSHA1": "QmAzbypFddS3Tl/HDca0X0zXZ/M=",
20 | "path": "github.com/jmoiron/sqlx",
21 | "revision": "de8647470aafe4854c976707c431dbe1eb2822c6",
22 | "revisionTime": "2017-12-11T23:49:05Z"
23 | },
24 | {
25 | "checksumSHA1": "bXFrevmVL5Q2EwYlRHlPihxvAJA=",
26 | "path": "github.com/jmoiron/sqlx/reflectx",
27 | "revision": "de8647470aafe4854c976707c431dbe1eb2822c6",
28 | "revisionTime": "2017-12-11T23:49:05Z"
29 | },
30 | {
31 | "checksumSHA1": "zKDmGyNRpAeeTGXwzKEvy+UY/QQ=",
32 | "path": "github.com/mattn/go-sqlite3",
33 | "revision": "d5ffb5c0cca8778699a929b236766f4a7af674e8",
34 | "revisionTime": "2017-11-22T00:24:37Z"
35 | },
36 | {
37 | "checksumSHA1": "zKKp5SZ3d3ycKe4EKMNT0BqAWBw=",
38 | "origin": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib",
39 | "path": "github.com/pmezard/go-difflib/difflib",
40 | "revision": "2aa2c176b9dab406a6970f6a55f513e8a8c8b18f",
41 | "revisionTime": "2017-08-14T20:04:35Z"
42 | },
43 | {
44 | "checksumSHA1": "mGbTYZ8dHVTiPTTJu3ktp+84pPI=",
45 | "path": "github.com/stretchr/testify/assert",
46 | "revision": "2aa2c176b9dab406a6970f6a55f513e8a8c8b18f",
47 | "revisionTime": "2017-08-14T20:04:35Z"
48 | },
49 | {
50 | "checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=",
51 | "path": "golang.org/x/net/context",
52 | "revision": "d866cfc389cec985d6fda2859936a575a55a3ab6",
53 | "revisionTime": "2017-12-11T20:45:21Z"
54 | }
55 | ],
56 | "rootPath": "anonymous-messaging"
57 | }
58 |
--------------------------------------------------------------------------------