crypto_box.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. from nacl import exceptions as exc
  16. from nacl._sodium import ffi, lib
  17. from nacl.exceptions import ensure
  18. __all__ = ["crypto_box_keypair", "crypto_box"]
  19. crypto_box_SECRETKEYBYTES = lib.crypto_box_secretkeybytes()
  20. crypto_box_PUBLICKEYBYTES = lib.crypto_box_publickeybytes()
  21. crypto_box_NONCEBYTES = lib.crypto_box_noncebytes()
  22. crypto_box_ZEROBYTES = lib.crypto_box_zerobytes()
  23. crypto_box_BOXZEROBYTES = lib.crypto_box_boxzerobytes()
  24. crypto_box_BEFORENMBYTES = lib.crypto_box_beforenmbytes()
  25. def crypto_box_keypair():
  26. """
  27. Returns a randomly generated public and secret key.
  28. :rtype: (bytes(public_key), bytes(secret_key))
  29. """
  30. pk = ffi.new("unsigned char[]", crypto_box_PUBLICKEYBYTES)
  31. sk = ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES)
  32. rc = lib.crypto_box_keypair(pk, sk)
  33. ensure(rc == 0,
  34. 'Unexpected library error',
  35. raising=exc.RuntimeError)
  36. return (
  37. ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:],
  38. ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:],
  39. )
  40. def crypto_box(message, nonce, pk, sk):
  41. """
  42. Encrypts and returns a message ``message`` using the secret key ``sk``,
  43. public key ``pk``, and the nonce ``nonce``.
  44. :param message: bytes
  45. :param nonce: bytes
  46. :param pk: bytes
  47. :param sk: bytes
  48. :rtype: bytes
  49. """
  50. if len(nonce) != crypto_box_NONCEBYTES:
  51. raise exc.ValueError("Invalid nonce size")
  52. if len(pk) != crypto_box_PUBLICKEYBYTES:
  53. raise exc.ValueError("Invalid public key")
  54. if len(sk) != crypto_box_SECRETKEYBYTES:
  55. raise exc.ValueError("Invalid secret key")
  56. padded = (b"\x00" * crypto_box_ZEROBYTES) + message
  57. ciphertext = ffi.new("unsigned char[]", len(padded))
  58. rc = lib.crypto_box(ciphertext, padded, len(padded), nonce, pk, sk)
  59. ensure(rc == 0,
  60. 'Unexpected library error',
  61. raising=exc.RuntimeError)
  62. return ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
  63. def crypto_box_open(ciphertext, nonce, pk, sk):
  64. """
  65. Decrypts and returns an encrypted message ``ciphertext``, using the secret
  66. key ``sk``, public key ``pk``, and the nonce ``nonce``.
  67. :param ciphertext: bytes
  68. :param nonce: bytes
  69. :param pk: bytes
  70. :param sk: bytes
  71. :rtype: bytes
  72. """
  73. if len(nonce) != crypto_box_NONCEBYTES:
  74. raise exc.ValueError("Invalid nonce size")
  75. if len(pk) != crypto_box_PUBLICKEYBYTES:
  76. raise exc.ValueError("Invalid public key")
  77. if len(sk) != crypto_box_SECRETKEYBYTES:
  78. raise exc.ValueError("Invalid secret key")
  79. padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
  80. plaintext = ffi.new("unsigned char[]", len(padded))
  81. res = lib.crypto_box_open(plaintext, padded, len(padded), nonce, pk, sk)
  82. ensure(res == 0, "An error occurred trying to decrypt the message",
  83. raising=exc.CryptoError)
  84. return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
  85. def crypto_box_beforenm(pk, sk):
  86. """
  87. Computes and returns the shared key for the public key ``pk`` and the
  88. secret key ``sk``. This can be used to speed up operations where the same
  89. set of keys is going to be used multiple times.
  90. :param pk: bytes
  91. :param sk: bytes
  92. :rtype: bytes
  93. """
  94. if len(pk) != crypto_box_PUBLICKEYBYTES:
  95. raise exc.ValueError("Invalid public key")
  96. if len(sk) != crypto_box_SECRETKEYBYTES:
  97. raise exc.ValueError("Invalid secret key")
  98. k = ffi.new("unsigned char[]", crypto_box_BEFORENMBYTES)
  99. rc = lib.crypto_box_beforenm(k, pk, sk)
  100. ensure(rc == 0,
  101. 'Unexpected library error',
  102. raising=exc.RuntimeError)
  103. return ffi.buffer(k, crypto_box_BEFORENMBYTES)[:]
  104. def crypto_box_afternm(message, nonce, k):
  105. """
  106. Encrypts and returns the message ``message`` using the shared key ``k`` and
  107. the nonce ``nonce``.
  108. :param message: bytes
  109. :param nonce: bytes
  110. :param k: bytes
  111. :rtype: bytes
  112. """
  113. if len(nonce) != crypto_box_NONCEBYTES:
  114. raise exc.ValueError("Invalid nonce")
  115. if len(k) != crypto_box_BEFORENMBYTES:
  116. raise exc.ValueError("Invalid shared key")
  117. padded = b"\x00" * crypto_box_ZEROBYTES + message
  118. ciphertext = ffi.new("unsigned char[]", len(padded))
  119. rc = lib.crypto_box_afternm(ciphertext, padded, len(padded), nonce, k)
  120. ensure(rc == 0,
  121. 'Unexpected library error',
  122. raising=exc.RuntimeError)
  123. return ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
  124. def crypto_box_open_afternm(ciphertext, nonce, k):
  125. """
  126. Decrypts and returns the encrypted message ``ciphertext``, using the shared
  127. key ``k`` and the nonce ``nonce``.
  128. :param ciphertext: bytes
  129. :param nonce: bytes
  130. :param k: bytes
  131. :rtype: bytes
  132. """
  133. if len(nonce) != crypto_box_NONCEBYTES:
  134. raise exc.ValueError("Invalid nonce")
  135. if len(k) != crypto_box_BEFORENMBYTES:
  136. raise exc.ValueError("Invalid shared key")
  137. padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
  138. plaintext = ffi.new("unsigned char[]", len(padded))
  139. res = lib.crypto_box_open_afternm(
  140. plaintext, padded, len(padded), nonce, k)
  141. ensure(res == 0, "An error occurred trying to decrypt the message",
  142. raising=exc.CryptoError)
  143. return ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]