You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
13 KiB

7 years ago
  1. # Copyright 2013 Donald Stufft and individual contributors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from __future__ import absolute_import, division, print_function
  15. import nacl.bindings
  16. from nacl import encoding
  17. from nacl import exceptions as exc
  18. from nacl.utils import EncryptedMessage, StringFixer, random
  19. class PublicKey(encoding.Encodable, StringFixer, object):
  20. """
  21. The public key counterpart to an Curve25519 :class:`nacl.public.PrivateKey`
  22. for encrypting messages.
  23. :param public_key: [:class:`bytes`] Encoded Curve25519 public key
  24. :param encoder: A class that is able to decode the `public_key`
  25. :cvar SIZE: The size that the public key is required to be
  26. """
  27. SIZE = nacl.bindings.crypto_box_PUBLICKEYBYTES
  28. def __init__(self, public_key, encoder=encoding.RawEncoder):
  29. self._public_key = encoder.decode(public_key)
  30. if not isinstance(self._public_key, bytes):
  31. raise exc.TypeError("PublicKey must be created from 32 bytes")
  32. if len(self._public_key) != self.SIZE:
  33. raise exc.ValueError(
  34. "The public key must be exactly {0} bytes long".format(
  35. self.SIZE
  36. )
  37. )
  38. def __bytes__(self):
  39. return self._public_key
  40. def __hash__(self):
  41. return hash(bytes(self))
  42. def __eq__(self, other):
  43. if not isinstance(other, self.__class__):
  44. return False
  45. return nacl.bindings.sodium_memcmp(bytes(self), bytes(other))
  46. def __ne__(self, other):
  47. return not (self == other)
  48. class PrivateKey(encoding.Encodable, StringFixer, object):
  49. """
  50. Private key for decrypting messages using the Curve25519 algorithm.
  51. .. warning:: This **must** be protected and remain secret. Anyone who
  52. knows the value of your :class:`~nacl.public.PrivateKey` can decrypt
  53. any message encrypted by the corresponding
  54. :class:`~nacl.public.PublicKey`
  55. :param private_key: The private key used to decrypt messages
  56. :param encoder: The encoder class used to decode the given keys
  57. :cvar SIZE: The size that the private key is required to be
  58. :cvar SEED_SIZE: The size that the seed used to generate the
  59. private key is required to be
  60. """
  61. SIZE = nacl.bindings.crypto_box_SECRETKEYBYTES
  62. SEED_SIZE = nacl.bindings.crypto_box_SEEDBYTES
  63. def __init__(self, private_key, encoder=encoding.RawEncoder):
  64. # Decode the secret_key
  65. private_key = encoder.decode(private_key)
  66. # verify the given secret key type and size are correct
  67. if not (isinstance(private_key, bytes) and
  68. len(private_key) == self.SIZE):
  69. raise exc.TypeError(("PrivateKey must be created from a {0} "
  70. "bytes long raw secret key").format(self.SIZE)
  71. )
  72. raw_public_key = nacl.bindings.crypto_scalarmult_base(private_key)
  73. self._private_key = private_key
  74. self.public_key = PublicKey(raw_public_key)
  75. @classmethod
  76. def from_seed(cls, seed, encoder=encoding.RawEncoder):
  77. """
  78. Generate a PrivateKey using a deterministic construction
  79. starting from a caller-provided seed
  80. .. warning:: The seed **must** be high-entropy; therefore,
  81. its generator **must** be a cryptographic quality
  82. random function like, for example, :func:`~nacl.utils.random`.
  83. .. warning:: The seed **must** be protected and remain secret.
  84. Anyone who knows the seed is really in possession of
  85. the corresponding PrivateKey.
  86. :param seed: The seed used to generate the private key
  87. :rtype: :class:`~nacl.public.PrivateKey`
  88. """
  89. # decode the seed
  90. seed = encoder.decode(seed)
  91. # Verify the given seed type and size are correct
  92. if not (isinstance(seed, bytes) and len(seed) == cls.SEED_SIZE):
  93. raise exc.TypeError(("PrivateKey seed must be a {0} bytes long "
  94. "binary sequence").format(cls.SEED_SIZE)
  95. )
  96. # generate a raw keypair from the given seed
  97. raw_pk, raw_sk = nacl.bindings.crypto_box_seed_keypair(seed)
  98. # construct a instance from the raw secret key
  99. return cls(raw_sk)
  100. def __bytes__(self):
  101. return self._private_key
  102. def __hash__(self):
  103. return hash((type(self), bytes(self.public_key)))
  104. def __eq__(self, other):
  105. if not isinstance(other, self.__class__):
  106. return False
  107. return self.public_key == other.public_key
  108. def __ne__(self, other):
  109. return not (self == other)
  110. @classmethod
  111. def generate(cls):
  112. """
  113. Generates a random :class:`~nacl.public.PrivateKey` object
  114. :rtype: :class:`~nacl.public.PrivateKey`
  115. """
  116. return cls(random(PrivateKey.SIZE), encoder=encoding.RawEncoder)
  117. class Box(encoding.Encodable, StringFixer, object):
  118. """
  119. The Box class boxes and unboxes messages between a pair of keys
  120. The ciphertexts generated by :class:`~nacl.public.Box` include a 16
  121. byte authenticator which is checked as part of the decryption. An invalid
  122. authenticator will cause the decrypt function to raise an exception. The
  123. authenticator is not a signature. Once you've decrypted the message you've
  124. demonstrated the ability to create arbitrary valid message, so messages you
  125. send are repudiable. For non-repudiable messages, sign them after
  126. encryption.
  127. :param private_key: :class:`~nacl.public.PrivateKey` used to encrypt and
  128. decrypt messages
  129. :param public_key: :class:`~nacl.public.PublicKey` used to encrypt and
  130. decrypt messages
  131. :cvar NONCE_SIZE: The size that the nonce is required to be.
  132. """
  133. NONCE_SIZE = nacl.bindings.crypto_box_NONCEBYTES
  134. def __init__(self, private_key, public_key):
  135. if private_key and public_key:
  136. if ((not isinstance(private_key, PrivateKey) or
  137. not isinstance(public_key, PublicKey))):
  138. raise exc.TypeError("Box must be created from "
  139. "a PrivateKey and a PublicKey")
  140. self._shared_key = nacl.bindings.crypto_box_beforenm(
  141. public_key.encode(encoder=encoding.RawEncoder),
  142. private_key.encode(encoder=encoding.RawEncoder),
  143. )
  144. else:
  145. self._shared_key = None
  146. def __bytes__(self):
  147. return self._shared_key
  148. @classmethod
  149. def decode(cls, encoded, encoder=encoding.RawEncoder):
  150. # Create an empty box
  151. box = cls(None, None)
  152. # Assign our decoded value to the shared key of the box
  153. box._shared_key = encoder.decode(encoded)
  154. return box
  155. def encrypt(self, plaintext, nonce=None, encoder=encoding.RawEncoder):
  156. """
  157. Encrypts the plaintext message using the given `nonce` (or generates
  158. one randomly if omitted) and returns the ciphertext encoded with the
  159. encoder.
  160. .. warning:: It is **VITALLY** important that the nonce is a nonce,
  161. i.e. it is a number used only once for any given key. If you fail
  162. to do this, you compromise the privacy of the messages encrypted.
  163. :param plaintext: [:class:`bytes`] The plaintext message to encrypt
  164. :param nonce: [:class:`bytes`] The nonce to use in the encryption
  165. :param encoder: The encoder to use to encode the ciphertext
  166. :rtype: [:class:`nacl.utils.EncryptedMessage`]
  167. """
  168. if nonce is None:
  169. nonce = random(self.NONCE_SIZE)
  170. if len(nonce) != self.NONCE_SIZE:
  171. raise exc.ValueError("The nonce must be exactly %s bytes long" %
  172. self.NONCE_SIZE)
  173. ciphertext = nacl.bindings.crypto_box_afternm(
  174. plaintext,
  175. nonce,
  176. self._shared_key,
  177. )
  178. encoded_nonce = encoder.encode(nonce)
  179. encoded_ciphertext = encoder.encode(ciphertext)
  180. return EncryptedMessage._from_parts(
  181. encoded_nonce,
  182. encoded_ciphertext,
  183. encoder.encode(nonce + ciphertext),
  184. )
  185. def decrypt(self, ciphertext, nonce=None, encoder=encoding.RawEncoder):
  186. """
  187. Decrypts the ciphertext using the `nonce` (explicitly, when passed as a
  188. parameter or implicitly, when omitted, as part of the ciphertext) and
  189. returns the plaintext message.
  190. :param ciphertext: [:class:`bytes`] The encrypted message to decrypt
  191. :param nonce: [:class:`bytes`] The nonce used when encrypting the
  192. ciphertext
  193. :param encoder: The encoder used to decode the ciphertext.
  194. :rtype: [:class:`bytes`]
  195. """
  196. # Decode our ciphertext
  197. ciphertext = encoder.decode(ciphertext)
  198. if nonce is None:
  199. # If we were given the nonce and ciphertext combined, split them.
  200. nonce = ciphertext[:self.NONCE_SIZE]
  201. ciphertext = ciphertext[self.NONCE_SIZE:]
  202. if len(nonce) != self.NONCE_SIZE:
  203. raise exc.ValueError("The nonce must be exactly %s bytes long" %
  204. self.NONCE_SIZE)
  205. plaintext = nacl.bindings.crypto_box_open_afternm(
  206. ciphertext,
  207. nonce,
  208. self._shared_key,
  209. )
  210. return plaintext
  211. def shared_key(self):
  212. """
  213. Returns the Curve25519 shared secret, that can then be used as a key in
  214. other symmetric ciphers.
  215. .. warning:: It is **VITALLY** important that you use a nonce with your
  216. symmetric cipher. If you fail to do this, you compromise the
  217. privacy of the messages encrypted. Ensure that the key length of
  218. your cipher is 32 bytes.
  219. :rtype: [:class:`bytes`]
  220. """
  221. return self._shared_key
  222. class SealedBox(encoding.Encodable, StringFixer, object):
  223. """
  224. The SealedBox class boxes and unboxes messages addressed to
  225. a specified key-pair by using ephemeral sender's keypairs,
  226. whose private part will be discarded just after encrypting
  227. a single plaintext message.
  228. The ciphertexts generated by :class:`~nacl.public.SecretBox` include
  229. the public part of the ephemeral key before the :class:`~nacl.public.Box`
  230. ciphertext.
  231. :param public_key: :class:`~nacl.public.PublicKey` used to encrypt
  232. messages and derive nonces
  233. :param private_key: :class:`~nacl.public.PrivateKey` used to decrypt
  234. messages
  235. .. versionadded:: 1.2
  236. """
  237. def __init__(self, recipient_key):
  238. if isinstance(recipient_key, PublicKey):
  239. self._public_key = recipient_key.encode(
  240. encoder=encoding.RawEncoder)
  241. self._private_key = None
  242. elif isinstance(recipient_key, PrivateKey):
  243. self._private_key = recipient_key.encode(
  244. encoder=encoding.RawEncoder)
  245. self._public_key = recipient_key.public_key.encode(
  246. encoder=encoding.RawEncoder)
  247. else:
  248. raise exc.TypeError("SealedBox must be created from "
  249. "a PublicKey or a PrivateKey")
  250. def __bytes__(self):
  251. return self._public_key
  252. def encrypt(self, plaintext, encoder=encoding.RawEncoder):
  253. """
  254. Encrypts the plaintext message using a random-generated ephemeral
  255. keypair and returns a "composed ciphertext", containing both
  256. the public part of the keypair and the ciphertext proper,
  257. encoded with the encoder.
  258. The private part of the ephemeral key-pair will be scrubbed before
  259. returning the ciphertext, therefore, the sender will not be able to
  260. decrypt the generated ciphertext.
  261. :param plaintext: [:class:`bytes`] The plaintext message to encrypt
  262. :param encoder: The encoder to use to encode the ciphertext
  263. :return bytes: encoded ciphertext
  264. """
  265. ciphertext = nacl.bindings.crypto_box_seal(
  266. plaintext,
  267. self._public_key
  268. )
  269. encoded_ciphertext = encoder.encode(ciphertext)
  270. return encoded_ciphertext
  271. def decrypt(self, ciphertext, encoder=encoding.RawEncoder):
  272. """
  273. Decrypts the ciphertext using the ephemeral public key enclosed
  274. in the ciphertext and the SealedBox private key, returning
  275. the plaintext message.
  276. :param ciphertext: [:class:`bytes`] The encrypted message to decrypt
  277. :param encoder: The encoder used to decode the ciphertext.
  278. :return bytes: The original plaintext
  279. """
  280. # Decode our ciphertext
  281. ciphertext = encoder.decode(ciphertext)
  282. plaintext = nacl.bindings.crypto_box_seal_open(
  283. ciphertext,
  284. self._public_key,
  285. self._private_key,
  286. )
  287. return plaintext

Powered by TurnKey Linux.