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