Skip to content

Commit

Permalink
Merge pull request #138 from JackWink/feature/jack/ed25519-to-curve25519
Browse files Browse the repository at this point in the history
Add ed25519 to curve25519 functions
  • Loading branch information
reaperhulk committed Dec 12, 2015
2 parents cf70d96 + d5acc7f commit dba6d55
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/nacl/_lib/crypto_sign.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ int crypto_sign(unsigned char *sm, unsigned long long *smlen,
int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
const unsigned char *sm, unsigned long long smlen,
const unsigned char *pk);

int crypto_sign_ed25519_pk_to_curve25519(unsigned char *curve25519_pk,
const unsigned char *ed25519_pk);

int crypto_sign_ed25519_sk_to_curve25519(unsigned char *curve25519_sk,
const unsigned char *ed25519_sk);
7 changes: 5 additions & 2 deletions src/nacl/bindings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
)
from nacl.bindings.crypto_sign import (
crypto_sign, crypto_sign_BYTES, crypto_sign_PUBLICKEYBYTES,
crypto_sign_SECRETKEYBYTES, crypto_sign_SEEDBYTES, crypto_sign_keypair,
crypto_sign_open, crypto_sign_seed_keypair,
crypto_sign_SECRETKEYBYTES, crypto_sign_SEEDBYTES,
crypto_sign_ed25519_pk_to_curve25519, crypto_sign_ed25519_sk_to_curve25519,
crypto_sign_keypair, crypto_sign_open, crypto_sign_seed_keypair
)
from nacl.bindings.randombytes import randombytes
from nacl.bindings.sodium_core import sodium_init
Expand Down Expand Up @@ -84,6 +85,8 @@
"crypto_sign_seed_keypair",
"crypto_sign",
"crypto_sign_open",
"crypto_sign_ed25519_pk_to_curve25519",
"crypto_sign_ed25519_sk_to_curve25519",

"randombytes",

Expand Down
50 changes: 50 additions & 0 deletions src/nacl/bindings/crypto_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
crypto_sign_PUBLICKEYBYTES = lib.crypto_sign_publickeybytes()
crypto_sign_SECRETKEYBYTES = lib.crypto_sign_secretkeybytes()

crypto_sign_curve25519_BYTES = lib.crypto_box_secretkeybytes()


def crypto_sign_keypair():
"""
Expand Down Expand Up @@ -100,3 +102,51 @@ def crypto_sign_open(signed, pk):
raise BadSignatureError("Signature was forged or corrupt")

return lib.ffi.buffer(message, message_len[0])[:]


def crypto_sign_ed25519_pk_to_curve25519(public_key_bytes):
"""
Converts a public Ed25519 key (encoded as bytes ``public_key_bytes``) to
a public Curve25519 key as bytes.
Raises a ValueError if ``public_key_bytes`` is not of length
``crypto_sign_PUBLICKEYBYTES``
:param public_key_bytes: bytes
:rtype: bytes
"""
if len(public_key_bytes) != crypto_sign_PUBLICKEYBYTES:
raise ValueError("Invalid curve public key")

curve_public_key_len = crypto_sign_curve25519_BYTES
curve_public_key = lib.ffi.new("unsigned char[]", curve_public_key_len)

rc = lib.crypto_sign_ed25519_pk_to_curve25519(curve_public_key,
public_key_bytes)
assert rc == 0

return lib.ffi.buffer(curve_public_key, curve_public_key_len)[:]


def crypto_sign_ed25519_sk_to_curve25519(secret_key_bytes):
"""
Converts a secret Ed25519 key (encoded as bytes ``secret_key_bytes``) to
a secret Curve25519 key as bytes.
Raises a ValueError if ``secret_key_bytes``is not of length
``crypto_sign_SECRETKEYBYTES``
:param public_key_bytes: bytes
:rtype: bytes
"""
if len(secret_key_bytes) != crypto_sign_SECRETKEYBYTES:
raise ValueError("Invalid curve public key")

curve_secret_key_len = crypto_sign_curve25519_BYTES
curve_secret_key = lib.ffi.new("unsigned char[]", curve_secret_key_len)

rc = lib.crypto_sign_ed25519_sk_to_curve25519(curve_secret_key,
secret_key_bytes)
assert rc == 0

return lib.ffi.buffer(curve_secret_key, curve_secret_key_len)[:]
23 changes: 23 additions & 0 deletions src/nacl/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import six

from nacl import encoding

import nacl.bindings
from nacl.public import PrivateKey, PublicKey
from nacl.utils import StringFixer, random


Expand Down Expand Up @@ -99,6 +101,16 @@ def verify(self, smessage, signature=None, encoder=encoding.RawEncoder):

return nacl.bindings.crypto_sign_open(smessage, self._key)

def to_public_key(self):
"""
Converts a :class:`~nacl.signing.VerifyKey` to a
:class:`~nacl.public.PublicKey`
:rtype: :class:`~nacl.public.PublicKey`
"""
raw_pk = nacl.bindings.crypto_sign_ed25519_pk_to_curve25519(self._key)
return PublicKey(raw_pk)


class SigningKey(encoding.Encodable, StringFixer, object):
"""
Expand Down Expand Up @@ -169,3 +181,14 @@ def sign(self, message, encoder=encoding.RawEncoder):
signed = encoder.encode(raw_signed)

