raw
genesis                 1 import hashlib
9985-single-thread 2 import serpent
9985-single-thread 3 from serpent import Serpent
9985-single-thread 4 from serpent import serpent_cbc_encrypt
9985-single-thread 5 from serpent import serpent_cbc_decrypt
genesis 6 from commands import BROADCAST
genesis 7 from commands import DIRECT
9990-keep-ephemer... 8 from commands import IGNORE
9985-single-thread 9 from message import Message
genesis 10 import base64
genesis 11 import binascii
genesis 12 import time
genesis 13 import struct
genesis 14 import sys
genesis 15 import hmac
genesis 16 import random
9990-keep-ephemer... 17 import os
genesis 18 import pprint
9987-embargoing 19 import logging
genesis 20 pp = pprint.PrettyPrinter(indent=4)
genesis 21
genesis 22 PACKET_SIZE = 496
genesis 23 MAX_SPEAKER_SIZE = 32
genesis 24 TS_ACCEPTABLE_SKEW = 60 * 15
genesis 25 BLACK_PACKET_FORMAT = "<448s48s"
genesis 26 RED_PACKET_FORMAT = "<16sBBxB428s"
genesis 27 RED_PACKET_LENGTH_WITH_PADDING = 448
genesis 28 MESSAGE_PACKET_FORMAT = "<q32s32s32s324s"
genesis 29 MAX_MESSAGE_LENGTH = 428
genesis 30 MAX_BOUNCES = 3
9991-improved-log... 31 STALE_PACKET = 0
9991-improved-log... 32 DUPLICATE_PACKET = 1
9991-improved-log... 33 MALFORMED_PACKET = 2
9991-improved-log... 34 INVALID_SIGNATURE = 3
9990-keep-ephemer... 35 IGNORED = 4
9985-single-thread 36 MESSAGE_LOGGING_FORMAT = "[%s:%d %s] <- %s %s %s"
genesis 37
genesis 38 class Infosec(object):
9987-embargoing 39 def __init__(self, state=None):
9987-embargoing 40 self.state = state
genesis 41
9987-embargoing 42 def message(self, message):
9985-single-thread 43 if not message.speaker:
9985-single-thread 44 logging.error("aborting message send due speaker not being set")
9985-single-thread 45 return
9985-single-thread 46
9987-embargoing 47 # if we are not rebroadcasting we need to set the timestamp
9987-embargoing 48 if message.timestamp == None:
9987-embargoing 49 message.original = True
9987-embargoing 50 message.timestamp = int(time.time())
9987-embargoing 51 else:
9987-embargoing 52 message.original = False
9987-embargoing 53
9987-embargoing 54 target_peer = (self.state.get_peer_by_handle(message.handle)
9987-embargoing 55 if message.command == DIRECT
9987-embargoing 56 else None)
9987-embargoing 57
9987-embargoing 58 if target_peer and not target_peer.get_key():
9987-embargoing 59 logging.debug("No key for peer associated with %s" % message.handle)
9987-embargoing 60 return
9987-embargoing 61
9987-embargoing 62 if message.command == DIRECT and target_peer == None:
9987-embargoing 63 logging.debug("Aborting message: unknown handle: %s" % message.handle)
9987-embargoing 64 return
9987-embargoing 65
9986-rebroadcast-... 66 if message.message_bytes == None:
9986-rebroadcast-... 67 message_bytes = self.get_message_bytes(message, target_peer)
9986-rebroadcast-... 68 else:
9986-rebroadcast-... 69 message_bytes = message.message_bytes
9986-rebroadcast-... 70
9985-single-thread 71 message_hash = binascii.hexlify(hashlib.sha256(message_bytes).digest())
9985-single-thread 72
9987-embargoing 73 if message.command != IGNORE:
9987-embargoing 74 logging.debug("generated message_hash: %s" % message_hash)
9987-embargoing 75 self.state.add_to_dedup_queue(message_hash)
9987-embargoing 76 self.state.log(message.speaker, message_bytes, target_peer)
9987-embargoing 77
9987-embargoing 78 if message.command == DIRECT:
9987-embargoing 79 signed_packet_bytes = self.pack(target_peer, message, message_bytes)
9987-embargoing 80 target_peer.send(signed_packet_bytes)
9985-single-thread 81 logging.info(MESSAGE_LOGGING_FORMAT % (target_peer.address,
9985-single-thread 82 target_peer.port,
9985-single-thread 83 target_peer.handles[0],
9985-single-thread 84 message.body,
9985-single-thread 85 message.bounces,
9985-single-thread 86 message_hash))
9985-single-thread 87
9987-embargoing 88 elif message.command == BROADCAST or message.command == IGNORE:
9986-rebroadcast-... 89 # sanity check
9986-rebroadcast-... 90 if message.message_bytes and message_bytes != message_bytes:
9985-single-thread 91 logging.error("aborting send: message modified by station!")
9986-rebroadcast-... 92 return
9986-rebroadcast-... 93
9987-embargoing 94 for peer in self.state.get_keyed_peers():
9987-embargoing 95
9987-embargoing 96 # we don't want to send a broadcast back to the originator
9987-embargoing 97 if message.peer and (peer.peer_id == message.peer.peer_id):
9986-rebroadcast-... 98 continue
9987-embargoing 99
9987-embargoing 100 signed_packet_bytes = self.pack(peer, message, message_bytes)
9987-embargoing 101 peer.send(signed_packet_bytes)
9985-single-thread 102 if message.command != IGNORE:
9985-single-thread 103 logging.info(MESSAGE_LOGGING_FORMAT % (peer.address,
9985-single-thread 104 peer.port,
9985-single-thread 105 peer.handles[0],
9985-single-thread 106 message.body,
9985-single-thread 107 message.bounces,
9985-single-thread 108 message_hash))
9987-embargoing 109
9987-embargoing 110 def get_message_bytes(self, message, peer=None):
9987-embargoing 111 timestamp = message.timestamp
9987-embargoing 112 command = message.command
9987-embargoing 113 speaker = self._pad(message.speaker, MAX_SPEAKER_SIZE)
genesis 114
genesis 115 # let's generate the self_chain value from the last message or set it to zero if
genesis 116 # there this is the first message
genesis 117
9990-keep-ephemer... 118 if message.original:
genesis 119 if command == DIRECT:
9987-embargoing 120 self_chain = self.state.get_last_message_hash(message.speaker, peer.peer_id)
genesis 121 elif command == BROADCAST:
9987-embargoing 122 self_chain = self.state.get_last_message_hash(message.speaker)
9990-keep-ephemer... 123 elif command == IGNORE:
9990-keep-ephemer... 124 self_chain = "\x00" * 32
genesis 125 net_chain = "\x00" * 32
genesis 126 else:
9990-keep-ephemer... 127 self_chain = message.self_chain
9990-keep-ephemer... 128 net_chain = message.net_chain
genesis 129
genesis 130 # pack message bytes
genesis 131
9987-embargoing 132 if message.command != IGNORE:
9987-embargoing 133 logging.debug("packing message bytes: %s" % message.body)
9987-embargoing 134 else:
9987-embargoing 135 logging.debug("packing rubbish message bytes: %s" % binascii.hexlify(message.body))
9987-embargoing 136
9987-embargoing 137 message_bytes = struct.pack(MESSAGE_PACKET_FORMAT, message.timestamp, self_chain, net_chain, speaker, message.body)
9988-hash-dedup 138 return message_bytes
genesis 139
9987-embargoing 140 def pack(self, peer, message, message_bytes):
9988-hash-dedup 141 key_bytes = base64.b64decode(peer.get_key())
9988-hash-dedup 142 signing_key = key_bytes[:32]
9988-hash-dedup 143 cipher_key = key_bytes[32:]
genesis 144
genesis 145 # pack packet bytes
genesis 146
genesis 147 nonce = self._generate_nonce(16)
9990-keep-ephemer... 148 bounces = message.bounces
genesis 149 version = 0xfe
9988-hash-dedup 150 red_packet_bytes = struct.pack(RED_PACKET_FORMAT, nonce, bounces, version, message.command, self._pad(message_bytes, MAX_MESSAGE_LENGTH))
genesis 151
genesis 152 # encrypt packet
genesis 153
genesis 154 serpent = Serpent(cipher_key)
genesis 155 black_packet_bytes = serpent_cbc_encrypt(cipher_key, red_packet_bytes)
genesis 156
genesis 157 # sign packet
genesis 158
genesis 159 signature_bytes = hmac.new(signing_key, black_packet_bytes, hashlib.sha384).digest()
genesis 160
genesis 161 # pack the signed black packet
genesis 162
genesis 163 signed_packet_bytes = struct.pack(BLACK_PACKET_FORMAT, black_packet_bytes, signature_bytes)
genesis 164
genesis 165 return signed_packet_bytes
genesis 166
genesis 167 def unpack(self, peer, black_packet):
genesis 168 # unpack the black packet
genesis 169
genesis 170 key_bytes = base64.b64decode(peer.get_key())
genesis 171 signing_key = key_bytes[:32]
genesis 172 cipher_key = key_bytes[32:]
genesis 173
genesis 174 try:
genesis 175 black_packet_bytes, signature_bytes = struct.unpack(BLACK_PACKET_FORMAT, black_packet)
genesis 176 except:
9987-embargoing 177 logging.error("Discarding malformed black packet from %s" % peer.get_key())
9987-embargoing 178 return Message({ "error_code": MALFORMED_PACKET })
genesis 179
genesis 180 # check signature
genesis 181
genesis 182 signature_check_bytes = hmac.new(signing_key, black_packet_bytes, hashlib.sha384).digest()
genesis 183
genesis 184 if(signature_check_bytes != signature_bytes):
9987-embargoing 185 return Message({ "error_code": INVALID_SIGNATURE })
genesis 186
genesis 187 # try to decrypt black packet
genesis 188
genesis 189 serpent = Serpent(cipher_key)
genesis 190 red_packet_bytes = serpent_cbc_decrypt(cipher_key, black_packet_bytes)
genesis 191
genesis 192 # unpack red packet
genesis 193
genesis 194 nonce, bounces, version, command, message_bytes = struct.unpack(RED_PACKET_FORMAT, red_packet_bytes)
genesis 195
9987-embargoing 196 # compute message_hash
9987-embargoing 197
9987-embargoing 198 message_hash = binascii.hexlify(hashlib.sha256(message_bytes).digest())
9987-embargoing 199
genesis 200 # unpack message
genesis 201
9987-embargoing 202 int_ts, self_chain, net_chain, speaker, body = struct.unpack(MESSAGE_PACKET_FORMAT, message_bytes)
9987-embargoing 203
9987-embargoing 204 # remove padding from speaker
9987-embargoing 205
9987-embargoing 206 for index, byte in enumerate(speaker):
9987-embargoing 207 if byte == '\x00':
9987-embargoing 208 speaker = speaker[0:index]
9987-embargoing 209 break
9987-embargoing 210
9987-embargoing 211 # remove padding from body
9987-embargoing 212
9987-embargoing 213 for index, byte in enumerate(body):
9987-embargoing 214 if byte == '\x00':
9987-embargoing 215 body = body[0:index]
9987-embargoing 216 break
9989-show-wot-nicks 217
9989-show-wot-nicks 218 # nothing to be done for an IGNORE command
9989-show-wot-nicks 219
9989-show-wot-nicks 220 if command == IGNORE:
9989-show-wot-nicks 221 return Message({"speaker": speaker, "error_code": IGNORED})
9989-show-wot-nicks 222
genesis 223 # check timestamp
genesis 224
genesis 225 if(int_ts not in self._ts_range()):
9987-embargoing 226 return Message({ "error_code": STALE_PACKET })
genesis 227
genesis 228 # check self_chain
genesis 229
genesis 230 if command == DIRECT:
9987-embargoing 231 self_chain_check = self.state.get_last_message_hash(speaker, peer.peer_id)
genesis 232 elif command == BROADCAST:
9987-embargoing 233 self_chain_check = self.state.get_last_message_hash(speaker)
genesis 234
genesis 235 self_chain_valid = (self_chain_check == self_chain)
genesis 236
genesis 237 # log this message for use in the self_chain check
genesis 238
9987-embargoing 239 self.state.log(speaker, message_bytes, peer if (command == DIRECT) else None)
genesis 240
9987-embargoing 241 # build message object
genesis 242
9987-embargoing 243 message = Message({
9990-keep-ephemer... 244 "peer": peer,
9987-embargoing 245 "body": body.rstrip(),
9990-keep-ephemer... 246 "timestamp": int_ts,
9990-keep-ephemer... 247 "command": command,
9990-keep-ephemer... 248 "speaker": speaker,
9990-keep-ephemer... 249 "bounces": bounces,
9990-keep-ephemer... 250 "self_chain": self_chain,
9990-keep-ephemer... 251 "net_chain": net_chain,
9990-keep-ephemer... 252 "self_chain_valid": self_chain_valid,
9986-rebroadcast-... 253 "message_hash": message_hash,
9986-rebroadcast-... 254 "message_bytes": message_bytes
9987-embargoing 255 })
9987-embargoing 256
9987-embargoing 257 # check for duplicates
9987-embargoing 258
9987-embargoing 259 if(self.state.is_duplicate_message(message_hash)):
9987-embargoing 260 message.error_code = DUPLICATE_PACKET
9987-embargoing 261 return message
9987-embargoing 262
9987-embargoing 263 return message
genesis 264
genesis 265 def _pad(self, text, size):
9987-embargoing 266 return text.ljust(size, "\x00")
genesis 267
genesis 268 def _ts_range(self):
genesis 269 current_ts = int(time.time())
genesis 270 return range(current_ts - TS_ACCEPTABLE_SKEW, current_ts + TS_ACCEPTABLE_SKEW)
genesis 271
genesis 272 def _generate_nonce(self, length=8):
genesis 273 """Generate pseudorandom number."""
genesis 274 return ''.join([str(random.randint(0, 9)) for i in range(length)])
9990-keep-ephemer... 275
9990-keep-ephemer... 276 def gen_rubbish_body(self):
9990-keep-ephemer... 277 return os.urandom(MAX_MESSAGE_LENGTH)