diff --git a/MANIFEST.in b/MANIFEST.in
index f7c92c539524cb53c6c9a23880df80a714efd8a8..c3b81f6a29783a888e2f0907d238322413ff24ea 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -26,3 +26,6 @@ recursive-include tests/data *
 
 # Remove CFFI files
 global-exclude __pycache__/*
+
+# Add PyNaCl header files
+recursive-include src/nacl/_lib *.h
diff --git a/setup.py b/setup.py
index 7aa269b87ed76e1f9dc51f17515271e217ec95c2..336b1e42b2ee6ae1ce4980f8a315c2ee483c2299 100644
--- a/setup.py
+++ b/setup.py
@@ -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,9 +210,12 @@ setup(
     package_dir={"": "src"},
     packages=[
         "nacl",
+        "nacl._lib",
+        "nacl.c",
     ],
+    package_data={"nacl._lib": ["*.h"]},
 
-    ext_package="nacl",
+    ext_package="nacl._lib",
     ext_modules=ext_modules,
 
     cmdclass={
diff --git a/src/nacl/_lib/__init__.py b/src/nacl/_lib/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4bc8a2141b2b613ab9197fc5ae0ef6bc2263e7d6
--- /dev/null
+++ b/src/nacl/_lib/__init__.py
@@ -0,0 +1,84 @@
+# 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
+
+import glob
+import os.path
+
+import six
+
+# We need to import this prior to importing cffi to fix prebuilding the
+#   extension modules
+from nacl import _cffi_fix
+
+from cffi import FFI
+from cffi.verifier import Verifier
+
+
+__all__ = ["ffi"]
+
+
+HEADERS = glob.glob(
+    os.path.join(os.path.abspath(os.path.dirname(__file__)), "*.h")
+)
+
+
+# Build our FFI instance
+ffi = FFI()
+
+
+# Add all of our header files
+for header in HEADERS:
+    with open(header, "r") as hfile:
+        ffi.cdef(hfile.read())
+
+
+# TODO: Can we use the ABI of libsodium for this instead?
+ffi.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._lib",
+)
+
+
+class Library(object):
+
+    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)
+
+        # 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)
diff --git a/src/nacl/_lib/crypto_box.h b/src/nacl/_lib/crypto_box.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7d59f76386f5e6fda65332418d35e9f80ff6f21
--- /dev/null
+++ b/src/nacl/_lib/crypto_box.h
@@ -0,0 +1,43 @@
+/* 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_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);
+
+int crypto_box(unsigned char *c,        const unsigned char *m,
+               unsigned long long mlen, const unsigned char *n,
+         const unsigned char *pk,       const unsigned char *sk);
+
+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);
diff --git a/src/nacl/_lib/crypto_hash.h b/src/nacl/_lib/crypto_hash.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ca6473799a203325f3a1a47c48cf79c13ace25d
--- /dev/null
+++ b/src/nacl/_lib/crypto_hash.h
@@ -0,0 +1,29 @@
+/* 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_hash_bytes();
+size_t crypto_hash_sha256_bytes();
+size_t 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);
diff --git a/src/nacl/_lib/crypto_scalarmult.h b/src/nacl/_lib/crypto_scalarmult.h
new file mode 100644
index 0000000000000000000000000000000000000000..dee5e84928e2f2271df849d898f5a2d4bc4ab138
--- /dev/null
+++ b/src/nacl/_lib/crypto_scalarmult.h
@@ -0,0 +1,19 @@
+/* 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);
diff --git a/src/nacl/_lib/crypto_secretbox.h b/src/nacl/_lib/crypto_secretbox.h
new file mode 100644
index 0000000000000000000000000000000000000000..88da2a3563250bc185b1c955bf8805dc73d8cae7
--- /dev/null
+++ b/src/nacl/_lib/crypto_secretbox.h
@@ -0,0 +1,28 @@
+/* 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_secretbox_keybytes();
+size_t crypto_secretbox_noncebytes();
+size_t crypto_secretbox_zerobytes();
+size_t 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);
diff --git a/src/nacl/_lib/crypto_sign.h b/src/nacl/_lib/crypto_sign.h
new file mode 100644
index 0000000000000000000000000000000000000000..e401567981b434bd6d112b3f34fb7c16274c2c81
--- /dev/null
+++ b/src/nacl/_lib/crypto_sign.h
@@ -0,0 +1,33 @@
+/* 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_sign_bytes();
+// size_t crypto_sign_seedbytes();
+size_t crypto_sign_publickeybytes();
+size_t crypto_sign_secretkeybytes();
+
+
+int crypto_sign_keypair(unsigned char *pk, unsigned char *sk);
+
+int crypto_sign_seed_keypair(unsigned char *pk, unsigned char *sk,
+                       const 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);
diff --git a/src/nacl/_lib/randombytes.h b/src/nacl/_lib/randombytes.h
new file mode 100644
index 0000000000000000000000000000000000000000..6952fbbe803173407a7633d0958ef3bae365b262
--- /dev/null
+++ b/src/nacl/_lib/randombytes.h
@@ -0,0 +1,16 @@
+/* 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.
+ */
+
+void randombytes(unsigned char * const buf, const unsigned long long buf_len);
diff --git a/src/nacl/c/__init__.py b/src/nacl/c/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..48b369a40ed57d6dcdc27b31d19be77d17987c71
--- /dev/null
+++ b/src/nacl/c/__init__.py
@@ -0,0 +1,85 @@
+# 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.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_hash import (
+    crypto_hash_BYTES, crypto_hash_sha256_BYTES, crypto_hash_sha512_BYTES,
+    crypto_hash, crypto_hash_sha256, crypto_hash_sha512,
+)
+from nacl.c.crypto_scalarmult import (
+    crypto_scalarmult_BYTES, crypto_scalarmult_SCALARBYTES,
+    crypto_scalarmult_base,
+)
+from nacl.c.crypto_secretbox import (
+    crypto_secretbox_KEYBYTES, crypto_secretbox_NONCEBYTES,
+    crypto_secretbox_ZEROBYTES, crypto_secretbox_BOXZEROBYTES,
+    crypto_secretbox, crypto_secretbox_open,
+)
+from nacl.c.crypto_sign import (
+    crypto_sign_BYTES, crypto_sign_SEEDBYTES, crypto_sign_PUBLICKEYBYTES,
+    crypto_sign_SECRETKEYBYTES, crypto_sign_keypair, crypto_sign_seed_keypair,
+    crypto_sign, crypto_sign_open,
+)
+from nacl.c.randombytes import randombytes
+
+
+__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_hash_BYTES",
+    "crypto_hash_sha256_BYTES",
+    "crypto_hash_sha512_BYTES",
+    "crypto_hash",
+    "crypto_hash_sha256",
+    "crypto_hash_sha512",
+
+    "crypto_scalarmult_BYTES",
+    "crypto_scalarmult_SCALARBYTES",
+    "crypto_scalarmult_base",
+
+    "crypto_secretbox_KEYBYTES",
+    "crypto_secretbox_NONCEBYTES",
+    "crypto_secretbox_ZEROBYTES",
+    "crypto_secretbox_BOXZEROBYTES",
+    "crypto_secretbox",
+    "crypto_secretbox_open",
+
+    "crypto_sign_BYTES",
+    "crypto_sign_SEEDBYTES",
+    "crypto_sign_PUBLICKEYBYTES",
+    "crypto_sign_SECRETKEYBYTES",
+    "crypto_sign_keypair",
+    "crypto_sign_seed_keypair",
+    "crypto_sign",
+    "crypto_sign_open",
+
+    "randombytes",
+]
diff --git a/src/nacl/c/crypto_box.py b/src/nacl/c/crypto_box.py
new file mode 100644
index 0000000000000000000000000000000000000000..069ff5c0a3cda089c3bc3ec1041bdce729d39296
--- /dev/null
+++ b/src/nacl/c/crypto_box.py
@@ -0,0 +1,179 @@
+# 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._lib import 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 = 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, crypto_box_SECRETKEYBYTES)[:],
+        lib.ffi.buffer(pk, crypto_box_PUBLICKEYBYTES)[:],
+    )
+
+
+def crypto_box(sk, pk, message, nonce):
+    """
+    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
+    :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(nonce) != crypto_box_NONCEBYTES:
+        raise ValueError("Invalid nonce size")
+
+    padded = (b"\x00" * crypto_box_ZEROBYTES) + message
+    ciphertext = lib.ffi.new("unsigned char[]", len(padded))
+
+    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))[crypto_box_BOXZEROBYTES:]
+
+
+def crypto_box_open(sk, pk, ciphertext, nonce):
+    """
+    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
+    :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(nonce) != crypto_box_NONCEBYTES:
+        raise ValueError("Invalid nonce size")
+
+    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_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))[crypto_box_ZEROBYTES:]
diff --git a/src/nacl/c/crypto_hash.py b/src/nacl/c/crypto_hash.py
new file mode 100644
index 0000000000000000000000000000000000000000..de6c3be5025c5c71f830d82f735f484edd5d7dfc
--- /dev/null
+++ b/src/nacl/c/crypto_hash.py
@@ -0,0 +1,62 @@
+# 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._lib import lib
+from nacl.exceptions import CryptoError
+
+
+# crypto_hash_BYTES = lib.crypto_hash_bytes()
+crypto_hash_BYTES = lib.crypto_hash_sha512_bytes()
+crypto_hash_sha256_BYTES = lib.crypto_hash_sha256_bytes()
+crypto_hash_sha512_BYTES = lib.crypto_hash_sha512_bytes()
+
+
+def crypto_hash(message):
+    """
+    Hashes and returns the message ``message``.
+
+    :param message: bytes
+    :rtype: bytes
+    """
+    digest = lib.ffi.new("unsigned char[]", crypto_hash_BYTES)
+    if lib.crypto_hash(digest, message, len(message)) != 0:
+        raise CryptoError("Hashing failed")
+    return lib.ffi.buffer(digest, crypto_hash_BYTES)[:]
+
+
+def crypto_hash_sha256(message):
+    """
+    Hashes and returns the message ``message``.
+
+    :param message: bytes
+    :rtype: bytes
+    """
+    digest = lib.ffi.new("unsigned char[]", crypto_hash_sha256_BYTES)
+    if lib.crypto_hash_sha256(digest, message, len(message)) != 0:
+        raise CryptoError("Hashing failed")
+    return lib.ffi.buffer(digest, crypto_hash_sha256_BYTES)[:]
+
+
+def crypto_hash_sha512(message):
+    """
+    Hashes and returns the message ``message``.
+
+    :param message: bytes
+    :rtype: bytes
+    """
+    digest = lib.ffi.new("unsigned char[]", crypto_hash_sha512_BYTES)
+    if lib.crypto_hash_sha512(digest, message, len(message)) != 0:
+        raise CryptoError("Hashing failed")
+    return lib.ffi.buffer(digest, crypto_hash_sha512_BYTES)[:]
diff --git a/src/nacl/c/crypto_scalarmult.py b/src/nacl/c/crypto_scalarmult.py
new file mode 100644
index 0000000000000000000000000000000000000000..b94841e49879f3ff19e2b9d2eccca20bc48227bc
--- /dev/null
+++ b/src/nacl/c/crypto_scalarmult.py
@@ -0,0 +1,38 @@
+# 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._lib import 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)[:]
diff --git a/src/nacl/c/crypto_secretbox.py b/src/nacl/c/crypto_secretbox.py
new file mode 100644
index 0000000000000000000000000000000000000000..94e6a66a35c2c0e78986571f4f070c4417de91af
--- /dev/null
+++ b/src/nacl/c/crypto_secretbox.py
@@ -0,0 +1,76 @@
+# 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._lib import lib
+from nacl.exceptions import CryptoError
+
+
+crypto_secretbox_KEYBYTES = lib.crypto_secretbox_keybytes()
+crypto_secretbox_NONCEBYTES = lib.crypto_secretbox_noncebytes()
+crypto_secretbox_ZEROBYTES = lib.crypto_secretbox_zerobytes()
+crypto_secretbox_BOXZEROBYTES = lib.crypto_secretbox_boxzerobytes()
+
+
+def crypto_secretbox(key, message, nonce):
+    """
+    Encrypts and returns the message ``message`` with the secret ``key`` and
+    the nonce ``nonce``.
+
+    :param key: bytes
+    :param message: bytes
+    :param nonce: bytes
+    :rtype: bytes
+    """
+    if len(key) != crypto_secretbox_KEYBYTES:
+        raise ValueError("Invalid key")
+
+    if len(nonce) != crypto_secretbox_NONCEBYTES:
+        raise ValueError("Invalid nonce")
+
+    padded = b"\x00" * crypto_secretbox_ZEROBYTES + message
+    ciphertext = lib.ffi.new("unsigned char[]", len(padded))
+
+    if lib.crypto_secretbox(ciphertext, padded, len(padded), nonce, key) != 0:
+        raise CryptoError("Encryption failed")
+
+    ciphertext = lib.ffi.buffer(ciphertext, len(padded))
+    return ciphertext[crypto_secretbox_BOXZEROBYTES:]
+
+
+def crypto_secretbox_open(key, ciphertext, nonce):
+    """
+    Decrypt and returns the encrypted message ``ciphertext`` with the secret
+    ``key`` and the nonce ``nonce``.
+
+    :param key: bytes
+    :param ciphertext: bytes
+    :param nonce: bytes
+    :rtype: bytes
+    """
+    if len(key) != crypto_secretbox_KEYBYTES:
+        raise ValueError("Invalid key")
+
+    if len(nonce) != crypto_secretbox_NONCEBYTES:
+        raise ValueError("Invalid nonce")
+
+    padded = b"\x00" * crypto_secretbox_BOXZEROBYTES + ciphertext
+    plaintext = lib.ffi.new("unsigned char[]", len(padded))
+
+    if lib.crypto_secretbox_open(
+            plaintext, padded, len(padded), nonce, key) != 0:
+        raise CryptoError("Decryption failed. Ciphertext failed verification")
+
+    plaintext = lib.ffi.buffer(plaintext, len(padded))
+    return plaintext[crypto_secretbox_ZEROBYTES:]
diff --git a/src/nacl/c/crypto_sign.py b/src/nacl/c/crypto_sign.py
new file mode 100644
index 0000000000000000000000000000000000000000..7286de062889e0743feecb5e4c730af91df49397
--- /dev/null
+++ b/src/nacl/c/crypto_sign.py
@@ -0,0 +1,101 @@
+# 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._lib import lib
+from nacl.exceptions import BadSignatureError, CryptoError
+
+
+crypto_sign_BYTES = lib.crypto_sign_bytes()
+# crypto_sign_SEEDBYTES = lib.crypto_sign_seedbytes()
+crypto_sign_SEEDBYTES = lib.crypto_sign_secretkeybytes() // 2
+crypto_sign_PUBLICKEYBYTES = lib.crypto_sign_publickeybytes()
+crypto_sign_SECRETKEYBYTES = lib.crypto_sign_secretkeybytes()
+
+
+def crypto_sign_keypair():
+    """
+    Returns a randomly generated secret key and public key.
+
+    :rtype: (bytes(secret_key), bytes(public_key))
+    """
+    sk = lib.ffi.new("unsigned char[]", crypto_sign_SECRETKEYBYTES)
+    pk = lib.ffi.new("unsigned char[]", crypto_sign_PUBLICKEYBYTES)
+
+    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)[:],
+    )
+
+
+def crypto_sign_seed_keypair(seed):
+    """
+    Computes and returns the secret key and public key using the seed ``seed``.
+
+    :param seed: bytes
+    :rtype: (bytes(secret_key), bytes(public_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)
+
+    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)[:],
+    )
+
+
+def crypto_sign(sk, message):
+    """
+    Signs the message ``message`` using the secret key ``sk`` and returns the
+    signed message.
+
+    :param sk: bytes
+    :param message: bytes
+    :rtype: bytes
+    """
+    signed = lib.ffi.new("unsigned char[]", len(message) + crypto_sign_BYTES)
+    signed_len = lib.ffi.new("unsigned long long *")
+
+    if lib.crypto_sign(signed, signed_len, message, len(message), sk) != 0:
+        raise CryptoError("Failed to sign the message")
+
+    return lib.ffi.buffer(signed, signed_len[0])[:]
+
+
+def crypto_sign_open(pk, signed):
+    """
+    Verifies the signature of the signed message ``signed`` using the public
+    key ``pkg`` and returns the unsigned message.
+
+    :param pk: bytes
+    :param signed: bytes
+    :rtype: bytes
+    """
+    message = lib.ffi.new("unsigned char[]", len(signed))
+    message_len = lib.ffi.new("unsigned long long *")
+
+    if lib.crypto_sign_open(
+            message, message_len, signed, len(signed), pk) != 0:
+        raise BadSignatureError("Signature was forged or corrupt")
+
+    return lib.ffi.buffer(message, message_len[0])[:]
diff --git a/src/nacl/c/randombytes.py b/src/nacl/c/randombytes.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6f01f5157bf62c1a96db27e4e4d30ca945f2e98
--- /dev/null
+++ b/src/nacl/c/randombytes.py
@@ -0,0 +1,29 @@
+# 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._lib import lib
+
+
+def randombytes(size):
+    """
+    Returns ``size`` number of random bytes from a cryptographically secure
+    random source.
+
+    :param size: int
+    :rtype: bytes
+    """
+    buf = lib.ffi.new("unsigned char[]", size)
+    lib.randombytes(buf, size)
+    return lib.ffi.buffer(buf, size)[:]
diff --git a/src/nacl/exceptions.py b/src/nacl/exceptions.py
index a50656b63e47faff20d4ed3d602b54a492f4ceca..ae8630f5ebb3845921a34def820b7afc642c7b5d 100644
--- a/src/nacl/exceptions.py
+++ b/src/nacl/exceptions.py
@@ -19,3 +19,9 @@ class CryptoError(Exception):
     """
     Base exception for all nacl related errors
     """
+
+
+class BadSignatureError(CryptoError):
+    """
+    Raised when the signature was forged or otherwise corrupt.
+    """
diff --git a/src/nacl/hash.py b/src/nacl/hash.py
index a6f7638111d10dd39eb9b90e696de56925f1103d..61f2f884de9601955a173b8142fc788a9351e962 100644
--- a/src/nacl/hash.py
+++ b/src/nacl/hash.py
@@ -14,23 +14,13 @@
 from __future__ import absolute_import
 from __future__ import division
 
-from . import nacl, encoding
-from .exceptions import CryptoError
+import nacl.c
+import nacl.encoding
 
 
-def sha256(message, encoder=encoding.HexEncoder):
-    digest = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_hash_sha256_BYTES)
-    if not nacl.lib.crypto_hash_sha256(digest, message, len(message)):
-        raise CryptoError("Hashing failed")
-    digest = nacl.ffi.buffer(digest, nacl.lib.crypto_hash_sha256_BYTES)[:]
+def sha256(message, encoder=nacl.encoding.HexEncoder):
+    return encoder.encode(nacl.c.crypto_hash_sha256(message))
 
-    return encoder.encode(digest)
 
-
-def sha512(message, encoder=encoding.HexEncoder):
-    digest = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_hash_sha512_BYTES)
-    if not nacl.lib.crypto_hash_sha512(digest, message, len(message)):
-        raise CryptoError("Hashing failed")
-    digest = nacl.ffi.buffer(digest, nacl.lib.crypto_hash_sha512_BYTES)[:]
-
-    return encoder.encode(digest)
+def sha512(message, encoder=nacl.encoding.HexEncoder):
+    return encoder.encode(nacl.c.crypto_hash_sha512(message))
diff --git a/src/nacl/nacl.py b/src/nacl/nacl.py
deleted file mode 100644
index 35ee3010c095b2669e3b59fbbb4d2bccbe1b9518..0000000000000000000000000000000000000000
--- a/src/nacl/nacl.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# 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.
-"""
-CFFI interface to NaCl and libsodium library
-"""
-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)
diff --git a/src/nacl/public.py b/src/nacl/public.py
index b9604b1088673e76f141c2752c9b396ba3d88a2a..eb083116abbd32de1fd3440e52dc0807bd90ca2a 100644
--- a/src/nacl/public.py
+++ b/src/nacl/public.py
@@ -14,9 +14,11 @@
 from __future__ import absolute_import
 from __future__ import division
 
-from . import nacl, encoding
-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):
@@ -30,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)
@@ -58,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
@@ -69,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
@@ -112,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),
-                        private_key.encode(encoder=encoding.RawEncoder),
-                    ):
-                raise CryptoError("Failed to derive shared key")
-
-            self._shared_key = nacl.ffi.buffer(_shared_key, _shared_key_size)[:]
+            self._shared_key = nacl.c.crypto_box_beforenm(
+                private_key.encode(encoder=encoding.RawEncoder),
+                public_key.encode(encoder=encoding.RawEncoder),
+            )
         else:
             self._shared_key = None
 
@@ -161,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,
-                    self._shared_key,
-                ):
-            raise CryptoError("Encryption failed")
-
-        box_zeros = nacl.lib.crypto_box_BOXZEROBYTES
-        ciphertext = nacl.ffi.buffer(ciphertext, len(padded))[box_zeros:]
+        ciphertext = nacl.c.crypto_box_afternm(
+            self._shared_key,
+            plaintext,
+            nonce,
+        )
 
         encoded_nonce = encoder.encode(nonce)
         encoded_ciphertext = encoder.encode(ciphertext)
@@ -208,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,
-                    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:]
+        plaintext = nacl.c.crypto_box_open_afternm(
+            self._shared_key,
+            ciphertext,
+            nonce,
+        )
 
         return plaintext
diff --git a/src/nacl/secret.py b/src/nacl/secret.py
index e0b4e113aaff24835a1e131b559bf0809b0302d8..31701f44fd4573b3f3ba984008b3baefa940483e 100644
--- a/src/nacl/secret.py
+++ b/src/nacl/secret.py
@@ -14,8 +14,9 @@
 from __future__ import absolute_import
 from __future__ import division
 
-from . import nacl, encoding
-from .exceptions import CryptoError
+import nacl.c
+
+from . import encoding
 from .utils import EncryptedMessage, StringFixer
 
 
@@ -39,8 +40,8 @@ class SecretBox(encoding.Encodable, StringFixer, object):
     :cvar NONCE_SIZE: The size that the nonce is required to be.
     """
 