return SignedMessage._from_parts(signature, message, signed)

def to_private_key(self):
"""
Converts a :class:`~nacl.signing.SigningKey` to a
:class:`~nacl.public.PrivateKey`
:rtype: :class:`~nacl.public.PrivateKey`
"""
sk = self._signing_key
raw_private = nacl.bindings.crypto_sign_ed25519_sk_to_curve25519(sk)
return PrivateKey(raw_private)
23 changes: 23 additions & 0 deletions tests/test_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,26 @@ def test_scalarmult():
"e34f73a93ebbaa271765e5036edfc519")
bz2 = c.crypto_scalarmult(z, base)
assert tohex(bz1) == tohex(bz2)


def test_sign_test_key_conversion():
"""
Taken from test vectors in libsodium
"""
keypair_seed = unhexlify(b"421151a459faeade3d247115f94aedae"
b"42318124095afabe4d1451a559faedee")
ed25519_pk, ed25519_sk = c.crypto_sign_seed_keypair(keypair_seed)

curve25519_pk = c.crypto_sign_ed25519_pk_to_curve25519(ed25519_pk)

with pytest.raises(ValueError):
c.crypto_sign_ed25519_pk_to_curve25519(unhexlify(b"12"))
with pytest.raises(ValueError):
c.crypto_sign_ed25519_sk_to_curve25519(unhexlify(b"12"))

curve25519_sk = c.crypto_sign_ed25519_sk_to_curve25519(ed25519_sk)

assert tohex(curve25519_pk) == ("f1814f0e8ff1043d8a44d25babff3ced"
"cae6c22c3edaa48f857ae70de2baae50")
assert tohex(curve25519_sk) == ("8052030376d47112be7f73ed7a019293"
"dd12ad910b654455798b4667d73de166")
19 changes: 19 additions & 0 deletions tests/test_signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
from nacl.signing import SignedMessage, SigningKey, VerifyKey


def tohex(b):
return binascii.hexlify(b).decode('ascii')


def ed25519_known_answers():
# Known answers taken from: http://ed25519.cr.yp.to/python/sign.input
answers = []
Expand Down Expand Up @@ -132,6 +136,21 @@ def test_invalid_signed_message(self):
forged = SignedMessage(signature + message)
skey.verify_key.verify(forged)

def test_key_conversion(self):
keypair_seed = (b"421151a459faeade3d247115f94aedae"
b"42318124095afabe4d1451a559faedee")
signing_key = SigningKey(binascii.unhexlify(keypair_seed))
verify_key = signing_key.verify_key

private_key = bytes(signing_key.to_private_key())
public_key = bytes(verify_key.to_public_key())

assert tohex(private_key) == ("8052030376d47112be7f73ed7a019293"
"dd12ad910b654455798b4667d73de166")

assert tohex(public_key) == ("f1814f0e8ff1043d8a44d25babff3ced"
"cae6c22c3edaa48f857ae70de2baae50")


def check_type_error(expected, f, *args):
with pytest.raises(TypeError) as e:
Expand Down

0 comments on commit dba6d55

Please sign in to comment.