Change Directory

This commit is contained in:
kokarare1212
2021-02-25 08:07:17 +09:00
parent 01dae8ada4
commit 8daf68831e
133 changed files with 221 additions and 38 deletions

View File

@@ -0,0 +1,62 @@
from librespot.crypto.Packet import Packet
from librespot.crypto.Shannon import Shannon
import struct
class CipherPair:
send_cipher: Shannon
receive_cipher: Shannon
send_nonce = 0
receive_nonce = 0
def __init__(self, send_key: bytes, receive_key: bytes):
# self.send_cipher = Shannon()
# self.send_cipher.key(send_key)
self.send_cipher = Shannon(send_key)
self.send_nonce = 0
# self.receive_cipher = Shannon()
# self.receive_cipher.key(receive_key)
self.receive_cipher = Shannon(receive_key)
self.receive_nonce = 0
def send_encoded(self, conn, cmd: bytes, payload: bytes):
self.send_cipher.nonce(self.send_nonce)
self.send_nonce += 1
buffer = b""
buffer += cmd
buffer += struct.pack(">H", len(payload))
buffer += payload
buffer = self.send_cipher.encrypt(buffer)
# mac = self.send_cipher.finish(bytes(4))
mac = self.send_cipher.finish(4)
conn.write(buffer)
conn.write(mac)
conn.flush()
def receive_encoded(self, conn) -> Packet:
try:
self.receive_cipher.nonce(self.receive_nonce)
self.receive_nonce += 1
header_bytes = self.receive_cipher.decrypt(conn.read(3))
cmd = struct.pack(">s", bytes([header_bytes[0]]))
payload_length = (header_bytes[1] << 8) | (header_bytes[2] & 0xff)
payload_bytes = self.receive_cipher.decrypt(
conn.read(payload_length))
mac = conn.read(4)
expected_mac = self.receive_cipher.finish(4)
if mac != expected_mac:
raise RuntimeError()
return Packet(cmd, payload_bytes)
except IndexError:
raise RuntimeError()

View File

@@ -0,0 +1,38 @@
from binascii import unhexlify
from librespot.common.Utils import Utils
import os
import struct
class DiffieHellman:
prime_bytes: bytearray = bytes([
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2,
0x21, 0x68, 0xc2, 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1,
0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6,
0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd,
0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d,
0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45,
0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9,
0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
])
prime: int = int.from_bytes(prime_bytes, "big")
private_key: int
public_key: int
def __init__(self):
key_data = os.urandom(95)
self.private_key = int.from_bytes(key_data, "big")
self.public_key = pow(2, self.private_key, self.prime)
def compute_shared_key(self, remote_key_bytes: bytes):
remote_key = int.from_bytes(remote_key_bytes, "big")
return pow(remote_key, self.private_key, self.prime)
def private_key(self):
return self.private_key
def public_key(self):
return self.public_key
def public_key_array(self):
return Utils.to_byte_array(self.public_key)

View File

@@ -0,0 +1,66 @@
import re
class Packet:
cmd: bytes
payload: bytes
def __init__(self, cmd: bytes, payload: bytes):
self.cmd = cmd
self.payload = payload
def is_cmd(self, cmd: bytes):
return cmd == self.cmd
class Type:
secret_block = b"\x02"
ping = b"\x04"
stream_chunk = b"\x08"
stream_chunk_res = b"\x09"
channel_error = b"\x0a"
channel_abort = b"\x0b"
request_key = b"\x0c"
aes_key = b"\x0d"
aes_key_error = b"\x0e"
image = b"\x19"
country_code = b"\x1b"
pong = b"\x49"
pong_ack = b"\x4a"
pause = b"\x4b"
product_info = b"\x50"
legacy_welcome = b"\x69"
license_version = b"\x76"
login = b"\xab"
ap_welcome = b"\xac"
auth_failure = b"\xad"
mercury_req = b"\xb2"
mercury_sub = b"\xb3"
mercury_unsub = b"\xb4"
mercury_event = b"\xb5"
track_ended_time = b"\x82"
unknown_data_all_zeros = b"\x1f"
preferred_locale = b"\x74"
unknown_0x4f = b"\x4f"
unknown_0x0f = b"\x0f"
unknown_0x10 = b"\x10"
@staticmethod
def parse(val: bytes):
for cmd in [
Packet.Type.__dict__[attr]
for attr in Packet.Type.__dict__.keys()
if re.search("__.+?__", attr) is None
and type(Packet.Type.__dict__[attr]) is bytes
]:
if cmd == val:
return cmd
return None
@staticmethod
def for_method(method: str):
if method == "SUB":
return Packet.Type.mercury_sub
if method == "UNSUB":
return Packet.Type.mercury_unsub
return Packet.Type.mercury_req