-    KEY_SIZE = nacl.lib.crypto_secretbox_KEYBYTES
-    NONCE_SIZE = nacl.lib.crypto_secretbox_NONCEBYTES
+    KEY_SIZE = nacl.c.crypto_secretbox_KEYBYTES
+    NONCE_SIZE = nacl.c.crypto_secretbox_NONCEBYTES
 
     def __init__(self, key, encoder=encoding.RawEncoder):
         key = encoder.decode(key)
@@ -72,18 +73,9 @@ class SecretBox(encoding.Encodable, StringFixer, object):
         """
         if len(nonce) != self.NONCE_SIZE:
             raise ValueError("The nonce must be exactly %s bytes long" %
-                                nacl.lib.crypto_secretbox_NONCEBYTES)
-
-        padded = b"\x00" * nacl.lib.crypto_secretbox_ZEROBYTES + plaintext
-        ciphertext = nacl.ffi.new("unsigned char[]", len(padded))
-
-        if not nacl.lib.crypto_secretbox(
-                    ciphertext, padded, len(padded), nonce, self._key,
-                ):
-            raise CryptoError("Encryption failed")
+                                self.NONCE_SIZE)
 
-        box_zeros = nacl.lib.crypto_secretbox_BOXZEROBYTES
-        ciphertext = nacl.ffi.buffer(ciphertext, len(padded))[box_zeros:]
+        ciphertext = nacl.c.crypto_secretbox(self._key, plaintext, nonce)
 
         encoded_nonce = encoder.encode(nonce)
         encoded_ciphertext = encoder.encode(ciphertext)
@@ -115,18 +107,8 @@ class SecretBox(encoding.Encodable, StringFixer, object):
 
         if len(nonce) != self.NONCE_SIZE:
             raise ValueError("The nonce must be exactly %s bytes long" %
-                                nacl.lib.crypto_secretbox_NONCEBYTES)
-
-        padded = b"\x00" * nacl.lib.crypto_secretbox_BOXZEROBYTES + ciphertext
-        plaintext = nacl.ffi.new("unsigned char[]", len(padded))
-
-        if not nacl.lib.crypto_secretbox_open(
-                    plaintext, padded, len(padded), nonce, self._key,
-                ):
-            raise CryptoError(
-                        "Decryption failed. Ciphertext failed verification")
+                                self.NONCE_SIZE)
 
-        box_zeros = nacl.lib.crypto_secretbox_ZEROBYTES
-        plaintext = nacl.ffi.buffer(plaintext, len(padded))[box_zeros:]
+        plaintext = nacl.c.crypto_secretbox_open(self._key, ciphertext, nonce)
 
         return plaintext
diff --git a/src/nacl/signing.py b/src/nacl/signing.py
index 8ee774160f41588a073534349ede6ec0fe2f238b..036b4d76466a55c1ce1ea0fadec40faa5ccf44ac 100644
--- a/src/nacl/signing.py
+++ b/src/nacl/signing.py
@@ -16,15 +16,10 @@ from __future__ import division
 
 import six
 
-from . import nacl, encoding
-from .exceptions import CryptoError
-from .utils import StringFixer, random
-
+import nacl.c
 
-class BadSignatureError(CryptoError):
-    """
-    Raised when the signature was forged or otherwise corrupt.
-    """
+from . import encoding
+from .utils import StringFixer, random
 
 
 class SignedMessage(six.binary_type):
@@ -68,9 +63,9 @@ class VerifyKey(encoding.Encodable, StringFixer, object):
         # Decode the key
         key = encoder.decode(key)
 
-        if len(key) != nacl.lib.crypto_sign_PUBLICKEYBYTES:
+        if len(key) != nacl.c.crypto_sign_PUBLICKEYBYTES:
             raise ValueError("The key must be exactly %s bytes long" %
-                                nacl.lib.crypto_sign_PUBLICKEYBYTES)
+                                nacl.c.crypto_sign_PUBLICKEYBYTES)
 
         self._key = key
 
@@ -99,13 +94,7 @@ class VerifyKey(encoding.Encodable, StringFixer, object):
         # Decode the signed message
         smessage = encoder.decode(smessage)
 
-        message = nacl.ffi.new("unsigned char[]", len(smessage))
-        message_len = nacl.ffi.new("unsigned long long *")
-
-        if not nacl.lib.crypto_sign_open(message, message_len, smessage, len(smessage), self._key):
-            raise BadSignatureError("Signature was forged or corrupt")
-
-        return nacl.ffi.buffer(message, message_len[0])[:]
+        return nacl.c.crypto_sign_open(self._key, smessage)
 
 
 class SigningKey(encoding.Encodable, StringFixer, object):
@@ -132,23 +121,15 @@ class SigningKey(encoding.Encodable, StringFixer, object):
         seed = encoder.decode(seed)
 
         # Verify that our seed is the proper size
-        seed_size = nacl.lib.crypto_sign_SECRETKEYBYTES // 2
-        if len(seed) != seed_size:
-            raise ValueError(
-                'The seed must be exactly %d bytes long' % (seed_size,))
-
-        pk = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_sign_PUBLICKEYBYTES)
-        sk = nacl.ffi.new("unsigned char[]", nacl.lib.crypto_sign_SECRETKEYBYTES)
+        if len(seed) != nacl.c.crypto_sign_SEEDBYTES:
+            raise ValueError("The seed must be exactly %d bytes long" %
+                                nacl.c.crypto_sign_SEEDBYTES)
 
-        if not nacl.lib.crypto_sign_seed_keypair(pk, sk, seed):
-            raise CryptoError("Failed to generate a key pair")
+        secret_key, public_key = nacl.c.crypto_sign_seed_keypair(seed)
 
-        # Secret values
         self._seed = seed
-        self._signing_key = nacl.ffi.buffer(sk, nacl.lib.crypto_sign_SECRETKEYBYTES)[:]
-
-        # Public values
-        self.verify_key = VerifyKey(nacl.ffi.buffer(pk, nacl.lib.crypto_sign_PUBLICKEYBYTES)[:])
+        self._signing_key = secret_key
+        self.verify_key = VerifyKey(public_key)
 
     def __bytes__(self):
         return self._seed
@@ -160,9 +141,10 @@ class SigningKey(encoding.Encodable, StringFixer, object):
 
         :rtype: :class:`~nacl.signing.SigningKey`
         """
