raw
genesis                 1 import hashlib
genesis 2 import lib.serpent
genesis 3 from lib.serpent import Serpent
genesis 4 from lib.serpent import serpent_cbc_encrypt
genesis 5 from lib.serpent import serpent_cbc_decrypt
genesis 6 from commands import BROADCAST
genesis 7 from commands import DIRECT
genesis 8 import base64
genesis 9 import binascii
genesis 10 import time
genesis 11 import struct
genesis 12 import sys
genesis 13 import hmac
genesis 14 import random
genesis 15 import pprint
genesis 16 pp = pprint.PrettyPrinter(indent=4)
genesis 17
genesis 18 PACKET_SIZE = 496
genesis 19 MAX_SPEAKER_SIZE = 32
genesis 20 TS_ACCEPTABLE_SKEW = 60 * 15
genesis 21 BLACK_PACKET_FORMAT = "<448s48s"
genesis 22 RED_PACKET_FORMAT = "<16sBBxB428s"
genesis 23 RED_PACKET_LENGTH_WITH_PADDING = 448
genesis 24 MESSAGE_PACKET_FORMAT = "<q32s32s32s324s"
genesis 25 MAX_MESSAGE_LENGTH = 428
genesis 26 MAX_BOUNCES = 3
genesis 27
genesis 28 class Infosec(object):
genesis 29 def __init__(self, server=None):
genesis 30 self.server = server
genesis 31
genesis 32 def pack(self, peer, message):
genesis 33 try:
genesis 34 timestamp = message["timestamp"]
genesis 35 except:
genesis 36 timestamp = None
genesis 37 command = message["command"]
genesis 38 speaker = self._pad(message["speaker"], MAX_SPEAKER_SIZE)
genesis 39
genesis 40 # if we are rebroadcasting we need to use the original timestamp
genesis 41
genesis 42 if(timestamp == None):
genesis 43 int_ts = int(time.time())
genesis 44 else:
genesis 45 int_ts = timestamp
genesis 46
genesis 47 key_bytes = base64.b64decode(peer.get_key())
genesis 48 signing_key = key_bytes[:32]
genesis 49 cipher_key = key_bytes[32:]
genesis 50
genesis 51 # let's generate the self_chain value from the last message or set it to zero if
genesis 52 # there this is the first message
genesis 53
genesis 54
genesis 55 if message["original"]:
genesis 56 if command == DIRECT:
genesis 57 self_chain = self.server.state.get_last_message_hash(message["speaker"], peer.peer_id)
genesis 58 elif command == BROADCAST:
genesis 59 self_chain = self.server.state.get_last_message_hash(message["speaker"])
genesis 60 net_chain = "\x00" * 32
genesis 61 else:
genesis 62 self_chain = message["self_chain"]
genesis 63 net_chain = message["net_chain"]
genesis 64
genesis 65 # pack message bytes
genesis 66
genesis 67 message_bytes = struct.pack(MESSAGE_PACKET_FORMAT, int_ts, self_chain, net_chain, speaker, message["body"].encode("ascii"))
genesis 68
genesis 69 # log messages
genesis 70
genesis 71 if message["original"]:
genesis 72 if command == DIRECT:
genesis 73 self.server.state.log(message["speaker"], message_bytes, peer.peer_id)
genesis 74 elif command == BROADCAST:
genesis 75 self.server.state.log(message["speaker"], message_bytes)
genesis 76
genesis 77 # pack packet bytes
genesis 78
genesis 79 nonce = self._generate_nonce(16)
genesis 80 bounces = message["bounces"]
genesis 81 version = 0xfe
genesis 82 red_packet_bytes = struct.pack(RED_PACKET_FORMAT, nonce, bounces, version, command, self._pad(message_bytes, MAX_MESSAGE_LENGTH))
genesis 83
genesis 84 # encrypt packet
genesis 85
genesis 86 serpent = Serpent(cipher_key)
genesis 87 black_packet_bytes = serpent_cbc_encrypt(cipher_key, red_packet_bytes)
genesis 88
genesis 89 # sign packet
genesis 90
genesis 91 signature_bytes = hmac.new(signing_key, black_packet_bytes, hashlib.sha384).digest()
genesis 92
genesis 93 # pack the signed black packet
genesis 94
genesis 95 signed_packet_bytes = struct.pack(BLACK_PACKET_FORMAT, black_packet_bytes, signature_bytes)
genesis 96
genesis 97 # we want to ignore this ts if it is sent back to us
genesis 98
genesis 99 self.server.recent.insert(int_ts)
genesis 100 return signed_packet_bytes
genesis 101
genesis 102 def unpack(self, peer, black_packet):
genesis 103 # unpack the black packet
genesis 104
genesis 105 key_bytes = base64.b64decode(peer.get_key())
genesis 106 signing_key = key_bytes[:32]
genesis 107 cipher_key = key_bytes[32:]
genesis 108
genesis 109 try:
genesis 110 black_packet_bytes, signature_bytes = struct.unpack(BLACK_PACKET_FORMAT, black_packet)
genesis 111 except:
genesis 112 self.server.print_error("Discarding malformed black packet from %s" % peer.get_key())
genesis 113 return None
genesis 114
genesis 115 # check signature
genesis 116
genesis 117 signature_check_bytes = hmac.new(signing_key, black_packet_bytes, hashlib.sha384).digest()
genesis 118
genesis 119 if(signature_check_bytes != signature_bytes):
genesis 120 self.server.print_debug("rejected black packet with bad signature for %s: %s" % (peer.get_key(), base64.b64encode(signature_check_bytes)))
genesis 121 return None
genesis 122
genesis 123 # try to decrypt black packet
genesis 124
genesis 125 serpent = Serpent(cipher_key)
genesis 126 red_packet_bytes = serpent_cbc_decrypt(cipher_key, black_packet_bytes)
genesis 127
genesis 128 # unpack red packet
genesis 129
genesis 130 nonce, bounces, version, command, message_bytes = struct.unpack(RED_PACKET_FORMAT, red_packet_bytes)
genesis 131
genesis 132 # unpack message
genesis 133
genesis 134 int_ts, self_chain, net_chain, speaker, message = struct.unpack(MESSAGE_PACKET_FORMAT, message_bytes)
genesis 135 speaker = speaker.strip()
genesis 136
genesis 137 # check timestamp
genesis 138
genesis 139 if(int_ts not in self._ts_range()):
genesis 140 self.server.print_debug("rejected message with timestamp out of range")
genesis 141 return None
genesis 142
genesis 143 if(self.server.recent.has(int_ts)):
genesis 144 self.server.print_debug("rejected known message: %d" % int_ts)
genesis 145 return None
genesis 146 else:
genesis 147 self.server.recent.insert(int_ts)
genesis 148
genesis 149 # check self_chain
genesis 150
genesis 151 if command == DIRECT:
genesis 152 self_chain_check = self.server.state.get_last_message_hash(speaker, peer.peer_id)
genesis 153 elif command == BROADCAST:
genesis 154 self_chain_check = self.server.state.get_last_message_hash(speaker)
genesis 155
genesis 156 self_chain_valid = (self_chain_check == self_chain)
genesis 157
genesis 158 # log this message for use in the self_chain check
genesis 159
genesis 160 self.server.state.log(speaker, message_bytes, peer.peer_id if (command == DIRECT) else None)
genesis 161
genesis 162 # remove padding from message bytes
genesis 163
genesis 164 for index, byte in enumerate(message):
genesis 165 if binascii.hexlify(byte) == "00":
genesis 166 unpadded_message = message[0:index]
genesis 167 break
genesis 168
genesis 169 # return the message, timestamp, and command (command replaces propagate)
genesis 170
genesis 171 return { "body": unpadded_message.rstrip(),
genesis 172 "timestamp": int_ts,
genesis 173 "command": command,
genesis 174 "speaker": speaker,
genesis 175 "bounces": bounces,
genesis 176 "self_chain": self_chain,
genesis 177 "net_chain": net_chain,
genesis 178 "self_chain_valid": self_chain_valid }
genesis 179
genesis 180 def _pad(self, text, size):
genesis 181 return text.ljust(size)
genesis 182
genesis 183 def _ts_range(self):
genesis 184 current_ts = int(time.time())
genesis 185 return range(current_ts - TS_ACCEPTABLE_SKEW, current_ts + TS_ACCEPTABLE_SKEW)
genesis 186
genesis 187 def _generate_nonce(self, length=8):
genesis 188 """Generate pseudorandom number."""
genesis 189 return ''.join([str(random.randint(0, 9)) for i in range(length)])