diff --git a/ap2-receiver.py b/ap2-receiver.py index 52f78eb..eb91880 100644 --- a/ap2-receiver.py +++ b/ap2-receiver.py @@ -166,6 +166,7 @@ def __str__(self): HTTP_CT_IMAGE = "image/jpeg" HTTP_CT_DMAP = "application/x-dmap-tagged" + def setup_global_structs(args): global sonos_one_info global sonos_one_setup @@ -176,21 +177,26 @@ def setup_global_structs(args): sonos_one_info = { # 'OSInfo': 'Linux 3.10.53', # 'PTPInfo': 'OpenAVNU ArtAndLogic-aPTP-changes a5d7f94-0.0.1', - 'audioLatencies': [ { 'inputLatencyMicros': 0, - 'outputLatencyMicros': 400000, - 'type': 100}, - { 'audioType': 'default', - 'inputLatencyMicros': 0, - 'outputLatencyMicros': 400000, - 'type': 100}, - { 'audioType': 'media', - 'inputLatencyMicros': 0, - 'outputLatencyMicros': 400000, - 'type': 100}, - { 'audioType': 'media', - 'inputLatencyMicros': 0, - 'outputLatencyMicros': 400000, - 'type': 102}], + 'audioLatencies': [{ + 'inputLatencyMicros': 0, + 'outputLatencyMicros': 400000, + 'type': 100}, + { + 'audioType': 'default', + 'inputLatencyMicros': 0, + 'outputLatencyMicros': 400000, + 'type': 100}, + { + 'audioType': 'media', + 'inputLatencyMicros': 0, + 'outputLatencyMicros': 400000, + 'type': 100}, + { + 'audioType': 'media', + 'inputLatencyMicros': 0, + 'outputLatencyMicros': 400000, + 'type': 102 + }], # 'build': '16.0', 'deviceID': DEVICE_ID, 'features': FEATURES, @@ -204,60 +210,64 @@ def setup_global_structs(args): 'model': 'One', 'name': 'Camera da letto', 'nameIsFactoryDefault': False, - 'pi': 'ba5cb8df-7f14-4249-901a-5e748ce57a93', # UUID generated casually.. + 'pi': 'ba5cb8df-7f14-4249-901a-5e748ce57a93', # UUID generated casually.. 'protocolVersion': '1.1', 'sdk': 'AirPlay;2.0.2', 'sourceVersion': '366.0', 'statusFlags': 4, # 'statusFlags': 0x404 # Sonos One - } + } if DISABLE_VM: volume = 0 - else: + else: volume = get_volume() second_stage_info = { "initialVolume": volume, - } + } sonos_one_setup = { - 'eventPort': 0, # AP2 receiver event server - 'timingPort': 0, - 'timingPeerInfo': { - 'Addresses': [ - IPV4, IPV6], - 'ID': IPV4} - } + 'eventPort': 0, # AP2 receiver event server + 'timingPort': 0, + 'timingPeerInfo': { + 'Addresses': + [ + IPV4, + IPV6 + ], + 'ID': IPV4} + } sonos_one_setup_data = { - 'streams': [ - { - 'type': 96, - 'dataPort': 0, # AP2 receiver data server - 'controlPort': 0 # AP2 receiver control server - } - ] + 'streams': [ + { + 'type': 96, + 'dataPort': 0, # AP2 receiver data server + 'controlPort': 0 # AP2 receiver control server } + ] + } mdns_props = { - "srcvers": SERVER_VERSION, - "deviceid": DEVICE_ID, - "features": "%s,%s" % (hex(FEATURES & 0xffffffff), hex(FEATURES >> 32 & 0xffffffff)), - "flags": "0x4", - # "name": "GINO", # random - "model": "Airplay2-Receiver", # random - # "manufacturer": "Pino", # random - # "serialNumber": "01234xX321", # random - "protovers": "1.1", - "acl": "0", - "rsf": "0x0", - "fv": "p20.78000.12", - "pi": "5dccfd20-b166-49cc-a593-6abd5f724ddb", # UUID generated casually - "gid": "5dccfd20-b166-49cc-a593-6abd5f724ddb", # UUID generated casually - "gcgl": "0", - # "vn": "65537", - "pk": "de352b0df39042e201d31564049023af58a106c6d904b74a68aa65012852997f" - } + "srcvers": SERVER_VERSION, + "deviceid": DEVICE_ID, + "features": "%s,%s" % (hex(FEATURES & 0xffffffff), hex(FEATURES >> 32 & 0xffffffff)), + "flags": "0x4", + # "name": "GINO", # random + "model": "Airplay2-Receiver", # random + # "manufacturer": "Pino", # random + # "serialNumber": "01234xX321", # random + "protovers": "1.1", + "acl": "0", + "rsf": "0x0", + "fv": "p20.78000.12", + "pi": "5dccfd20-b166-49cc-a593-6abd5f724ddb", # UUID generated casually + "gid": "5dccfd20-b166-49cc-a593-6abd5f724ddb", # UUID generated casually + "gcgl": "0", + # "vn": "65537", + "pk": "de352b0df39042e201d31564049023af58a106c6d904b74a68aa65012852997f" + } + class AP2Handler(http.server.BaseHTTPRequestHandler): @@ -299,7 +309,7 @@ def do_OPTIONS(self): self.send_response(200) self.send_header("Server", self.version_string()) self.send_header("CSeq", self.headers["CSeq"]) - self.send_header("Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, FLUSHBUFFERED, TEARDOWN, OPTIONS, POST, GET, PUT") + self.send_header("Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, FLUSHBUFFERED, TEARDOWN, OPTIONS, POST, GET, PUT") self.end_headers() def do_FLUSHBUFFERED(self): @@ -389,7 +399,7 @@ def do_SETUP(self): self.wfile.write(res) else: print("Sending CONTROL/DATA:") - buff = 8388608 # determines how many CODEC frame size 1024 we can hold + buff = 8388608 # determines how many CODEC frame size 1024 we can hold stream = Stream(plist["streams"][0], buff) self.server.streams.append(stream) sonos_one_setup_data["streams"][0]["controlPort"] = stream.control_port @@ -441,7 +451,7 @@ def do_GET_PARAMETER(self): self.send_header("Server", self.version_string()) self.send_header("CSeq", self.headers["CSeq"]) self.end_headers() - hexdump(res); + hexdump(res) self.wfile.write(res) def do_SET_PARAMETER(self): @@ -539,7 +549,7 @@ def do_TEARDOWN(self): self.send_header("Server", self.version_string()) self.send_header("CSeq", self.headers["CSeq"]) self.end_headers() - + # Erase the hap() instance, otherwise reconnects fail self.server.hap = None @@ -597,7 +607,7 @@ def handle_feedback(self): plist = readPlistFromString(body) # feedback logs are pretty much noise... - #self.pp.pprint(plist) + # self.pp.pprint(plist) self.send_response(200) self.send_header("Server", self.version_string()) self.send_header("CSeq", self.headers["CSeq"]) @@ -731,40 +741,46 @@ def handle_info(self): def upgrade_to_encrypted(self, shared_key): self.request = self.server.upgrade_to_encrypted( - self.client_address, - shared_key) + self.client_address, + shared_key) self.connection = self.request self.rfile = self.connection.makefile('rb', self.rbufsize) self.wfile = self.connection.makefile('wb') self.is_encrypted = True print("----- ENCRYPTED CHANNEL -----") + def register_mdns(receiver_name): addresses = [] for ifen in ni.interfaces(): ifenaddr = ni.ifaddresses(ifen) if ni.AF_INET in ifenaddr: - addresses.append(socket.inet_pton(ni.AF_INET, - ifenaddr[ni.AF_INET][0]["addr"])) + addresses.append(socket.inet_pton( + ni.AF_INET, + ifenaddr[ni.AF_INET][0]["addr"]) + ) if ni.AF_INET6 in ifenaddr: - addresses.append(socket.inet_pton(ni.AF_INET6, - ifenaddr[ni.AF_INET6][0]["addr"].split("%")[0])) + addresses.append(socket.inet_pton( + ni.AF_INET6, + ifenaddr[ni.AF_INET6][0]["addr"].split("%")[0]) + ) info = ServiceInfo( - "_airplay._tcp.local.", - "%s._airplay._tcp.local." % receiver_name, - # addresses=[socket.inet_aton("127.0.0.1")], - addresses=addresses, - port=7000, - properties=mdns_props, - server="%s.local." % receiver_name, - ) + "_airplay._tcp.local.", + "%s._airplay._tcp.local." % receiver_name, + # addresses=[socket.inet_aton("127.0.0.1")], + addresses=addresses, + port=7000, + properties=mdns_props, + server="%s.local." % receiver_name, + ) zeroconf = Zeroconf(ip_version=IPVersion.V4Only) zeroconf.register_service(info) print("mDNS service registered") return (zeroconf, info) + def unregister_mdns(zeroconf, info): print("Unregistering...") zeroconf.unregister_service(info) @@ -793,7 +809,7 @@ def __init__(self, addr_port, handler): self.enc_layer = False self.streams = [] - #Override + # Override def get_request(self): client_socket, client_addr = super().get_request() print("Got connection with %s:%d" % client_addr) @@ -806,6 +822,7 @@ def upgrade_to_encrypted(self, client_address, shared_key): self.connections[client_address] = hap_socket return hap_socket + if __name__ == "__main__": multiprocessing.set_start_method("spawn") diff --git a/ap2/connections/audio.py b/ap2/connections/audio.py index c86a34f..274b4e3 100644 --- a/ap2/connections/audio.py +++ b/ap2/connections/audio.py @@ -104,7 +104,11 @@ def next(self): else: buffered_object = self.buffer_array[self.read_index] if self.read_index % 1000 == 0: - print("buffer: reading - full at %s - ri=%i - wi=%i - seq=%i" % ("{:.1%}".format(self.get_fullness()), self.read_index, self.write_index,buffered_object.sequence_no)) + print("buffer: reading - full at %s - ri=%i - wi=%i - seq=%i" + % ("{:.1%}".format(self.get_fullness()), + self.read_index, + self.write_index, + buffered_object.sequence_no)) if self.increment_index(self.read_index) == self.write_index: # buffer underrun, nothing we can do @@ -129,24 +133,24 @@ def get_bounds(self): def find_seq(self, seq): # do binary search. Bin = O(log n) vs linear O(n) # here we iterate max several times - l = self.read_index - r = self.write_index # len(self.buffer_array) - 1 + left = self.read_index + right = self.write_index - if l == -1: + if left == -1: return - if l == r: + if left == right: return - while l <= r: - m = (l + r // 2) % self.BUFFER_SIZE - # print('searching l=%d, r=%d, m=%d, srch=%d, now_at=%d' % \ - # (l, r, m, seq, self.buffer_array_seqs[m] )) + while left <= right: + m = (left + right // 2) % self.BUFFER_SIZE + # print('searching left=%d, right=%d, m=%d, srch=%d, now_at=%d' % \ + # (left, right, m, seq, self.buffer_array_seqs[m] )) if self.buffer_array_seqs[m] == seq: return m if self.buffer_array_seqs[m] < seq: - l = self.increment_index(m) + left = self.increment_index(m) elif self.buffer_array_seqs[m] > seq: - l = self.decrement_index(m) + left = self.decrement_index(m) # initialize buffer for reading def init(self): @@ -203,7 +207,7 @@ def set_audio_params(self, audio_format): self.channel_count = 2 self.af = af = str(Audio.AudioFormat(audio_format)) - if '8000' in af: + if '8000' in af: self.sample_rate = 8000 elif'16000' in af: self.sample_rate = 16000 @@ -215,24 +219,22 @@ def set_audio_params(self, audio_format): self.sample_rate = 44100 elif'48000' in af: self.sample_rate = 48000 - else: #default + else: # default self.sample_rate = 44100 - if '_16' in af: + if '_16' in af: self.sample_size = 16 - elif'_24' in af: + elif'_24' in af: self.sample_size = 24 else: # default self.sample_size = 16 - if af.endswith('_1'): + if af.endswith('_1'): self.channel_count = 1 else: self.channel_count = 2 print("Negotiated audio format: ", Audio.AudioFormat(audio_format)) - # - def __init__(self, session_key, audio_format, buff_size): self.audio_format = audio_format @@ -280,12 +282,11 @@ def init_audio_sink(self): elif self.audio_format == Audio.AudioFormat.ALAC_48000_24_2.value: extradata = self.set_alac_extradata(self, 48000, 24, 2) - - if 'ALAC' in self.af: + if 'ALAC' in self.af: self.codec = av.codec.Codec('alac', 'r') - elif'AAC' in self.af: + elif'AAC' in self.af: self.codec = av.codec.Codec('aac', 'r') - elif'OPUS' in self.af: + elif'OPUS' in self.af: self.codec = av.codec.Codec('opus', 'r') # PCM elif'PCM' and '_16_' in self.af: @@ -312,7 +313,7 @@ def init_audio_sink(self): self.codecContext.extradata = extradata self.resampler = av.AudioResampler( - format=av.AudioFormat('s' + str(self.sample_size) ).packed, + format=av.AudioFormat('s' + str(self.sample_size)).packed, layout='stereo', rate=self.sample_rate, ) @@ -334,11 +335,13 @@ def decrypt(self, rtp): return data def handle(self, rtp): - self.logger.debug("v=%d p=%d x=%d cc=%d m=%d pt=%d seq=%d ts=%d ssrc=%d" % (rtp.version, rtp.padding, - rtp.extension, rtp.csrc_count, - rtp.marker, rtp.payload_type, - rtp.sequence_no, rtp.timestamp, - rtp.ssrc)) + self.logger.debug( + "v=%d p=%d x=%d cc=%d m=%d pt=%d seq=%d ts=%d ssrc=%d" + % (rtp.version, rtp.padding, + rtp.extension, rtp.csrc_count, + rtp.marker, rtp.payload_type, + rtp.sequence_no, rtp.timestamp, + rtp.ssrc)) def process(self, rtp): data = self.decrypt(rtp) @@ -351,7 +354,7 @@ def run(self, parent_reader_connection): # This pipe is between player (read data) and server (write data) parent_writer_connection, writer_connection = multiprocessing.Pipe() server_thread = threading.Thread(target=self.serve, args=(writer_connection,)) - player_thread = threading.Thread(target=self.play, args=(parent_reader_connection,parent_writer_connection)) + player_thread = threading.Thread(target=self.play, args=(parent_reader_connection, parent_writer_connection)) server_thread.start() player_thread.start() @@ -425,7 +428,6 @@ def get_min_timestamp(self): return res - def forward(self, requested_timestamp): finished = False while not finished: @@ -440,7 +442,7 @@ def forward(self, requested_timestamp): print("player: !!! error while forwarding !!!") finished = True - # player moves readindex in buffer + # player moves readindex in buffer def play(self, rtspconn, serverconn): playing = False data_ready = False @@ -502,7 +504,7 @@ def play(self, rtspconn, serverconn): if time_offset_ms >= (self.sample_delay * 1000): # print("player: offset %i ms too big - seq = %i - sleeping %s sec" % (time_offset_ms, rtp.sequence_no, "{:05.2f}".format(time_offset_ms /1000))) # time.sleep(time_offset_ms / 1000) - time.sleep( (self.sample_delay / 2) - 0.001 ) + time.sleep((self.sample_delay / 2) - 0.001) elif time_offset_ms < -100: print("player: offset %i ms too low - seq = %i - sending ontime data request" % (time_offset_ms, rtp.sequence_no)) # request on_time data message @@ -574,4 +576,4 @@ def serve(self, playerconn): pass finally: conn.close() - self.socket.close() \ No newline at end of file + self.socket.close() diff --git a/ap2/connections/control.py b/ap2/connections/control.py index 1a0c83b..584adac 100644 --- a/ap2/connections/control.py +++ b/ap2/connections/control.py @@ -4,6 +4,7 @@ from ..utils import get_logger, get_free_port + class RTCP: TIME_ANNOUNCE = 215 @@ -21,6 +22,7 @@ def __init__(self, data): self.rtpTime = struct.unpack(">I", data[16:20])[0] self.net_base = struct.unpack(">Q", data[20:28])[0] + class Control: def __init__(self): self.port = get_free_port() @@ -54,4 +56,3 @@ def spawn(): p = multiprocessing.Process(target=control.serve) p.start() return control.port, p - diff --git a/ap2/connections/event.py b/ap2/connections/event.py index 91899ec..dac5766 100644 --- a/ap2/connections/event.py +++ b/ap2/connections/event.py @@ -3,6 +3,7 @@ from ..utils import get_logger, get_free_port + class Event: def __init__(self): self.port = get_free_port() @@ -16,7 +17,7 @@ def serve(self): conn, addr = sock.accept() self.logger.debug("Connection open from %s:%d" % addr) - event_file = open("./events.bin","wb") + event_file = open("./events.bin", "wb") try: while True: data = conn.recv(1) @@ -35,4 +36,3 @@ def spawn(): p = multiprocessing.Process(target=event.serve) p.start() return event.port, p - diff --git a/ap2/connections/stream.py b/ap2/connections/stream.py index 07c99ce..d8d3bed 100644 --- a/ap2/connections/stream.py +++ b/ap2/connections/stream.py @@ -3,6 +3,7 @@ from .control import Control from .audio import AudioRealtime, AudioBuffered + class Stream: REALTIME = 96 diff --git a/ap2/pairing/hap.py b/ap2/pairing/hap.py index b8864f9..96bbc43 100644 --- a/ap2/pairing/hap.py +++ b/ap2/pairing/hap.py @@ -13,6 +13,7 @@ from . import srp + class PairingMethod: PAIR_SETUP = b'\x00' PAIR_SETUP_AUTH = b'\x01' @@ -21,6 +22,7 @@ class PairingMethod: REMOVE_PAIRING = b'\x04' LIST_PAIRINGS = b'\x05' + class PairingErrors: RESERVED = 0 UNKNOWN = 1 @@ -31,9 +33,11 @@ class PairingErrors: UNAVAILABLE = 6 BUSY = 7 + class PairingFlags: TRANSIENT = b'\x10' + class PairingState: M1 = b'\x01' M2 = b'\x02' @@ -42,6 +46,7 @@ class PairingState: M5 = b'\x05' M6 = b'\x06' + class Tlv8: class Tag: METHOD = 0 @@ -67,8 +72,8 @@ def decode(req, debug=True): ptr = 0 while ptr < len(req): tag = req[ptr] - length = req[ptr+1] - value = req[ptr+2:ptr+2+length] + length = req[ptr + 1] + value = req[ptr + 2:ptr + 2 + length] # print("dec tag=%d length=%d value=%s" % (tag, length, value.hex())) if tag in res: res[tag] = res[tag] + value @@ -83,7 +88,7 @@ def encode(req): res = b"" for i in range(0, len(req), 2): tag = req[i] - value = req[i+1] + value = req[i + 1] length = len(value) # print("enc tag=%d length=%d value=%s" % (tag, length, value.hex())) @@ -91,12 +96,13 @@ def encode(req): res += bytes([tag]) + bytes([length]) + value else: for i in range(0, length // 255): - res += bytes([tag]) + b"\xff" + value[i*255:(i+1)*255] + res += bytes([tag]) + b"\xff" + value[i * 255:(i + 1) * 255] left = length % 255 res += bytes([tag]) + bytes([left]) + value[-left:] return res + class Hap: def __init__(self): self.transient = False @@ -144,15 +150,16 @@ def pair_verify(self, req): self.encrypted = True return Tlv8.encode(res) - def pair_setup_m1_m2(self): self.ctx = srp.SRPServer(b"Pair-Setup", b"3939") server_public = self.ctx.public_key salt = self.ctx.salt - return [ Tlv8.Tag.STATE, PairingState.M2, - Tlv8.Tag.SALT, salt, - Tlv8.Tag.PUBLICKEY, server_public ] + return [ + Tlv8.Tag.STATE, PairingState.M2, + Tlv8.Tag.SALT, salt, + Tlv8.Tag.PUBLICKEY, server_public + ] def pair_setup_m3_m4(self, client_public, client_proof): self.ctx.set_client_public(client_public) @@ -161,8 +168,10 @@ def pair_setup_m3_m4(self, client_public, client_proof): self.accessory_shared_key = self.ctx.session_key server_proof = self.ctx.proof - return [ Tlv8.Tag.STATE, PairingState.M4, - Tlv8.Tag.PROOF, server_proof ] + return [ + Tlv8.Tag.STATE, PairingState.M4, + Tlv8.Tag.PROOF, server_proof + ] def pair_setup_m5_m6(self, encrypted): print("-----\tPair-Setup [3/5]") @@ -172,8 +181,10 @@ def pair_setup_m5_m6(self, encrypted): print("-----\tPair-Setup [5/5]") enc_tlv, tag = self.pair_setup_m5_m6_3(session_key) - return [ Tlv8.Tag.STATE, PairingState.M6, - Tlv8.Tag.ENCRYPTEDDATA, enc_tlv + tag ] + return [ + Tlv8.Tag.STATE, PairingState.M6, + Tlv8.Tag.ENCRYPTEDDATA, enc_tlv + tag + ] def pair_setup_m5_m6_1(self, encrypted): prk = hkdf.hkdf_extract(b"Pair-Setup-Encrypt-Salt", self.ctx.session_key) @@ -210,32 +221,35 @@ def pair_setup_m5_m6_3(self, session_key): accessory_signed = self.accessory_ltsk.sign(accessory_info) accessory_sig = accessory_signed.signature - dec_tlv = Tlv8.encode([ Tlv8.Tag.IDENTIFIER, self.accessory_id, - Tlv8.Tag.PUBLICKEY, self.accessory_ltpk, - Tlv8.Tag.SIGNATURE, accessory_sig ]) + dec_tlv = Tlv8.encode([ + Tlv8.Tag.IDENTIFIER, self.accessory_id, + Tlv8.Tag.PUBLICKEY, self.accessory_ltpk, + Tlv8.Tag.SIGNATURE, accessory_sig + ]) c = ChaCha20_Poly1305.new(key=session_key, nonce=b"PS-Msg06") enc_tlv, tag = c.encrypt_and_digest(dec_tlv) return enc_tlv, tag - def pair_verify_m1_m2(self, client_public): self.client_curve_public = client_public self.accessory_curve = x25519.X25519PrivateKey.generate() self.accessory_curve_public = self.accessory_curve.public_key().public_bytes( - encoding=serialization.Encoding.Raw, - format=serialization.PublicFormat.Raw - ) + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw + ) self.accessory_shared_key = self.accessory_curve.exchange(x25519.X25519PublicKey.from_public_bytes(client_public)) accessory_info = self.accessory_curve_public + self.accessory_id + client_public accessory_signed = self.accessory_ltsk.sign(accessory_info) accessory_sig = accessory_signed.signature - sub_tlv = Tlv8.encode([ Tlv8.Tag.IDENTIFIER, self.accessory_id, - Tlv8.Tag.SIGNATURE, accessory_sig ]) + sub_tlv = Tlv8.encode([ + Tlv8.Tag.IDENTIFIER, self.accessory_id, + Tlv8.Tag.SIGNATURE, accessory_sig + ]) prk = hkdf.hkdf_extract(b"Pair-Verify-Encrypt-Salt", self.accessory_shared_key) session_key = hkdf.hkdf_expand(prk, b"Pair-Verify-Encrypt-Info", 32) @@ -243,9 +257,11 @@ def pair_verify_m1_m2(self, client_public): c = ChaCha20_Poly1305.new(key=session_key, nonce=b"PV-Msg02") enc_tlv, tag = c.encrypt_and_digest(sub_tlv) - return [ Tlv8.Tag.STATE, PairingState.M2, - Tlv8.Tag.PUBLICKEY, self.accessory_curve_public, - Tlv8.Tag.ENCRYPTEDDATA, enc_tlv + tag ] + return [ + Tlv8.Tag.STATE, PairingState.M2, + Tlv8.Tag.PUBLICKEY, self.accessory_curve_public, + Tlv8.Tag.ENCRYPTEDDATA, enc_tlv + tag + ] def pair_verify_m3_m4(self, encrypted): prk = hkdf.hkdf_extract(b"Pair-Verify-Encrypt-Salt", self.accessory_shared_key) @@ -264,7 +280,9 @@ def pair_verify_m3_m4(self, encrypted): verify_key = nacl.signing.VerifyKey(self.device_ltpk) verify_key.verify(device_info, device_sig) - return [ Tlv8.Tag.STATE, PairingState.M4 ] + return [ + Tlv8.Tag.STATE, PairingState.M4 + ] # diff --git a/ap2/pairing/srp.py b/ap2/pairing/srp.py index 585db1e..3b094fc 100644 --- a/ap2/pairing/srp.py +++ b/ap2/pairing/srp.py @@ -114,10 +114,8 @@ def set_client_public(self, A): self.K = H(self.S) self.M1 = H(H(self.N) ^ H(self.g), H(self.username), self.s, self.A, self.B, self.K) - def verify(self, M1_client): if self.M1 != from_bytes(M1_client): raise Exception("Authentication failed - invalid proof") self.M2 = H(self.A, self.M1, self.K) return True - diff --git a/ap2/playfair.py b/ap2/playfair.py index 214c7cd..199ee43 100644 --- a/ap2/playfair.py +++ b/ap2/playfair.py @@ -17,7 +17,7 @@ class PlayFair: b'\x46\x50\x4c\x59\x03\x01\x02\x00\x00\x00\x00\x82\x02\x02\xc1\x69\xa3\x52\xee\xed\x35\xb1\x8c\xdd\x9c\x58\xd6\x4f\x16\xc1\x51\x9a\x89\xeb\x53\x17\xbd\x0d\x43\x36\xcd\x68\xf6\x38\xff\x9d\x01\x6a\x5b\x52\xb7\xfa\x92\x16\xb2\xb6\x54\x82\xc7\x84\x44\x11\x81\x21\xa2\xc7\xfe\xd8\x3d\xb7\x11\x9e\x91\x82\xaa\xd7\xd1\x8c\x70\x63\xe2\xa4\x57\x55\x59\x10\xaf\x9e\x0e\xfc\x76\x34\x7d\x16\x40\x43\x80\x7f\x58\x1e\xe4\xfb\xe4\x2c\xa9\xde\xdc\x1b\x5e\xb2\xa3\xaa\x3d\x2e\xcd\x59\xe7\xee\xe7\x0b\x36\x29\xf2\x2a\xfd\x16\x1d\x87\x73\x53\xdd\xb9\x9a\xdc\x8e\x07\x00\x6e\x56\xf8\x50\xce', b'\x46\x50\x4c\x59\x03\x01\x02\x00\x00\x00\x00\x82\x02\x03\x90\x01\xe1\x72\x7e\x0f\x57\xf9\xf5\x88\x0d\xb1\x04\xa6\x25\x7a\x23\xf5\xcf\xff\x1a\xbb\xe1\xe9\x30\x45\x25\x1a\xfb\x97\xeb\x9f\xc0\x01\x1e\xbe\x0f\x3a\x81\xdf\x5b\x69\x1d\x76\xac\xb2\xf7\xa5\xc7\x08\xe3\xd3\x28\xf5\x6b\xb3\x9d\xbd\xe5\xf2\x9c\x8a\x17\xf4\x81\x48\x7e\x3a\xe8\x63\xc6\x78\x32\x54\x22\xe6\xf7\x8e\x16\x6d\x18\xaa\x7f\xd6\x36\x25\x8b\xce\x28\x72\x6f\x66\x1f\x73\x88\x93\xce\x44\x31\x1e\x4b\xe6\xc0\x53\x51\x93\xe5\xef\x72\xe8\x68\x62\x33\x72\x9c\x22\x7d\x82\x0c\x99\x94\x45\xd8\x92\x46\xc8\xc3\x59'] - fp_header = b'\x46\x50\x4c\x59\x03\x01\x04\x00\x00\x00\x00\x14'; + fp_header = b'\x46\x50\x4c\x59\x03\x01\x04\x00\x00\x00\x00\x14' class fairplay_s: def __init__(self): @@ -28,19 +28,19 @@ def __init__(self): def fairplay_setup(self, fp, request): if request[4] != 3: # Unsupported fairplay version - return -1; + return -1 - type = request[self.TYPE_POSITION]; - seq = request[self.SEQ_POSITION]; + type = request[self.TYPE_POSITION] + seq = request[self.SEQ_POSITION] # fp.keymsglen = 0; if type == self.SETUP_MESSAGE_TYPE: if seq == self.SETUP1_MESSAGE_SEQ: - mode = request[self.MODE_POSITON]; + mode = request[self.MODE_POSITON] response = self.reply_message[mode] elif seq == self.SETUP2_MESSAGE_SEQ: response = self.fp_header response = response + request[len(request) - self.SETUP2_RESPONSE_SUFFIX_LENGTH:len(request)] return response else: - return None \ No newline at end of file + return None diff --git a/ap2/utils.py b/ap2/utils.py index 4301d04..fd1cfc1 100644 --- a/ap2/utils.py +++ b/ap2/utils.py @@ -4,14 +4,18 @@ import platform import subprocess + def get_logger(name, level="INFO"): - logging.basicConfig(filename="%s.log" % name, - filemode='a', - format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', - datefmt='%H:%M:%S', - level=level) + logging.basicConfig( + filename="%s.log" % name, + filemode='a', + format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level=level + ) return logging.getLogger(name) + def get_free_port(): free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) free_socket.bind(('0.0.0.0', 0)) @@ -20,17 +24,20 @@ def get_free_port(): free_socket.close() return port + def get_free_tcp_socket(): free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) free_socket.bind(('0.0.0.0', 0)) free_socket.listen(5) return free_socket + def get_free_udp_socket(): free_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) free_socket.bind(('0.0.0.0', 0)) return free_socket + def interpolate(value, from_min, from_max, to_min, to_max): from_span = from_max - from_min to_span = to_max - to_min @@ -39,6 +46,7 @@ def interpolate(value, from_min, from_max, to_min, to_max): return to_min + (value_scale * to_span) + def get_volume(): subsys = platform.system() if subsys == "Darwin": @@ -51,11 +59,12 @@ def get_volume(): pct = int(m.group(1)) if pct < 45: pct = 45 - else: pct = 50 + else: + pct = 50 vol = interpolate(pct, 45, 100, -30, 0) elif subsys == "Windows": # Volume get is not managed under windows, let's set to a default volume - vol = 50; + vol = 50 if vol == -30: return -144 return vol