Skip to content

Commit

Permalink
Merge 73d854f into 61cc9f2
Browse files Browse the repository at this point in the history
  • Loading branch information
skion committed Aug 13, 2014
2 parents 61cc9f2 + 73d854f commit e63eb4d
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Features
* Digital signatures
* Secret-key encryption
* Public-key encryption
* One-time authentication
* HMAC (coming soon)


Expand Down
24 changes: 24 additions & 0 deletions src/nacl/_lib/crypto_onetimeauth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* 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_onetimeauth_bytes();
size_t crypto_onetimeauth_keybytes();


int crypto_onetimeauth(unsigned char *out, const unsigned char *in,
unsigned long long inlen, const unsigned char *k);

int crypto_onetimeauth_verify(const unsigned char *h, const unsigned char *in,
unsigned long long inlen, const unsigned char *k);
9 changes: 9 additions & 0 deletions src/nacl/c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
crypto_hash, crypto_hash_BYTES, crypto_hash_sha256,
crypto_hash_sha256_BYTES, crypto_hash_sha512, crypto_hash_sha512_BYTES,
)
from nacl.c.crypto_onetimeauth import (
crypto_onetimeauth, crypto_onetimeauth_BYTES, crypto_onetimeauth_KEYBYTES,
crypto_onetimeauth_verify,
)
from nacl.c.crypto_scalarmult import (
crypto_scalarmult, crypto_scalarmult_BYTES, crypto_scalarmult_SCALARBYTES,
crypto_scalarmult_base
Expand Down Expand Up @@ -64,6 +68,11 @@
"crypto_hash_sha256",
"crypto_hash_sha512",

"crypto_onetimeauth_BYTES",
"crypto_onetimeauth_KEYBYTES",
"crypto_onetimeauth",
"crypto_onetimeauth_verify",

"crypto_scalarmult_BYTES",
"crypto_scalarmult_SCALARBYTES",
"crypto_scalarmult",
Expand Down
55 changes: 55 additions & 0 deletions src/nacl/c/crypto_onetimeauth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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_onetimeauth_BYTES = lib.crypto_onetimeauth_bytes()
crypto_onetimeauth_KEYBYTES = lib.crypto_onetimeauth_keybytes()


def crypto_onetimeauth(message, k):
"""
Authenticates a ``message`` using a secret key ``k``, and returns an
authenticator.
:param message: bytes
:param k: bytes
:rtype: bytes
"""
authenticator = lib.ffi.new("unsigned char[]", crypto_onetimeauth_BYTES)

if lib.crypto_onetimeauth(authenticator, message, len(message), k) != 0:
raise CryptoError("Failed to generate authenticator for message")

return lib.ffi.buffer(authenticator, crypto_onetimeauth_BYTES)[:]


def crypto_onetimeauth_verify(authenticator, message, k):
"""
Check that ``authenticator`` is correct for ``message`` under the
secret ``k`` and raise a ``BadSignatureError`` otherwise.
:param authenticator: bytes
:param message: bytes
:param k: bytes
:rtype: bool
"""
if lib.crypto_onetimeauth_verify(authenticator,
message, len(message), k) != 0:
raise BadSignatureError("Authenticator was forged or corrupt")

return True
2 changes: 1 addition & 1 deletion src/nacl/c/crypto_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def crypto_sign(message, sk):
def crypto_sign_open(signed, pk):
"""
Verifies the signature of the signed message ``signed`` using the public
key ``pkg`` and returns the unsigned message.
key ``pk`` and returns the unsigned message.
:param signed: bytes
:param pk: bytes
Expand Down
68 changes: 68 additions & 0 deletions src/nacl/onetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 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 nacl.c
import nacl.encoding

AUTH_SIZE = nacl.c.crypto_onetimeauth_BYTES
KEY_SIZE = nacl.c.crypto_onetimeauth_KEYBYTES


def generate(message, key, encoder=nacl.encoding.RawEncoder):
"""
Authenticates ``message`` using a secret ``key``, and returns an
authenticator. All three are passed through the provided ``encoder``.
:param message: bytes
:param key: bytes
:rtype: bytes
"""
message = encoder.decode(message)
key = encoder.decode(key)

if len(key) != KEY_SIZE:
raise ValueError("The key must be exactly %s bytes long" %
KEY_SIZE)

authenticator = nacl.c.crypto_onetimeauth(message, key)

return encoder.encode(authenticator)


def verify(authenticator, message, key, encoder=nacl.encoding.RawEncoder):
"""
Check that ``authenticator`` is correct for ``message`` under the
secret ``key`` and raise a ``BadSignatureError`` otherwise.
:param authenticator: bytes
:param message: bytes
:param key: bytes
:rtype: bool
"""

authenticator = encoder.decode(authenticator)
message = encoder.decode(message)
key = encoder.decode(key)

if len(authenticator) != AUTH_SIZE:
raise ValueError("The authenticator must be exactly %s bytes long" %
AUTH_SIZE)

if len(key) != KEY_SIZE:
raise ValueError("The key must be exactly %s bytes long" %
KEY_SIZE)

return nacl.c.crypto_onetimeauth_verify(authenticator, message, key)
125 changes: 125 additions & 0 deletions tests/test_onetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# 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 pytest

import nacl.encoding
import nacl.exceptions
import nacl.onetime


class TestOneTimeGenerate:

def test_generate(self):
a = nacl.onetime.generate(b"foo", b"1" * nacl.onetime.KEY_SIZE)
assert len(a) == nacl.onetime.AUTH_SIZE

@pytest.mark.parametrize("encoder", [
nacl.encoding.HexEncoder,
nacl.encoding.RawEncoder,
])
def test_encoding(self, encoder):
rawmsg = b"foo"
rawkey = b"1" * nacl.onetime.KEY_SIZE

encmsg = encoder.encode(rawmsg)
enckey = encoder.encode(rawkey)

a1 = nacl.onetime.generate(rawmsg, rawkey)
a2 = nacl.onetime.generate(encmsg, enckey, encoder)

assert a1 == encoder.decode(a2)

def test_wrong_length(self):
with pytest.raises(ValueError):
nacl.onetime.generate(b"foo", b"1" * 31)


class TestOneTimeVerify:

def test_verify(self):
msg = b"foo"
key = b"1" * nacl.onetime.KEY_SIZE

a = nacl.onetime.generate(b"foo", b"1" * nacl.onetime.KEY_SIZE)
assert len(a) == nacl.onetime.AUTH_SIZE

a = nacl.onetime.generate(b"foo", b"1" * nacl.onetime.KEY_SIZE)
assert len(a) == nacl.onetime.AUTH_SIZE

@pytest.mark.parametrize("encoder", [
nacl.encoding.HexEncoder,
nacl.encoding.RawEncoder,
])
def test_encoding(self, encoder):
rawmsg = b"foo"
rawkey = b"1" * nacl.onetime.KEY_SIZE

encmsg = encoder.encode(rawmsg)
enckey = encoder.encode(rawkey)

a = nacl.onetime.generate(encmsg, enckey, encoder)
assert nacl.onetime.verify(a, encmsg, enckey, encoder)

@pytest.mark.parametrize(("msg", "key", "a"), [
(
# a was generated by tweet-nacl
b'12e88ecb241226f24d298685de7ad2cc93ed745f',
b'09f61ec34505cd0f4c9dddaa15cbae2eaf234429991f2c12e41a5eee964819fa',
b'd483cc10046057a315ae643f94c6c24c',
)
])
def test_positive_samples(self, msg, key, a):
assert nacl.onetime.verify(a, msg, key, nacl.encoding.HexEncoder)

@pytest.mark.parametrize(("msg", "key", "a"), [
(
# a was generated by tweet-nacl
# corrupt msg
b'A2e88ecb241226f24d298685de7ad2cc93ed745f',
b'09f61ec34505cd0f4c9dddaa15cbae2eaf234429991f2c12e41a5eee964819fa',
b'd483cc10046057a315ae643f94c6c24c',
),
(
# a was generated by tweet-nacl
# corrupt key
b'12e88ecb241226f24d298685de7ad2cc93ed745f',
b'A9f61ec34505cd0f4c9dddaa15cbae2eaf234429991f2c12e41a5eee964819fa',
b'd483cc10046057a315ae643f94c6c24c',
),
(
# a was generated by tweet-nacl
# corrupt authenticator
b'12e88ecb241226f24d298685de7ad2cc93ed745f',
b'09f61ec34505cd0f4c9dddaa15cbae2eaf234429991f2c12e41a5eee964819fa',
b'A483cc10046057a315ae643f94c6c24c',
)
])
def test_negative_samples(self, msg, key, a):
with pytest.raises(nacl.exceptions.BadSignatureError):
nacl.onetime.verify(a, msg, key, nacl.encoding.HexEncoder)

def test_wrong_auth_length(self):
with pytest.raises(ValueError):
nacl.onetime.verify(b"1" * (nacl.onetime.AUTH_SIZE + 1),
b"foo",
b"1" * nacl.onetime.KEY_SIZE)

def test_wrong_key_length(self):
with pytest.raises(ValueError):
nacl.onetime.verify(b"1" * nacl.onetime.AUTH_SIZE,
b"foo",
b"1" * (nacl.onetime.KEY_SIZE - 1))

0 comments on commit e63eb4d

Please sign in to comment.