-        return cls(random(nacl.lib.crypto_sign_SECRETKEYBYTES // 2),
-                    encoder=encoding.RawEncoder,
-                )
+        return cls(
+            random(nacl.c.crypto_sign_SEEDBYTES),
+            encoder=encoding.RawEncoder,
+        )
 
     def sign(self, message, encoder=encoding.RawEncoder):
         """
@@ -172,16 +154,10 @@ class SigningKey(encoding.Encodable, StringFixer, object):
         :param encoder: A class that is used to encode the signed message.
         :rtype: :class:`~nacl.signing.SignedMessage`
         """
-        sm = nacl.ffi.new("unsigned char[]", len(message) + nacl.lib.crypto_sign_BYTES)
-        smlen = nacl.ffi.new("unsigned long long *")
-
-        if not nacl.lib.crypto_sign(sm, smlen, message, len(message), self._signing_key):
-            raise CryptoError("Failed to sign the message")
-
-        raw_signed = nacl.ffi.buffer(sm, smlen[0])[:]
+        raw_signed = nacl.c.crypto_sign(self._signing_key, message)
 
-        signature = encoder.encode(raw_signed[:nacl.lib.crypto_sign_BYTES])
-        message = encoder.encode(raw_signed[nacl.lib.crypto_sign_BYTES:])
+        signature = encoder.encode(raw_signed[:nacl.c.crypto_sign_BYTES])
+        message = encoder.encode(raw_signed[nacl.c.crypto_sign_BYTES:])
         signed = encoder.encode(raw_signed)
 
         return SignedMessage._from_parts(signature, message, signed)
diff --git a/src/nacl/utils.py b/src/nacl/utils.py
index 98c41891364e46744f977b95697b8c512b03fae4..61b901cb0c47df3d1a703324cb2f3acdabaad0d0 100644
--- a/src/nacl/utils.py
+++ b/src/nacl/utils.py
@@ -16,7 +16,7 @@ from __future__ import division
 
 import six
 
-from . import nacl
+import nacl.c
 
 
 class EncryptedMessage(six.binary_type):
@@ -57,6 +57,4 @@ class StringFixer(object):
 
 
 def random(size=32):
-    data = nacl.ffi.new("unsigned char[]", size)
-    nacl.lib.randombytes(data, size)
-    return nacl.ffi.buffer(data, size)[:]
+    return nacl.c.randombytes(size)
diff --git a/tests/test_signing.py b/tests/test_signing.py
index 92ca3f043108551020a2309a0504886bf1b04e36..803f3617b006fe9aaba889ad722307d93beb16f6 100644
--- a/tests/test_signing.py
+++ b/tests/test_signing.py
@@ -21,7 +21,7 @@ import pytest
 
 import nacl.signing
 import nacl.encoding
-import nacl.nacl
+import nacl.exceptions
 
 
 def ed25519_known_answers():
@@ -86,9 +86,9 @@ class TestVerifyKey:
         # Small sanity check
         assert skey.verify_key.verify(smessage)
 
-        with pytest.raises(nacl.signing.BadSignatureError):
+        with pytest.raises(nacl.exceptions.BadSignatureError):
             skey.verify_key.verify(message, signature)
 
-        with pytest.raises(nacl.signing.BadSignatureError):
+        with pytest.raises(nacl.exceptions.BadSignatureError):
             forged = nacl.signing.SignedMessage(signature + message)
             skey.verify_key.verify(forged)