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

Port the crypto_box* functionality to use the new layout

parent 18b0fc62
No related branches found
No related tags found
No related merge requests found
......@@ -62,13 +62,13 @@ def which(name, flags=os.X_OK): # Taken from twisted
sys.path += glob.glob("*.egg")
try:
import nacl.nacl
import nacl._lib
except ImportError:
# installing - there is no cffi yet
ext_modules = []
else:
# building bdist - cffi is here!
ext_modules = [nacl.nacl.ffi.verifier.get_extension()]
ext_modules = [nacl._lib.ffi.verifier.get_extension()]
def use_system():
......@@ -210,6 +210,7 @@ setup(
package_dir={"": "src"},
packages=[
"nacl",
"nacl._lib",
"nacl.c",
],
......
......@@ -52,7 +52,7 @@ lib = ffi.verify(
libraries=["sodium"],
# Our ext_package is nacl so look for it
ext_package="nacl.c.lib",
ext_package="nacl.c._lib",
)
......
......@@ -15,10 +15,10 @@
size_t crypto_box_secretkeybytes();
size_t crypto_box_publickeybytes();
size_t crypto_box_zerobytes();
size_t crypto_box_boxzerobytes();
size_t crypto_box_noncebytes();
size_t crypto_box_beforenmbytes();
int crypto_box_keypair(unsigned char *pk, unsigned char *sk);
......@@ -30,3 +30,14 @@ int crypto_box(unsigned char *c, const unsigned char *m,
int crypto_box_open(unsigned char *m, const unsigned char *c,
unsigned long long clen, const unsigned char *n,
const unsigned char *pk, const unsigned char *sk);
int crypto_box_beforenm(unsigned char *k, const unsigned char *pk,
const unsigned char *sk);
int crypto_box_afternm(unsigned char *c, const unsigned char *m,
unsigned long long mlen, const unsigned char *n,
const unsigned char *k);
int crypto_box_open_afternm(unsigned char *m, const unsigned char *c,
unsigned long long clen, const unsigned char *n,
const unsigned char *k);
/* 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.
*/
size_t crypto_scalarmult_bytes();
size_t crypto_scalarmult_scalarbytes();
int crypto_scalarmult_base(unsigned char *q, const unsigned char *n);
......@@ -13,9 +13,33 @@
# limitations under the License.
from __future__ import absolute_import, division, print_function
from nacl.c.crypto_box import crypto_box_keypair
from nacl.c.crypto_box import (
crypto_box_SECRETKEYBYTES, crypto_box_PUBLICKEYBYTES,
crypto_box_NONCEBYTES, crypto_box_ZEROBYTES, crypto_box_BOXZEROBYTES,
crypto_box_BEFORENMBYTES, crypto_box_keypair, crypto_box, crypto_box_open,
crypto_box_beforenm, crypto_box_afternm, crypto_box_open_afternm,
)
from nacl.c.crypto_scalarmult import (
crypto_scalarmult_BYTES, crypto_scalarmult_SCALARBYTES,
crypto_scalarmult_base,
)
__all__ = [
"crypto_box_SECRETKEYBYTES",
"crypto_box_PUBLICKEYBYTES",
"crypto_box_NONCEBYTES",
"crypto_box_ZEROBYTES",
"crypto_box_BOXZEROBYTES",
"crypto_box_BEFORENMBYTES",
"crypto_box_keypair",
"crypto_box",
"crypto_box_open",
"crypto_box_beforenm",
"crypto_box_afternm",
"crypto_box_open_afternm",
"crypto_scalarmult_BYTES",
"crypto_scalarmult_SCALARBYTES",
"crypto_scalarmult_base",
]
# 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 __future__ import absolute_import
from __future__ import division
import functools
# We need to patch cffi before importing it
from nacl import _cffi_fix
import cffi.verifier
from cffi import FFI
__all__ = ["ffi", "lib"]
ffi = FFI()
ffi.cdef(
# Secret Key Encryption
"""
static const int crypto_secretbox_KEYBYTES;
static const int crypto_secretbox_NONCEBYTES;
static const int crypto_secretbox_ZEROBYTES;
static const int crypto_secretbox_BOXZEROBYTES;
int crypto_secretbox(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k);
int crypto_secretbox_open(unsigned char *m, const unsigned char *c, unsigned long long clen, const unsigned char *n, const unsigned char *k);
"""
# Public Key Encryption - Signatures
"""
static const int crypto_sign_PUBLICKEYBYTES;
static const int crypto_sign_SECRETKEYBYTES;
static const int crypto_sign_BYTES;
int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk, unsigned char *seed);
int crypto_sign(unsigned char *sm, unsigned long long *smlen, const unsigned char *m, unsigned long long mlen, const unsigned char *sk);
int crypto_sign_open(unsigned char *m, unsigned long long *mlen, const unsigned char *sm, unsigned long long smlen, const unsigned char *pk);
"""
# Public Key Encryption
"""
static const int crypto_box_PUBLICKEYBYTES;
static const int crypto_box_SECRETKEYBYTES;
static const int crypto_box_BEFORENMBYTES;
static const int crypto_box_NONCEBYTES;
static const int crypto_box_ZEROBYTES;
static const int crypto_box_BOXZEROBYTES;
int crypto_box_keypair(unsigned char *pk, unsigned char *sk);
int crypto_box_afternm(unsigned char *c, const unsigned char *m, unsigned long long mlen, const unsigned char *n, const unsigned char *k);
int crypto_box_open_afternm(unsigned char *m, const unsigned char *c, unsigned long long clen, const unsigned char *n, const unsigned char *k);
int crypto_box_beforenm(unsigned char *k, const unsigned char *pk, const unsigned char *sk);
"""
# Hashing
"""
static const int crypto_hash_BYTES;
static const int crypto_hash_sha256_BYTES;
static const int crypto_hash_sha512_BYTES;
int crypto_hash(unsigned char *out, const unsigned char *in, unsigned long long inlen);
int crypto_hash_sha256(unsigned char *out, const unsigned char *in, unsigned long long inlen);
int crypto_hash_sha512(unsigned char *out, const unsigned char *in, unsigned long long inlen);
"""
# Secure Random
"""
void randombytes(unsigned char * const buf, const unsigned long long buf_len);
"""
# Low Level - Scalar Multiplication
"""
int crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n);
"""
)
ffi.verifier = cffi.verifier.Verifier(ffi,
"#include <sodium.h>",
# We need to link to the sodium library
libraries=["sodium"],
# Our ext_package is nacl so look for it
ext_package="nacl",
)
# This works around a bug in PyPy where CFFI exposed functions do not have a
# __name__ attribute. See https://bugs.pypy.org/issue1452
def wraps(wrapped):
def inner(func):
if hasattr(wrapped, "__name__"):
return functools.wraps(wrapped)(func)
else:
return func
return inner
# A lot of the functions in nacl return 0 for success and a negative integer
# for failure. This is inconvenient in Python as 0 is a falsey value while
# negative integers are truthy. This wrapper has them return True/False as
# you'd expect in Python
def wrap_nacl_function(func):
@wraps(func)
def wrapper(*args, **kwargs):
ret = func(*args, **kwargs)
return ret == 0
return wrapper
class Library(object):
wrap = [
"crypto_secretbox",
"crypto_secretbox_open",
"crypto_sign_seed_keypair",
"crypto_sign",
"crypto_sign_open",
"crypto_box_keypair",
"crypto_box_afternm",
"crypto_box_open_afternm",
"crypto_box_beforenm",
"crypto_hash",
"crypto_hash_sha256",
"crypto_hash_sha512",
"crypto_scalarmult_curve25519_base",
]
def __init__(self, ffi):
self._ffi = ffi
self._initalized = False
# This prevents the compile_module() from being called, the module
# should have been compiled by setup.py
def _compile_module(*args, **kwargs):
raise RuntimeError("Cannot compile module during runtime")
self._ffi.verifier.compile_module = _compile_module
def __getattr__(self, name):
if not self._initalized:
self._lib = self._ffi.verifier.load_library()
# redirect attribute access to the underlying lib
attr = getattr(self._lib, name)
# If this is a function that we're wrapping do the actual wrapping
if name in self.wrap:
attr = wrap_nacl_function(attr)
# Go ahead and assign the returned value to this class so we don't
# need to do this lookup again
setattr(self, name, attr)
return attr
lib = Library(ffi)
......@@ -13,29 +13,37 @@
# limitations under the License.
from __future__ import absolute_import, division, print_function
from nacl.c import lib
from nacl import _lib as lib
from nacl.exceptions import CryptoError
__all__ = ["crypto_box_keypair", "crypto_box"]
crypto_box_SECRETKEYBYTES = lib.crypto_box_secretkeybytes()
crypto_box_PUBLICKEYBYTES = lib.crypto_box_publickeybytes()
crypto_box_NONCEBYTES = lib.crypto_box_noncebytes()
crypto_box_ZEROBYTES = lib.crypto_box_zerobytes()
crypto_box_BOXZEROBYTES = lib.crypto_box_boxzerobytes()
crypto_box_BEFORENMBYTES = lib.crypto_box_beforenmbytes()
def crypto_box_keypair():
"""
Returns a randomly generated secret and public key.
:rtype: (bytes(secret_key), bytes(public_key))
"""
sk_size = lib.crypto_box_secretkeybytes()
pk_size = lib.crypto_box_publickeybytes()
sk = lib.ffi.new("unsigned char[]", sk_size)
pk = lib.ffi.new("unsigned char[]", pk_size)
sk = lib.ffi.new("unsigned char[]", crypto_box_SECRETKEYBYTES)
pk = lib.ffi.new("unsigned char[]", crypto_box_PUBLICKEYBYTES)
if lib.crypto_box_keypair(pk, sk) != 0:
raise CryptoError("An error occurred trying to generate the keypair")
return (lib.ffi.buffer(sk, sk_size)[:], lib.ffi.buffer(pk, pk_size)[:])
return (
lib.ffi.buffer(sk, crypto_box_SECRETKEYBYTES)[:],
lib.ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:],
)
def crypto_box(sk, pk, message, nonce):
......@@ -49,28 +57,22 @@ def crypto_box(sk, pk, message, nonce):
:param nonce: bytes
:rtype: bytes
"""
sk_size = lib.crypto_box_secretkeybytes()
pk_size = lib.crypto_box_publickeybytes()
n_size = lib.crypto_box_noncebytes()
zero_bytes = lib.crypto_box_zerobytes()
box_zeros = lib.crypto_box_boxzerobytes()
if len(sk) != sk_size:
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
if len(pk) != pk_size:
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise ValueError("Invalid public key")
if len(nonce) != n_size:
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce size")
padded = (b"\x00" * zero_bytes) + message
padded = (b"\x00" * crypto_box_ZEROBYTES) + message
ciphertext = lib.ffi.new("unsigned char[]", len(padded))
if lib.crypto_box(ciphertext, message, len(message), nonce, pk, sk) != 0:
if lib.crypto_box(ciphertext, padded, len(padded), nonce, pk, sk) != 0:
raise CryptoError("An error occurred trying to encrypt the message")
return lib.ffi.buffer(ciphertext, len(padded))[box_zeros:]
return lib.ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
def crypto_box_open(sk, pk, ciphertext, nonce):
......@@ -84,26 +86,94 @@ def crypto_box_open(sk, pk, ciphertext, nonce):
:param nonce: bytes
:rtype: bytes
"""
sk_size = lib.crypto_box_secretkeybytes()
pk_size = lib.crypto_box_publickeybytes()
n_size = lib.crypto_box_noncebytes()
box_zeros = lib.crypto_box_boxzerobytes()
zero_bytes = lib.crypto_box_zerobytes()
if len(sk) != sk_size:
if len(sk) != crypto_box_SECRETKEYBYTES:
raise ValueError("Invalid secret key")
if len(pk) != pk_size:
if len(pk) != crypto_box_PUBLICKEYBYTES:
raise ValueError("Invalid public key")
if len(nonce) != n_size:
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce size")
padded = (b"\x00" * box_zeros) + ciphertext
padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
plaintext = lib.ffi.new("unsigned char[]", len(padded))
if lib.crypto_box_open(plaintext, padded, len(padded), nonce, pk, sk) != 0:
raise CryptoError("An error occurred trying to decrypt the message")
return lib.ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
def crypto_box_beforenm(sk, pk):
"""
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
set of keys is going to be used multiple times.
:param sk: bytes
:param pk: 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")
k = lib.ffi.new("unsigned char[]", crypto_box_BEFORENMBYTES)
if lib.crypto_box_beforenm(k, pk, sk) != 0:
raise CryptoError("An error occurred computing the shared key.")
return lib.ffi.buffer(k, crypto_box_BEFORENMBYTES)[:]
def crypto_box_afternm(k, message, nonce):
"""
Encrypts and returns the message ``message`` using the shared key ``k`` and
the nonce ``nonce``.
:param k: bytes
:param message: bytes
:param nonce: bytes
:rtype: bytes
"""
if len(k) != crypto_box_BEFORENMBYTES:
raise ValueError("Invalid shared key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce")
padded = b"\x00" * crypto_box_ZEROBYTES + message
ciphertext = lib.ffi.new("unsigned char[]", len(padded))
if lib.crypto_box_afternm(ciphertext, padded, len(padded), nonce, k) != 0:
raise CryptoError("An error occurred trying to encrypt the message")
return lib.ffi.buffer(ciphertext, len(padded))[crypto_box_BOXZEROBYTES:]
def crypto_box_open_afternm(k, ciphertext, nonce):
"""
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
:rtype: bytes
"""
if len(k) != crypto_box_BEFORENMBYTES:
raise ValueError("Invalid shared key")
if len(nonce) != crypto_box_NONCEBYTES:
raise ValueError("Invalid nonce")
padded = (b"\x00" * crypto_box_BOXZEROBYTES) + ciphertext
plaintext = lib.ffi.new("unsigned char[]", len(padded))
if lib.crypto_box_open(
plaintext, ciphertext, len(ciphertext), nonce, pk, sk):
if lib.crypto_box_open_afternm(
plaintext, padded, len(padded), nonce, k) != 0:
raise CryptoError("An error occurred trying to decrypt the message")
return lib.ffi.buffer(plaintext, len(padded))[zero_bytes:]
return lib.ffi.buffer(plaintext, len(padded))[crypto_box_ZEROBYTES:]
# 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 __future__ import absolute_import, division, print_function
from nacl import _lib as lib
from nacl.exceptions import CryptoError
crypto_scalarmult_BYTES = lib.crypto_scalarmult_bytes()
crypto_scalarmult_SCALARBYTES = lib.crypto_scalarmult_scalarbytes()
def crypto_scalarmult_base(n):
"""
Computes and returns the scalar product of a standard group element and an
integer ``n``.
:param n: bytes
:rtype: bytes
"""
q = lib.ffi.new("unsigned char[]", crypto_scalarmult_BYTES)
if lib.crypto_scalarmult_base(q, n) != 0:
raise CryptoError(
"An error occurred while computing the scalar product")
return lib.ffi.buffer(q, crypto_scalarmult_SCALARBYTES)[:]
......@@ -14,10 +14,11 @@
from __future__ import absolute_import
from __future__ import division
from . import encoding
from .c import _lib as nacl
from .exceptions import CryptoError
from .utils import EncryptedMessage, StringFixer, random
import nacl.c
import nacl.c.crypto_box
from nacl import encoding
from nacl.utils import EncryptedMessage, StringFixer, random
class PublicKey(encoding.Encodable, StringFixer, object):
......@@ -31,7 +32,7 @@ class PublicKey(encoding.Encodable, StringFixer, object):
:cvar SIZE: The size that the public key is required to be
"""
SIZE = nacl.lib.crypto_box_PUBLICKEYBYTES
SIZE = nacl.c.crypto_box_PUBLICKEYBYTES
def __init__(self, public_key, encoder=encoding.RawEncoder):
self._public_key = encoder.decode(public_key)
......@@ -59,7 +60,7 @@ class PrivateKey(encoding.Encodable, StringFixer, object):
:cvar SIZE: The size that the private key is required to be
"""
SIZE = nacl.lib.crypto_box_SECRETKEYBYTES
SIZE = nacl.c.crypto_box_SECRETKEYBYTES
def __init__(self, private_key, encoder=encoding.RawEncoder):
# Decode the secret_key
......@@ -70,15 +71,10 @@ class PrivateKey(encoding.Encodable, StringFixer, object):
raise ValueError(
"The secret key must be exactly %d bytes long" % self.SIZE)
pk = nacl.ffi.new("unsigned char[]", PublicKey.SIZE)
if not nacl.lib.crypto_scalarmult_curve25519_base(pk, private_key):
raise CryptoError("Failed to generate a key pair")
_pkey = nacl.ffi.buffer(pk, nacl.lib.crypto_box_PUBLICKEYBYTES)[:]
raw_public_key = nacl.c.crypto_scalarmult_base(private_key)
self._private_key = private_key
self.public_key = PublicKey(_pkey)
self.public_key = PublicKey(raw_public_key)
def __bytes__(self):
return self._private_key
......@@ -113,21 +109,14 @@ class Box(encoding.Encodable, StringFixer, object):
:cvar NONCE_SIZE: The size that the nonce is required to be.
"""
NONCE_SIZE = nacl.lib.crypto_box_NONCEBYTES
NONCE_SIZE = nacl.c.crypto_box_NONCEBYTES
def __init__(self, private_key, public_key):
if private_key and public_key:
_shared_key_size = nacl.lib.crypto_box_BEFORENMBYTES
_shared_key = nacl.ffi.new("unsigned char[]", _shared_key_size)
if not nacl.lib.crypto_box_beforenm(
_shared_key,
public_key.encode(encoder=encoding.RawEncoder),
self._shared_key = nacl.c.crypto_box_beforenm(
private_key.encode(encoder=encoding.RawEncoder),
):
raise CryptoError("Failed to derive shared key")
self._shared_key = nacl.ffi.buffer(_shared_key, _shared_key_size)[:]
public_key.encode(encoder=encoding.RawEncoder),
)
else:
self._shared_key = None
......@@ -162,20 +151,11 @@ class Box(encoding.Encodable, StringFixer, object):
raise ValueError("The nonce must be exactly %s bytes long" %
self.NONCE_SIZE)
padded = b"\x00" * nacl.lib.crypto_box_ZEROBYTES + plaintext
ciphertext = nacl.ffi.new("unsigned char[]", len(padded))
if not nacl.lib.crypto_box_afternm(
ciphertext,
padded,
len(padded),
nonce,
ciphertext = nacl.c.crypto_box_afternm(
self._shared_key,
):
raise CryptoError("Encryption failed")
box_zeros = nacl.lib.crypto_box_BOXZEROBYTES
ciphertext = nacl.ffi.buffer(ciphertext, len(padded))[box_zeros:]
plaintext,
nonce,
)
encoded_nonce = encoder.encode(nonce)
encoded_ciphertext = encoder.encode(ciphertext)
......@@ -209,20 +189,10 @@ class Box(encoding.Encodable, StringFixer, object):
raise ValueError("The nonce must be exactly %s bytes long" %
self.NONCE_SIZE)
padded = b"\x00" * nacl.lib.crypto_box_BOXZEROBYTES + ciphertext
plaintext = nacl.ffi.new("unsigned char[]", len(padded))
if not nacl.lib.crypto_box_open_afternm(
plaintext,
padded,
len(padded),
nonce,
plaintext = nacl.c.crypto_box_open_afternm(
self._shared_key,
):
raise CryptoError(
"Decryption failed. Ciphertext failed verification")
box_zeros = nacl.lib.crypto_box_ZEROBYTES
plaintext = nacl.ffi.buffer(plaintext, len(padded))[box_zeros:]
ciphertext,
nonce,
)
return plaintext
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment