1""" 2Contains functions to decode, encode and generate keys. 3""" 4import enum 5import hashlib 6import hmac 7 8import libnacl.encode 9import libnacl.public 10import libnacl.secret 11 12from .exception import GatewayKeyError 13 14__all__ = ( 15 'HMAC', 16 'Key', 17) 18 19 20class HMAC: 21 """ 22 A collection of HMAC functions used for the gateway service. 23 """ 24 keys = { 25 'email': b'\x30\xa5\x50\x0f\xed\x97\x01\xfa\x6d\xef\xdb\x61\x08\x41\x90\x0f' 26 b'\xeb\xb8\xe4\x30\x88\x1f\x7a\xd8\x16\x82\x62\x64\xec\x09\xba\xd7', 27 'phone': b'\x85\xad\xf8\x22\x69\x53\xf3\xd9\x6c\xfd\x5d\x09\xbf\x29\x55\x5e' 28 b'\xb9\x55\xfc\xd8\xaa\x5e\xc4\xf9\xfc\xd8\x69\xe2\x58\x37\x07\x23' 29 } 30 31 @staticmethod 32 def hash(message, hash_type): 33 """ 34 Generate the hash for a message type. 35 36 Arguments: 37 - `message`: A message. 38 - `hash_type`: `email` or `phone`. 39 40 Return a :class:`hmac.HMAC` instance. 41 """ 42 return hmac.new(HMAC.keys[hash_type], message.encode('ascii'), hashlib.sha256) 43 44 45class Key: 46 """ 47 Encode or decode a key. 48 """ 49 separator = ':' 50 51 @enum.unique 52 class Type(enum.Enum): 53 """ 54 The type of a key. 55 """ 56 private = 'private' 57 public = 'public' 58 59 @staticmethod 60 def decode(encoded_key, expected_type): 61 """ 62 Decode a key and check its type if required. 63 64 Arguments: 65 - `encoded_key`: The encoded key. 66 - `expected_type`: One of the types of :class:`Key.Type`. 67 68 Return the key as an :class:`libnacl.public.SecretKey` or 69 :class:`libnacl.public.PublicKey` instance. 70 """ 71 # Split key 72 try: 73 type_, key = encoded_key.split(Key.separator) 74 except ValueError as exc: 75 raise GatewayKeyError('Invalid key format') from exc 76 type_ = Key.Type(type_) 77 78 # Check type 79 if type_ != expected_type: 80 raise GatewayKeyError('Invalid key type: {}, expected: {}'.format( 81 type_, expected_type 82 )) 83 84 # De-hexlify 85 key = libnacl.encode.hex_decode(key) 86 87 # Convert to SecretKey or PublicKey 88 if type_ == Key.Type.private: 89 key = libnacl.public.SecretKey(key) 90 elif type_ == Key.Type.public: 91 key = libnacl.public.PublicKey(key) 92 93 return key 94 95 @staticmethod 96 def encode(libnacl_key): 97 """ 98 Encode a key. 99 100 Arguments: 101 - `libnacl_key`: An instance of either a 102 :class:`libnacl.public.SecretKey` or a 103 :class:`libnacl.public.PublicKey`. 104 105 Return the encoded key. 106 """ 107 # Detect key type and hexlify 108 if isinstance(libnacl_key, libnacl.public.SecretKey): 109 type_ = Key.Type.private 110 key = libnacl_key.hex_sk() 111 elif isinstance(libnacl_key, libnacl.public.PublicKey): 112 type_ = Key.Type.public 113 key = libnacl.encode.hex_encode(libnacl_key.pk) 114 else: 115 raise GatewayKeyError('Unknown key type: {}'.format(libnacl_key)) 116 117 # Encode key 118 return Key.separator.join((type_.value, key.decode('utf-8'))) 119 120 @staticmethod 121 def generate_pair(): 122 """ 123 Generate a new key pair. 124 125 Return the key pair as a tuple of a 126 :class:`libnacl.public.SecretKey` instance and a 127 :class:`libnacl.public.PublicKey` instance. 128 """ 129 private_key = libnacl.public.SecretKey() 130 public_key = libnacl.public.PublicKey(private_key.pk) 131 return private_key, public_key 132 133 @staticmethod 134 def generate_secret_key(): 135 """ 136 Generate a new secret key box. 137 138 Return a tuple of the key's :class:`bytes` and hex-encoded 139 representation. 140 """ 141 box = libnacl.secret.SecretBox() 142 return box.sk, box.hex_sk() 143 144 @staticmethod 145 def derive_public(private_key): 146 """ 147 Derive a public key from a class:`libnacl.public.SecretKey` 148 instance. 149 150 Arguments: 151 - `private_key`: A class:`libnacl.public.SecretKey` 152 instance. 153 154 Return the :class:`libnacl.public.PublicKey` instance. 155 """ 156 return libnacl.public.PublicKey(private_key.pk) 157