├── 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 | --------------------------------------------------------------------------------