1# -*- coding: utf-8 -*- 2''' 3nacling.py raet protocol nacl (crypto) management classes 4''' 5 6# Import python libs 7import sys 8import time 9import binascii 10import six 11import libnacl 12 13from ioflo.base.consoling import getConsole 14console = getConsole() 15 16# Import raet libs 17# pylint: disable=wildcard-import,unused-wildcard-import,redefined-builtin 18from .abiding import * # import globals 19# pylint: enable=wildcard-import,unused-wildcard-import,redefined-builtin 20from . import encoding 21 22 23class CryptoError(Exception): 24 """ 25 Base exception for all nacl related errors 26 """ 27 28 29class BadSignatureError(CryptoError): 30 """ 31 Raised when the signature was forged or otherwise corrupt. 32 """ 33 34 35class EncryptedMessage(six.binary_type): 36 """ 37 A bytes subclass that holds a messaged that has been encrypted by a 38 :class:`SecretBox`. 39 """ 40 41 @classmethod 42 def _from_parts(cls, nonce, ciphertext, combined): 43 obj = cls(combined) 44 obj._nonce = nonce 45 obj._ciphertext = ciphertext 46 return obj 47 48 @property 49 def nonce(self): 50 """ 51 The nonce used during the encryption of the :class:`EncryptedMessage`. 52 """ 53 return self._nonce 54 55 @property 56 def ciphertext(self): 57 """ 58 The ciphertext contained within the :class:`EncryptedMessage`. 59 """ 60 return self._ciphertext 61 62 63class StringFixer(object): 64 ''' 65 Python 3 support 66 ''' 67 def __str__(self): 68 if six.PY3: 69 return self.__unicode__() 70 else: 71 return self.__bytes__() 72 73 74class PublicKey(encoding.Encodable, StringFixer, object): 75 """ 76 The public key counterpart to an Curve25519 :class:`PrivateKey` 77 for encrypting messages. 78 79 :param public_key: [:class:`bytes`] Encoded Curve25519 public key 80 :param encoder: A class that is able to decode the `public_key` 81 82 :cvar SIZE: The size that the public key is required to be 83 """ 84 85 SIZE = libnacl.crypto_box_PUBLICKEYBYTES 86 87 def __init__(self, public_key, encoder=encoding.RawEncoder): 88 self._public_key = encoder.decode(public_key) 89 90 if len(self._public_key) != self.SIZE: 91 raise ValueError("The public key must be exactly %s bytes long" % 92 self.SIZE) 93 94 def __bytes__(self): 95 return self._public_key 96 97 98class PrivateKey(encoding.Encodable, StringFixer, object): 99 """ 100 Private key for decrypting messages using the Curve25519 algorithm. 101 102 .. warning:: This **must** be protected and remain secret. Anyone who 103 knows the value of your :class:`~PrivateKey` can decrypt 104 any message encrypted by the corresponding 105 :class:`~PublicKey` 106 107 :param private_key: The private key used to decrypt messages 108 :param encoder: The encoder class used to decode the given keys 109 110 :cvar SIZE: The size that the private key is required to be 111 """ 112 113 SIZE = libnacl.crypto_box_SECRETKEYBYTES 114 115 def __init__(self, private_key, encoder=encoding.RawEncoder): 116 # Decode the secret_key 117 private_key = encoder.decode(private_key) 118 119 # Verify that our seed is the proper size 120 if len(private_key) != self.SIZE: 121 raise ValueError( 122 "The secret key must be exactly %d bytes long" % self.SIZE) 123 124 raw_public_key = libnacl.crypto_scalarmult_base(private_key) 125 126 self._private_key = private_key 127 self.public_key = PublicKey(raw_public_key) 128 129 def __bytes__(self): 130 return self._private_key 131 132 @classmethod 133 def generate(cls): 134 """ 135 Generates a random :class:`~PrivateKey` object 136 137 :rtype: :class:`~PrivateKey` 138 """ 139 return cls(libnacl.randombytes(PrivateKey.SIZE), encoder=encoding.RawEncoder) 140 141 142class Box(encoding.Encodable, StringFixer, object): 143 """ 144 The Box class boxes and unboxes messages between a pair of keys 145 146 The ciphertexts generated by :class:`~Box` include a 16 147 byte authenticator which is checked as part of the decryption. An invalid 148 authenticator will cause the decrypt function to raise an exception. The 149 authenticator is not a signature. Once you've decrypted the message you've 150 demonstrated the ability to create arbitrary valid message, so messages you 151 send are repudiable. For non-repudiable messages, sign them after 152 encryption. 153 154 :param private_key: :class:`~PrivateKey` used to encrypt and 155 decrypt messages 156 :param public_key: :class:`~PublicKey` used to encrypt and 157 decrypt messages 158 159 :cvar NONCE_SIZE: The size that the nonce is required to be. 160 """ 161 162 NONCE_SIZE = libnacl.crypto_box_NONCEBYTES 163 164 def __init__(self, private_key, public_key): 165 if private_key and public_key: 166 self._shared_key = libnacl.crypto_box_beforenm( 167 public_key.encode(encoder=encoding.RawEncoder), 168 private_key.encode(encoder=encoding.RawEncoder), 169 ) 170 else: 171 self._shared_key = None 172 173 def __bytes__(self): 174 return self._shared_key 175 176 @classmethod 177 def decode(cls, encoded, encoder=encoding.RawEncoder): 178 # Create an empty box 179 box = cls(None, None) 180 181 # Assign our decoded value to the shared key of the box 182 box._shared_key = encoder.decode(encoded) 183 184 return box 185 186 def encrypt(self, plaintext, nonce, encoder=encoding.RawEncoder): 187 """ 188 Encrypts the plaintext message using the given `nonce` and returns 189 the ciphertext encoded with the encoder. 190 191 .. warning:: It is **VITALLY** important that the nonce is a nonce, 192 i.e. it is a number used only once for any given key. If you fail 193 to do this, you compromise the privacy of the messages encrypted. 194 195 :param plaintext: [:class:`bytes`] The plaintext message to encrypt 196 :param nonce: [:class:`bytes`] The nonce to use in the encryption 197 :param encoder: The encoder to use to encode the ciphertext 198 :rtype: [:class:`nacl.utils.EncryptedMessage`] 199 """ 200 if len(nonce) != self.NONCE_SIZE: 201 raise ValueError("The nonce must be exactly %s bytes long" % 202 self.NONCE_SIZE) 203 204 ciphertext = libnacl.crypto_box_afternm( 205 plaintext, 206 nonce, 207 self._shared_key, 208 ) 209 210 encoded_nonce = encoder.encode(nonce) 211 encoded_ciphertext = encoder.encode(ciphertext) 212 213 return EncryptedMessage._from_parts( 214 encoded_nonce, 215 encoded_ciphertext, 216 encoder.encode(nonce + ciphertext), 217 ) 218 219 def decrypt(self, ciphertext, nonce=None, encoder=encoding.RawEncoder): 220 """ 221 Decrypts the ciphertext using the given nonce and returns the 222 plaintext message. 223 224 :param ciphertext: [:class:`bytes`] The encrypted message to decrypt 225 :param nonce: [:class:`bytes`] The nonce used when encrypting the 226 ciphertext 227 :param encoder: The encoder used to decode the ciphertext. 228 :rtype: [:class:`bytes`] 229 """ 230 # Decode our ciphertext 231 ciphertext = encoder.decode(ciphertext) 232 233 if nonce is None: 234 # If we were given the nonce and ciphertext combined, split them. 235 nonce = ciphertext[:self.NONCE_SIZE] 236 ciphertext = ciphertext[self.NONCE_SIZE:] 237 238 if len(nonce) != self.NONCE_SIZE: 239 raise ValueError("The nonce must be exactly %s bytes long" % 240 self.NONCE_SIZE) 241 242 plaintext = libnacl.crypto_box_open_afternm( 243 ciphertext, 244 nonce, 245 self._shared_key, 246 ) 247 248 return plaintext 249 250 251class SignedMessage(six.binary_type): 252 """ 253 A bytes subclass that holds a messaged that has been signed by a 254 :class:`SigningKey`. 255 """ 256 257 @classmethod 258 def _from_parts(cls, signature, message, combined): 259 obj = cls(combined) 260 obj._signature = signature 261 obj._message = message 262 return obj 263 264 @property 265 def signature(self): 266 """ 267 The signature contained within the :class:`SignedMessage`. 268 """ 269 return self._signature 270 271 @property 272 def message(self): 273 """ 274 The message contained within the :class:`SignedMessage`. 275 """ 276 return self._message 277 278 279class VerifyKey(encoding.Encodable, StringFixer, object): 280 """ 281 The public key counterpart to an Ed25519 SigningKey for producing digital 282 signatures. 283 284 :param key: [:class:`bytes`] Serialized Ed25519 public key 285 :param encoder: A class that is able to decode the `key` 286 """ 287 288 def __init__(self, key, encoder=encoding.RawEncoder): 289 # Decode the key 290 key = encoder.decode(key) 291 292 if len(key) != libnacl.crypto_sign_PUBLICKEYBYTES: 293 raise ValueError( 294 "The key must be exactly %s bytes long" % 295 libnacl.crypto_sign_PUBLICKEYBYTES, 296 ) 297 298 self._key = key 299 300 def __bytes__(self): 301 return self._key 302 303 def verify(self, smessage, signature=None, encoder=encoding.RawEncoder): 304 """ 305 Verifies the signature of a signed message, returning the message 306 if it has not been tampered with else raising 307 :class:`~ValueError`. 308 309 :param smessage: [:class:`bytes`] Either the original messaged or a 310 signature and message concated together. 311 :param signature: [:class:`bytes`] If an unsigned message is given for 312 smessage then the detached signature must be provded. 313 :param encoder: A class that is able to decode the secret message and 314 signature. 315 :rtype: :class:`bytes` 316 """ 317 if signature is not None: 318 # If we were given the message and signature separately, combine 319 # them. 320 smessage = signature + smessage 321 322 # Decode the signed message 323 smessage = encoder.decode(smessage) 324 325 return libnacl.crypto_sign_open(smessage, self._key) 326 327 328class SigningKey(encoding.Encodable, StringFixer, object): 329 """ 330 Private key for producing digital signatures using the Ed25519 algorithm. 331 332 Signing keys are produced from a 32-byte (256-bit) random seed value. This 333 value can be passed into the :class:`~SigningKey` as a 334 :func:`bytes` whose length is 32. 335 336 .. warning:: This **must** be protected and remain secret. Anyone who knows 337 the value of your :class:`~SigningKey` or it's seed can 338 masquerade as you. 339 340 :param seed: [:class:`bytes`] Random 32-byte value (i.e. private key) 341 :param encoder: A class that is able to decode the seed 342 343 :ivar: verify_key: [:class:`~VerifyKey`] The verify 344 (i.e. public) key that corresponds with this signing key. 345 """ 346 347 def __init__(self, seed, encoder=encoding.RawEncoder): 348 # Decode the seed 349 seed = encoder.decode(seed) 350 351 # Verify that our seed is the proper size 352 if len(seed) != libnacl.crypto_sign_SEEDBYTES: 353 raise ValueError( 354 "The seed must be exactly %d bytes long" % 355 libnacl.crypto_sign_SEEDBYTES 356 ) 357 358 public_key, secret_key = libnacl.crypto_sign_seed_keypair(seed) 359 360 self._seed = seed 361 self._signing_key = secret_key 362 self.verify_key = VerifyKey(public_key) 363 364 def __bytes__(self): 365 return self._seed 366 367 @classmethod 368 def generate(cls): 369 """ 370 Generates a random :class:`~SigningKey` object. 371 372 :rtype: :class:`~SigningKey` 373 """ 374 return cls( 375 libnacl.randombytes(libnacl.crypto_sign_SEEDBYTES), 376 encoder=encoding.RawEncoder, 377 ) 378 379 def sign(self, message, encoder=encoding.RawEncoder): 380 """ 381 Sign a message using this key. 382 383 :param message: [:class:`bytes`] The data to be signed. 384 :param encoder: A class that is used to encode the signed message. 385 :rtype: :class:`~SignedMessage` 386 """ 387 raw_signed = libnacl.crypto_sign(message, self._signing_key) 388 389 signature = encoder.encode(raw_signed[:libnacl.crypto_sign_BYTES]) 390 message = encoder.encode(raw_signed[libnacl.crypto_sign_BYTES:]) 391 signed = encoder.encode(raw_signed) 392 393 return SignedMessage._from_parts(signature, message, signed) 394 395 396class Signer(object): 397 ''' 398 Used to sign messages with nacl digital signature 399 ''' 400 def __init__(self, key=None): 401 if key: 402 if not isinstance(key, SigningKey): # not key so seed to regenerate 403 if len(key) == 32: 404 key = SigningKey(seed=key, encoder=encoding.RawEncoder) 405 else: 406 key = SigningKey(seed=key, encoder=encoding.HexEncoder) 407 else: 408 key = SigningKey.generate() 409 self.key = key 410 self.keyhex = self.key.encode(encoding.HexEncoder) # seed 411 self.keyraw = self.key.encode(encoding.RawEncoder) # seed 412 self.verhex = self.key.verify_key.encode(encoding.HexEncoder) 413 self.verraw = self.key.verify_key.encode(encoding.RawEncoder) 414 415 def sign(self, msg): 416 ''' 417 Sign the message 418 ''' 419 return self.key.sign(msg) 420 421 def signature(self, msg): 422 ''' 423 Return only the signature string resulting from signing the message 424 ''' 425 return self.key.sign(msg).signature 426 427 428class Verifier(object): 429 ''' 430 Used to verify messages with nacl digital signature 431 ''' 432 def __init__(self, key=None): 433 if key: 434 if not isinstance(key, VerifyKey): 435 if len(key) == 32: 436 key = VerifyKey(key, encoding.RawEncoder) 437 else: 438 key = VerifyKey(key, encoding.HexEncoder) 439 self.key = key 440 if isinstance(self.key, VerifyKey): 441 self.keyhex = self.key.encode(encoding.HexEncoder) 442 self.keyraw = self.key.encode(encoding.RawEncoder) 443 else: 444 self.keyhex = '' 445 self.keyraw = '' 446 447 def verify(self, signature, msg): 448 ''' 449 Verify the message 450 ''' 451 if not self.key: 452 return False 453 try: 454 self.key.verify(signature + msg) 455 except ValueError: 456 return False 457 return True 458 459 460class Publican(object): 461 ''' 462 Container to manage remote nacl public key 463 .key is the public key 464 Intelligently converts hex encoded to object 465 ''' 466 def __init__(self, key=None): 467 if key: 468 if not isinstance(key, PublicKey): 469 if len(key) == 32: 470 key = PublicKey(key, encoding.RawEncoder) 471 else: 472 key = PublicKey(key, encoding.HexEncoder) 473 self.key = key 474 if isinstance(self.key, PublicKey): 475 self.keyhex = self.key.encode(encoding.HexEncoder) 476 self.keyraw = self.key.encode(encoding.RawEncoder) 477 else: 478 self.keyhex = '' 479 self.keyraw = '' 480 481 482class Privateer(object): 483 ''' 484 Container for local nacl key pair 485 .key is the private key 486 ''' 487 def __init__(self, key=None): 488 if key: 489 if not isinstance(key, PrivateKey): 490 if len(key) == 32: 491 key = PrivateKey(key, encoding.RawEncoder) 492 else: 493 key = PrivateKey(key, encoding.HexEncoder) 494 else: 495 key = PrivateKey.generate() 496 self.key = key 497 self.keyhex = self.key.encode(encoding.HexEncoder) 498 self.keyraw = self.key.encode(encoding.RawEncoder) 499 self.pubhex = self.key.public_key.encode(encoding.HexEncoder) 500 self.pubraw = self.key.public_key.encode(encoding.RawEncoder) 501 502 def nonce(self): 503 ''' 504 Generate a safe nonce value (safe assuming only this method is used to 505 create nonce values) 506 ''' 507 return libnacl.randombytes(Box.NONCE_SIZE) 508 509 def encrypt(self, msg, pubkey, enhex=False): 510 ''' 511 Return duple of (cyphertext, nonce) resulting from encrypting the message 512 using shared key generated from the .key and the pubkey 513 If pubkey is hex encoded it is converted first 514 If enhex is True then use HexEncoder otherwise use RawEncoder 515 516 Intended for the owner of the passed in public key 517 518 msg is string 519 pub is Publican instance 520 ''' 521 if not isinstance(pubkey, PublicKey): 522 if len(pubkey) == 32: 523 pubkey = PublicKey(pubkey, encoding.RawEncoder) 524 else: 525 pubkey = PublicKey(pubkey, encoding.HexEncoder) 526 box = Box(self.key, pubkey) 527 nonce = self.nonce() 528 encoder = encoding.HexEncoder if enhex else encoding.RawEncoder 529 encrypted = box.encrypt(msg, nonce, encoder) 530 return (encrypted.ciphertext, encrypted.nonce) 531 532 def decrypt(self, cipher, nonce, pubkey, dehex=False): 533 ''' 534 Return decrypted msg contained in cypher using nonce and shared key 535 generated from .key and pubkey. 536 If pubkey is hex encoded it is converted first 537 If dehex is True then use HexEncoder otherwise use RawEncoder 538 539 Intended for the owner of .key 540 541 cypher is string 542 nonce is string 543 pub is Publican instance 544 ''' 545 if not isinstance(pubkey, PublicKey): 546 if len(pubkey) == 32: 547 pubkey = PublicKey(pubkey, encoding.RawEncoder) 548 else: 549 pubkey = PublicKey(pubkey, encoding.HexEncoder) 550 box = Box(self.key, pubkey) 551 decoder = encoding.HexEncoder if dehex else encoding.RawEncoder 552 if dehex and len(nonce) != box.NONCE_SIZE: 553 nonce = decoder.decode(nonce) 554 return box.decrypt(cipher, nonce, decoder) 555 556 557def uuid(size=16): 558 ''' 559 Generate universally unique id hex string with size characters 560 Timebased with random bytes 561 Minimum size is 16 562 563 Uses time.clock instead of time.time on windows. 564 Tests of rapid uuid generation fail to generate unique uuids 565 on Windows with time.time(). 566 See http://www.pythoncentral.io/measure-time-in-python-time-time-vs-time-clock/ 567 for discussion. 568 ''' 569 size = max(int(size), 16) 570 if sys.platform == 'win32': 571 front = ns2b("{0:0x}".format(int(time.clock() * 1000000))) # microseconds 572 else: 573 front = ns2b("{0:0x}".format(int(time.time() * 1000000))) # microseconds 574 extra = size - len(front) 575 back = binascii.hexlify(libnacl.randombytes(extra // 2 + extra % 2)) 576 return ((front + back)[:size]).decode(encoding='ISO-8859-1') 577