495
librespot/crypto/Shannon.py Normal file
View File

@@ -0,0 +1,495 @@
"""
Shannon: Shannon stream cipher and MAC -- reference implementation, ported from C code written by Greg Rose
https://github.com/sashahilton00/spotify-connect-resources/blob/master/Shannon-1.0/ShannonRef.c
Copyright 2017, Dmitry Borisov
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND AGAINST
INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import struct, \
copy
# Constants
N = 16
INITKONST = 0x6996c53a
KEYP = 13 # where to insert key/MAC/counter words
FOLD = N # how many iterations of folding to do
class Shannon:
@staticmethod
def ROTL(w, x):
return ((w << x) | (w >> (32 - x))) & 0xFFFFFFFF
@staticmethod
def ROTR(w, x):
return ((w >> x) | (w << (32 - x))) & 0xFFFFFFFF
""" Nonlinear transform (sbox) of a word.
There are two slightly different combinations. """
@staticmethod
def sbox1(w):
w ^= Shannon.ROTL(w, 5) | Shannon.ROTL(w, 7)
w ^= Shannon.ROTL(w, 19) | Shannon.ROTL(w, 22)
return w
""" Nonlinear transform (sbox) of a word.
There are two slightly different combinations. """
@staticmethod
def sbox2(w):
w ^= Shannon.ROTL(w, 7) | Shannon.ROTL(w, 22)
w ^= Shannon.ROTL(w, 5) | Shannon.ROTL(w, 19)
return w
""" initialise to known state """
def _initstate(self):
global N, \
INITKONST
# Generate fibonacci numbers up to N
self._R = [1, 1]
for x in range(1, N - 1):
self._R.append(self._R[x] + self._R[x - 1])
self._konst = INITKONST
""" cycle the contents of the register and calculate output word in _sbuf. """
def _cycle(self):
# nonlinear feedback function
t = self._R[12] ^ self._R[13] ^ self._konst
t = Shannon.sbox1(t) ^ Shannon.ROTL(self._R[0], 1)
# Shift to the left
self._R = self._R[1:] + [t]
t = Shannon.sbox2(self._R[2] ^ self._R[15])
self._R[0] ^= t
self._sbuf = t ^ self._R[8] ^ self._R[12]
""" The Shannon MAC function is modelled after the concepts of Phelix and SHA.
Basically, words to be accumulated in the MAC are incorporated in two
different ways:
1. They are incorporated into the stream cipher register at a place
where they will immediately have a nonlinear effect on the state
2. They are incorporated into bit-parallel CRC-16 registers; the
contents of these registers will be used in MAC finalization. """
""" Accumulate a CRC of input words, later to be fed into MAC.
This is actually 32 parallel CRC-16s, using the IBM CRC-16
polynomial x^16 + x^15 + x^2 + 1. """
def _crcfunc(self, i):
t = self._CRC[0] ^ self._CRC[2] ^ self._CRC[15] ^ i
# Accumulate CRC of input
self._CRC = self._CRC[1:] + [t]
""" Normal MAC word processing: do both stream register and CRC. """
def _macfunc(self, i):
global KEYP
self._crcfunc(i)
self._R[KEYP] ^= i
""" extra nonlinear diffusion of register for key and MAC """
def _diffuse(self):
global FOLD
for i in range(FOLD):
self._cycle()
""" Common actions for loading key material
Allow non-word-multiple key and nonce material.
Note also initializes the CRC register as a side effect. """
def _loadkey(self, key):
global KEYP, \
N
# Pad key with 00s to align on 4 bytes and add key_len
padding_size = int((len(key) + 3) / 4) * 4 - len(key)
key = key + (b'\x00' * padding_size) + struct.pack("<I", len(key))
for i in range(0, len(key), 4):
self._R[KEYP] = self._R[KEYP] ^ struct.unpack(
"<I", key[i:i + 4])[0] # Little Endian order
self._cycle()
# save a copy of the register
self._CRC = copy.copy(self._R)
# now diffuse
self._diffuse()
# now xor the copy back -- makes key loading irreversible */
for i in range(N):
self._R[i] ^= self._CRC[i]
""" Constructor """
def __init__(self, key):
self._initstate()
self._loadkey(key)
self._konst = self._R[0] # in case we proceed to stream generation
self._initR = copy.copy(self._R)
self._nbuf = 0
""" Published "IV" interface """
def nonce(self, nonce):
global INITKONST
if type(nonce) == int:
# Accept int as well (BigEndian)
nonce = bytes(struct.pack(">I", nonce))
self._R = copy.copy(self._initR)
self._konst = INITKONST
self._loadkey(nonce)
self._konst = self._R[0]
self._nbuf = 0
self._mbuf = 0
""" Encrypt small chunk """
def _encrypt_chunk(self, chunk):
result = []
for c in chunk:
self._mbuf ^= c << (32 - self._nbuf)
result.append(c ^ (self._sbuf >> (32 - self._nbuf)) & 0xFF)
self._nbuf -= 8
return result
""" Combined MAC and encryption.
Note that plaintext is accumulated for MAC. """
def encrypt(self, buf):
# handle any previously buffered bytes
result = []
if self._nbuf != 0:
head = buf[:(self._nbuf >> 3)]
buf = buf[(self._nbuf >> 3):]
result = self._encrypt_chunk(head)
if self._nbuf != 0:
return bytes(result)
# LFSR already cycled
self._macfunc(self._mbuf)
# Handle body
i = 0
while len(buf) >= 4:
self._cycle()
t = struct.unpack("<I", buf[i:i + 4])[0]
self._macfunc(t)
t ^= self._sbuf
result += struct.pack("<I", t)
buf = buf[4:]
# handle any trailing bytes
if len(buf):
self._cycle()
self._mbuf = 0
self._nbuf = 32
result += self._encrypt_chunk(buf)
return bytes(result)
""" Decrypt small chunk """
def _decrypt_chunk(self, chunk):
result = []
for c in chunk:
result.append(c ^ ((self._sbuf >> (32 - self._nbuf)) & 0xFF))
self._mbuf ^= result[-1] << (32 - self._nbuf)
self._nbuf -= 8
return result
""" Combined MAC and decryption.
Note that plaintext is accumulated for MAC. """
def decrypt(self, buf):
# handle any previously buffered bytes
result = []
if self._nbuf != 0:
head = buf[:(self._nbuf >> 3)]
buf = buf[(self._nbuf >> 3):]
result = self._decrypt_chunk(head)
if self._nbuf != 0:
return bytes(result)
# LFSR already cycled
self._macfunc(self._mbuf)
# Handle whole words
i = 0
while len(buf) >= 4:
self._cycle()
t = struct.unpack("<I", buf[i:i + 4])[0] ^ self._sbuf
self._macfunc(t)
result += struct.pack("<I", t)
buf = buf[4:]
# handle any trailing bytes
if len(buf):
self._cycle()
self._mbuf = 0
self._nbuf = 32
result += self._decrypt_chunk(buf)
return bytes(result)
""" Having accumulated a MAC, finish processing and return it.
Note that any unprocessed bytes are treated as if
they were encrypted zero bytes, so plaintext (zero) is accumulated. """
def finish(self, buf_len):
global KEYP, \
INITKONST
# handle any previously buffered bytes
if self._nbuf != 0:
# LFSR already cycled
self._macfunc(self._mbuf)
# perturb the MAC to mark end of input.
# Note that only the stream register is updated, not the CRC. This is an
# action that can't be duplicated by passing in plaintext, hence
# defeating any kind of extension attack.
self._cycle()
self._R[KEYP] ^= INITKONST ^ (self._nbuf << 3)
self._nbuf = 0
# now add the CRC to the stream register and diffuse it
for i in range(N):
self._R[i] ^= self._CRC[i]
self._diffuse()
result = []
# produce output from the stream buffer
i = 0
for i in range(0, buf_len, 4):
self._cycle()
if i + 4 <= buf_len:
result += struct.pack("<I", self._sbuf)
else:
sbuf = self._sbuf
for j in range(i, buf_len):
result.append(sbuf & 0xFF)
sbuf >>= 8
return bytes(result)
if __name__ == '__main__':
TESTSIZE = 23
TEST_KEY = b"test key 128bits"
TEST_PHRASE = b'\x00' * 20
sh = Shannon(
bytes([
133, 199, 15, 101, 207, 100, 229, 237, 15, 249, 248, 155, 76, 170,
62, 189, 239, 251, 147, 213, 22, 186, 157, 47, 218, 198, 235, 14,
171, 50, 11, 121
]))
sh.set_nonce(0)
p1 = sh.decrypt(
bytes([
235,
94,
210,
19,
246,
203,
195,
35,
22,
215,
80,
69,
158,
247,
110,
146,
241,
101,
199,
37,
67,
92,
5,
197,
112,
244,
77,
185,
197,
118,
119,
56,
164,
246,
159,
242,
56,
200,
39,
27,
141,
191,
37,
244,
244,
164,
44,
250,
59,
227,
245,
155,
239,
155,
137,
85,
244,
29,
52,
233,
180,
119,
166,
46,
252,
24,
141,
20,
135,
73,
144,
10,
176,
79,
88,
228,
140,
62,
173,
192,
117,
116,
152,
182,
246,
183,
88,
90,
73,
51,
159,
83,
227,
222,
140,
48,
157,
137,
185,
131,
201,
202,
122,
112,
207,
231,
153,
155,
9,
163,
225,
73,
41,
252,
249,
65,
33,
102,
83,
100,
36,
115,
174,
191,
43,
250,
113,
229,
146,
47,
154,
175,
55,
101,
73,
164,
49,
234,
103,
32,
53,
190,
236,
47,
210,
78,
141,
0,
176,
255,
79,
151,
159,
66,
20,
]))
print([hex(x) for x in p1])
print([hex(x) for x in sh.finish(4)])
sh.set_nonce(1)
print([hex(x) for x in sh.decrypt(bytes([173, 184, 50]))])
sh = Shannon(TEST_KEY)
sh.set_nonce(0)
encr = [sh.encrypt(bytes([x])) for x in TEST_PHRASE]
print('Encrypted 1-by-1 (len %d)' % len(encr), [hex(x[0]) for x in encr])
print(' sbuf %08x' % sh._sbuf)
print(' MAC', [hex(x) for x in sh.finish(4)])
sh.set_nonce(0)
encr = sh.encrypt(TEST_PHRASE)
print('Encrypted whole (len %d)' % len(encr), [hex(x) for x in encr])
print(' sbuf %08x' % sh._sbuf)
print(' MAC', [hex(x) for x in sh.finish(4)])
sh.set_nonce(0)
print('Decrypted whole', [hex(x) for x in sh.decrypt(encr)])
print(' MAC', [hex(x) for x in sh.finish(4)])
sh.set_nonce(0)
decr = [sh.decrypt(bytes([x])) for x in encr]
print('Decrypted 1-by-1', [hex(x[0]) for x in decr])
print(' MAC', [hex(x) for x in sh.finish(4)])

View File

@@ -0,0 +1,311 @@
import struct
class Shannon:
n = 16
fold = n
initkonst = 0x6996c53a
keyp = 13
r: list
crc: list
initr: list
konst: int
sbuf: int
mbuf: int
nbuf: int
def __init__(self):
self.r = [0 for _ in range(self.n)]
self.crc = [0 for _ in range(self.n)]
self.initr = [0 for _ in range(self.n)]
def rotl(self, i: int, distance: int):
return ((i << distance) | (i >> (32 - distance))) & 0xffffffff
def sbox(self, i: int):
i ^= self.rotl(i, 5) | self.rotl(i, 7)
i ^= self.rotl(i, 19) | self.rotl(i, 22)
return i
def sbox2(self, i: int):
i ^= self.rotl(i, 7) | self.rotl(i, 22)
i ^= self.rotl(i, 5) | self.rotl(i, 19)
return i
def cycle(self):
t: int
t = self.r[12] ^ self.r[13] ^ self.konst
t = self.sbox(t) ^ self.rotl(self.r[0], 1)
for i in range(1, self.n):
self.r[i - 1] = self.r[i]
self.r[self.n - 1] = t
t = self.sbox2(self.r[2] ^ self.r[15])
self.r[0] ^= t
self.sbuf = t ^ self.r[8] ^ self.r[12]
def crc_func(self, i: int):
t: int
t = self.crc[0] ^ self.crc[2] ^ self.crc[15] ^ i
for j in range(1, self.n):
self.crc[j - 1] = self.crc[j]
self.crc[self.n - 1] = t
def mac_func(self, i: int):
self.crc_func(i)
self.r[self.keyp] ^= i
def init_state(self):
self.r[0] = 1
self.r[1] = 1
for i in range(2, self.n):
self.r[i] = self.r[i - 1] + self.r[i - 2]
self.konst = self.initkonst
def save_state(self):
for i in range(self.n):
self.initr[i] = self.r[i]
def reload_state(self):
for i in range(self.n):
self.r[i] = self.initr[i]
def gen_konst(self):
self.konst = self.r[0]
def add_key(self, k: int):
self.r[self.keyp] ^= k
def diffuse(self):
for i in range(self.fold):
self.cycle()
def load_key(self, key: bytes):
extra = bytearray(4)
i: int
j: int
t: int
padding_size = int((len(key) + 3) / 4) * 4 - len(key)
key = key + (b"\x00" * padding_size) + struct.pack("<I", len(key))
for i in range(0, len(key), 4):
self.r[self.keyp] = \
self.r[self.keyp] ^ \
struct.unpack("<I", key[i: i + 4])[0]
self.cycle()
for i in range(self.n):
self.crc[i] = self.r[i]
self.diffuse()
for i in range(self.n):
self.r[i] ^= self.crc[i]
def key(self, key: bytes):
self.init_state()
self.load_key(key)
self.gen_konst()
self.save_state()
self.nbuf = 0
def nonce(self, nonce: bytes):
self.reload_state()
self.konst = self.initkonst
self.load_key(nonce)
self.gen_konst()
self.nbuf = 0
def encrypt(self, buffer: bytes, n: int = None):
if n is None:
return self.encrypt(buffer, len(buffer))
buffer = bytearray(buffer)
i = 0
j: int
t: int
if self.nbuf != 0:
while self.nbuf != 0 and n != 0:
self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf)
buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff
i += 1
self.nbuf -= 8
n -= 1
if self.nbuf != 0:
return
self.mac_func(self.mbuf)
j = n & ~0x03
while i < j:
self.cycle()
t = ((buffer[i + 3] & 0xFF) << 24) | \
((buffer[i + 2] & 0xFF) << 16) | \
((buffer[i + 1] & 0xFF) << 8) | \
(buffer[i] & 0xFF)
self.mac_func(t)
t ^= self.sbuf
buffer[i + 3] = (t >> 24) & 0xFF
buffer[i + 2] = (t >> 16) & 0xFF
buffer[i + 3] = (t >> 8) & 0xFF
buffer[i] = t & 0xFF
i += 4
n &= 0x03
if n != 0:
self.cycle()
self.mbuf = 0
self.nbuf = 32
while self.nbuf != 0 and n != 0:
self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf)
buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff
i += 1
self.nbuf -= 8
n -= 1
return bytes(buffer)
def decrypt(self, buffer: bytes, n: int = None):
if n is None:
return self.decrypt(buffer, len(buffer))
buffer = bytearray(buffer)
i = 0
j: int
t: int
if self.nbuf != 0:
while self.nbuf != 0 and n != 0:
buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff
self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf)
i += 1
self.nbuf -= 8
n -= 1
if self.nbuf != 0:
return
self.mac_func(self.mbuf)
j = n & ~0x03
while i < j:
self.cycle()
t = ((buffer[i + 3] & 0xFF) << 24) | \
((buffer[i + 2] & 0xFF) << 16) | \
((buffer[i + 1] & 0xFF) << 8) | \
(buffer[i] & 0xFF)
t ^= self.sbuf
self.mac_func(t)
buffer[i + 3] = (t >> 24) & 0xFF
buffer[i + 2] = (t >> 16) & 0xFF
buffer[i + 1] = (t >> 8) & 0xFF
buffer[i] = t & 0xFF
i += 4
n &= 0x03
if n != 0:
self.cycle()
self.mbuf = 0
self.nbuf = 32
while self.nbuf != 0 and n != 0:
buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff
self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf)
i += 1
self.nbuf -= 8
n -= 1
return bytes(buffer)
def finish(self, buffer: bytes, n: int = None):
if n is None:
return self.finish(buffer, len(buffer))
buffer = bytearray(buffer)
i = 0
j: int
if self.nbuf != 0:
self.mac_func(self.mbuf)
self.cycle()
self.add_key(self.initkonst ^ (self.nbuf << 3))
self.nbuf = 0
for j in range(self.n):
self.r[j] ^= self.crc[j]
self.diffuse()
while n > 0:
self.cycle()
if n >= 4:
buffer[i + 3] = (self.sbuf >> 24) & 0xff
buffer[i + 2] = (self.sbuf >> 16) & 0xff
buffer[i + 1] = (self.sbuf >> 8) & 0xff
buffer[i] = self.sbuf & 0xff
n -= 4
i += 4
else:
for j in range(n):
buffer[i + j] = (self.sbuf >> (i * 8)) & 0xff
break
return bytes(buffer)

View File

@@ -0,0 +1,4 @@
from librespot.crypto.CipherPair import CipherPair
from librespot.crypto.DiffieHellman import DiffieHellman
from librespot.crypto.Packet import Packet
from librespot.crypto.Shannon import Shannon