Skip to content
Snippets Groups Projects
Commit 0263d521 authored by Donald Stufft's avatar Donald Stufft
Browse files

Merge pull request #66 from warner/fixraw

fix argument order of low-level NaCl functions
parents 65388715 45a0421e
No related branches found
No related tags found
No related merge requests found
......@@ -37,3 +37,14 @@ Features
* Secret-key encryption
* Public-key encryption
* HMAC (coming soon)
Changes
-------
* 0.3.0: the low-level API (`nacl.c.*`) has been changed to match the
upstream NaCl C/C++ conventions (as well as those of other NaCl bindings).
The order of arguments and return values has changed significantly. If you
have code which calls these functions (e.g. `nacl.c.crypto_box_keypair()`),
you must review the new docstrings and update your code to match the new
conventions.
......@@ -24,7 +24,7 @@ __summary__ = ("Python binding to the Networking and Cryptography (NaCl) "
"library")
__uri__ = "https://github.com/pyca/pynacl/"
__version__ = "0.2.3"
__version__ = "0.3.0"
__author__ = "Donald Stufft"
__email__ = "donald@stufft.io"
......
......@@ -30,41 +30,41 @@ crypto_box_BEFORENMBYTES = lib.crypto_box_beforenmbytes()
def crypto_box_keypair():
"""
Returns a randomly generated secret and public key.
Returns a randomly generated public and secret key.
:rtype: (bytes(secret_key), bytes(public_key))
:rtype: (bytes(public_key), bytes(secret_key))
"""
sk = lib.ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES)
pk = lib.ffi.new("unsigned char[]", crypto_box_PUBLICKEYBYTES)
sk = lib.ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES)
if lib.crypto_box_keypair(pk, sk) != 0:
raise CryptoError("An error occurred trying to generate the keypair")
return (
lib.ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:],
lib.ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:],
lib.ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:],
)
def crypto_box(sk, pk, message, nonce):
def crypto_box(message, nonce, pk, sk):
"""
Encrypts and returns a message ``message`` using the secret key ``sk``,
public key ``pk``, and the nonce ``nonce``.
:param sk: bytes
:param pk: bytes
:param message: bytes
:param nonce: bytes
:param pk: bytes
:param sk: bytes
:rtype: bytes
"""
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce size")
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise ValueError("Invalid public key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce size")
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
padded = (b"\x00" * crypto_box_ZEROBYTES) + message
ciphertext = lib.ffi.new("unsigned char[]", len(padded))
......@@ -75,25 +75,25 @@ def crypto_box(sk, pk, message, nonce):
return lib.ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
def crypto_box_open(sk, pk, ciphertext, nonce):
def crypto_box_open(ciphertext, nonce, pk, sk):
"""
Decrypts and returns an encrypted message ``ciphertext``, using the secret
key ``sk``, public key ``pk``, and the nonce ``nonce``.
:param sk: bytes
:param pk: bytes
:param ciphertext: bytes
:param nonce: bytes
:param pk: bytes
:param sk: bytes
:rtype: bytes
"""
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce size")
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise ValueError("Invalid public key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce size")
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
plaintext = lib.ffi.new("unsigned char[]", len(padded))
......@@ -104,22 +104,22 @@ def crypto_box_open(sk, pk, ciphertext, nonce):
return lib.ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
def crypto_box_beforenm(sk, pk):
def crypto_box_beforenm(pk, sk):
"""
Computes and returns the shared key for the secret key ``sk`` and the
public key ``pk``. This can be used to speed up operations where the same
Computes and returns the shared key for the public key ``pk`` and the
secret key ``sk``. This can be used to speed up operations where the same
set of keys is going to be used multiple times.
:param sk: bytes
:param pk: bytes
:param sk: bytes
:rtype: bytes
"""
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise ValueError("Invalid public key")
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
k = lib.ffi.new("unsigned char[]", crypto_box_BEFORENMBYTES)
if lib.crypto_box_beforenm(k, pk, sk) != 0:
......@@ -128,22 +128,22 @@ def crypto_box_beforenm(sk, pk):
return lib.ffi.buffer(k, crypto_box_BEFORENMBYTES)[:]
def crypto_box_afternm(k, message, nonce):
def crypto_box_afternm(message, nonce, k):
"""
Encrypts and returns the message ``message`` using the shared key ``k`` and
the nonce ``nonce``.
:param k: bytes
:param message: bytes
:param nonce: bytes
:param k: bytes
:rtype: bytes
"""
if len(k) != crypto_box_BEFORENMBYTES:
raise ValueError("Invalid shared key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce")
if len(k) != crypto_box_BEFORENMBYTES:
raise ValueError("Invalid shared key")
padded = b"\x00" * crypto_box_ZEROBYTES + message
ciphertext = lib.ffi.new("unsigned char[]", len(padded))
......@@ -153,22 +153,22 @@ def crypto_box_afternm(k, message, nonce):
return lib.ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
def crypto_box_open_afternm(k, ciphertext, nonce):
def crypto_box_open_afternm(ciphertext, nonce, k):
"""
Decrypts and returns the encrypted message ``ciphertext``, using the shared
key ``k`` and the nonce ``nonce``.
:param k: bytes
:param ciphertext: bytes
:param nonce: bytes
:param k: bytes
:rtype: bytes
"""
if len(k) != crypto_box_BEFORENMBYTES:
raise ValueError("Invalid shared key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce")
if len(k) != crypto_box_BEFORENMBYTES:
raise ValueError("Invalid shared key")
padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
plaintext = lib.ffi.new("unsigned char[]", len(padded))
......
......@@ -23,14 +23,14 @@ crypto_secretbox_ZEROBYTES = lib.crypto_secretbox_zerobytes()
crypto_secretbox_BOXZEROBYTES = lib.crypto_secretbox_boxzerobytes()
def crypto_secretbox(key, message, nonce):
def crypto_secretbox(message, nonce, key):
"""
Encrypts and returns the message ``message`` with the secret ``key`` and
the nonce ``nonce``.
:param key: bytes
:param message: bytes
:param nonce: bytes
:param key: bytes
:rtype: bytes
"""
if len(key) != crypto_secretbox_KEYBYTES:
......@@ -49,14 +49,14 @@ def crypto_secretbox(key, message, nonce):
return ciphertext[crypto_secretbox_BOXZEROBYTES:]
def crypto_secretbox_open(key, ciphertext, nonce):
def crypto_secretbox_open(ciphertext, nonce, key):
"""
Decrypt and returns the encrypted message ``ciphertext`` with the secret
``key`` and the nonce ``nonce``.
:param key: bytes
:param ciphertext: bytes
:param nonce: bytes
:param key: bytes
:rtype: bytes
"""
if len(key) != crypto_secretbox_KEYBYTES:
......
......@@ -26,51 +26,51 @@ crypto_sign_SECRETKEYBYTES = lib.crypto_sign_secretkeybytes()
def crypto_sign_keypair():
"""
Returns a randomly generated secret key and public key.
Returns a randomly generated public key and secret key.
:rtype: (bytes(secret_key), bytes(public_key))
:rtype: (bytes(public_key), bytes(secret_key))
"""
sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
pk = lib.ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
if lib.crypto_sign_keypair(pk, sk) != 0:
raise CryptoError("An error occurred while generating keypairs")
return (
lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
lib.ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
)
def crypto_sign_seed_keypair(seed):
"""
Computes and returns the secret key and public key using the seed ``seed``.
Computes and returns the public key and secret key using the seed ``seed``.
:param seed: bytes
:rtype: (bytes(secret_key), bytes(public_key))
:rtype: (bytes(public_key), bytes(secret_key))
"""
if len(seed) != crypto_sign_SEEDBYTES:
raise ValueError("Invalid seed")
sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
pk = lib.ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
if lib.crypto_sign_seed_keypair(pk, sk, seed) != 0:
raise CryptoError("An error occured while generating keypairs")
return (
lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
lib.ffi.buffer(pk, crypto_sign_PUBLICKEYBYTES)[:],
lib.ffi.buffer(sk, crypto_sign_SECRETKEYBYTES)[:],
)
def crypto_sign(sk, message):
def crypto_sign(message, sk):
"""
Signs the message ``message`` using the secret key ``sk`` and returns the
signed message.
:param sk: bytes
:param message: bytes
:param sk: bytes
:rtype: bytes
"""
signed = lib.ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES)
......@@ -82,13 +82,13 @@ def crypto_sign(sk, message):
return lib.ffi.buffer(signed, signed_len[0])[:]
def crypto_sign_open(pk, signed):
def crypto_sign_open(signed, pk):
"""
Verifies the signature of the signed message ``signed`` using the public
key ``pkg`` and returns the unsigned message.
:param pk: bytes
:param signed: bytes
:param pk: bytes
:rtype: bytes
"""
message = lib.ffi.new("unsigned char[]", len(signed))
......
......@@ -114,8 +114,8 @@ class Box(encoding.Encodable, StringFixer, object):
def __init__(self, private_key, public_key):
if private_key and public_key:
self._shared_key = nacl.c.crypto_box_beforenm(
private_key.encode(encoder=encoding.RawEncoder),
public_key.encode(encoder=encoding.RawEncoder),
private_key.encode(encoder=encoding.RawEncoder),
)
else:
self._shared_key = None
......@@ -152,9 +152,9 @@ class Box(encoding.Encodable, StringFixer, object):
self.NONCE_SIZE)
ciphertext = nacl.c.crypto_box_afternm(
self._shared_key,
plaintext,
nonce,
self._shared_key,
)
encoded_nonce = encoder.encode(nonce)
......@@ -190,9 +190,9 @@ class Box(encoding.Encodable, StringFixer, object):
self.NONCE_SIZE)
plaintext = nacl.c.crypto_box_open_afternm(
self._shared_key,
ciphertext,
nonce,
self._shared_key,
)
return plaintext
......@@ -78,7 +78,7 @@ class SecretBox(encoding.Encodable, StringFixer, object):
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE,
)
ciphertext = nacl.c.crypto_secretbox(self._key, plaintext, nonce)
ciphertext = nacl.c.crypto_secretbox(plaintext, nonce, self._key)
encoded_nonce = encoder.encode(nonce)
encoded_ciphertext = encoder.encode(ciphertext)
......@@ -113,6 +113,6 @@ class SecretBox(encoding.Encodable, StringFixer, object):
"The nonce must be exactly %s bytes long" % self.NONCE_SIZE,
)
plaintext = nacl.c.crypto_secretbox_open(self._key, ciphertext, nonce)
plaintext = nacl.c.crypto_secretbox_open(ciphertext, nonce, self._key)
return plaintext
......@@ -96,7 +96,7 @@ class VerifyKey(encoding.Encodable, StringFixer, object):
# Decode the signed message
smessage = encoder.decode(smessage)
return nacl.c.crypto_sign_open(self._key, smessage)
return nacl.c.crypto_sign_open(smessage, self._key)
class SigningKey(encoding.Encodable, StringFixer, object):
......@@ -129,7 +129,7 @@ class SigningKey(encoding.Encodable, StringFixer, object):
nacl.c.crypto_sign_SEEDBYTES
)
secret_key, public_key = nacl.c.crypto_sign_seed_keypair(seed)
public_key, secret_key = nacl.c.crypto_sign_seed_keypair(seed)
self._seed = seed
self._signing_key = secret_key
......@@ -158,7 +158,7 @@ class SigningKey(encoding.Encodable, StringFixer, object):
:param encoder: A class that is used to encode the signed message.
:rtype: :class:`~nacl.signing.SignedMessage`
"""
raw_signed = nacl.c.crypto_sign(self._signing_key, message)
raw_signed = nacl.c.crypto_sign(message, self._signing_key)
signature = encoder.encode(raw_signed[:nacl.c.crypto_sign_BYTES])
message = encoder.encode(raw_signed[nacl.c.crypto_sign_BYTES:])
......
# Copyright 2013 Donald Stufft and individual contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from binascii import hexlify
from nacl import c
import hashlib
def tohex(b):
return hexlify(b).decode("ascii")
def test_hash():
msg = b"message"
h1 = c.crypto_hash(msg)
assert len(h1) == c.crypto_hash_BYTES
assert tohex(h1) == ("f8daf57a3347cc4d6b9d575b31fe6077"
"e2cb487f60a96233c08cb479dbf31538"
"cc915ec6d48bdbaa96ddc1a16db4f4f9"
"6f37276cfcb3510b8246241770d5952c")
assert tohex(h1) == hashlib.sha512(msg).hexdigest()
h2 = c.crypto_hash_sha512(msg)
assert len(h2) == c.crypto_hash_sha512_BYTES
assert tohex(h2) == tohex(h1)
h3 = c.crypto_hash_sha256(msg)
assert len(h3) == c.crypto_hash_sha256_BYTES
assert tohex(h3) == ("ab530a13e45914982b79f9b7e3fba994"
"cfd1f3fb22f71cea1afbf02b460c6d1d")
assert tohex(h3) == hashlib.sha256(msg).hexdigest()
def test_secretbox():
key = b"\x00" * c.crypto_secretbox_KEYBYTES
msg = b"message"
nonce = b"\x01" * c.crypto_secretbox_NONCEBYTES
ct = c.crypto_secretbox(msg, nonce, key)
assert len(ct) == len(msg) + c.crypto_secretbox_BOXZEROBYTES
assert tohex(ct) == "3ae84dfb89728737bd6e2c8cacbaf8af3d34cc1666533a"
msg2 = c.crypto_secretbox_open(ct, nonce, key)
assert msg2 == msg
def test_box():
A_pubkey, A_secretkey = c.crypto_box_keypair()
assert len(A_secretkey) == c.crypto_box_SECRETKEYBYTES
assert len(A_pubkey) == c.crypto_box_PUBLICKEYBYTES
B_pubkey, B_secretkey = c.crypto_box_keypair()
k1 = c.crypto_box_beforenm(B_pubkey, A_secretkey)
assert len(k1) == c.crypto_box_BEFORENMBYTES
k2 = c.crypto_box_beforenm(A_pubkey, B_secretkey)
assert tohex(k1) == tohex(k2)
message = b"message"
nonce = b"\x01" * c.crypto_box_NONCEBYTES
ct1 = c.crypto_box_afternm(message, nonce, k1)
assert len(ct1) == len(message) + c.crypto_box_BOXZEROBYTES
ct2 = c.crypto_box(message, nonce, B_pubkey, A_secretkey)
assert tohex(ct2) == tohex(ct1)
m1 = c.crypto_box_open(ct1, nonce, A_pubkey, B_secretkey)
assert m1 == message
m2 = c.crypto_box_open_afternm(ct1, nonce, k1)
assert m2 == message
def test_sign():
seed = b"\x00" * c.crypto_sign_SEEDBYTES
pubkey, secretkey = c.crypto_sign_seed_keypair(seed)
assert len(pubkey) == c.crypto_sign_PUBLICKEYBYTES
assert len(secretkey) == c.crypto_sign_SECRETKEYBYTES
pubkey, secretkey = c.crypto_sign_keypair()
assert len(pubkey) == c.crypto_sign_PUBLICKEYBYTES
assert len(secretkey) == c.crypto_sign_SECRETKEYBYTES
msg = b"message"
sigmsg = c.crypto_sign(msg, secretkey)
assert len(sigmsg) == len(msg) + c.crypto_sign_BYTES
msg2 = c.crypto_sign_open(sigmsg, pubkey)
assert msg2 == msg
def secret_scalar():
pubkey, secretkey = c.crypto_box_keypair()
assert len(secretkey) == c.crypto_box_SECRETKEYBYTES
assert c.crypto_box_SECRETKEYBYTES == c.crypto_scalarmult_BYTES
return secretkey, pubkey
def test_scalarmult():
x, xpub = secret_scalar()
assert len(x) == 32
y, ypub = secret_scalar()
bx = c.crypto_scalarmult_base(x)
assert tohex(bx) == tohex(xpub)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment