Skip to content

Commit

Permalink
Fix #13 - add Keypair API and re-derive public keys, raise an error i…
Browse files Browse the repository at this point in the history
…f the privkey and pubkey are not associated
  • Loading branch information
potatosalad committed Aug 28, 2022
1 parent 80ece09 commit 16ba07e
Show file tree
Hide file tree
Showing 22 changed files with 1,248 additions and 215 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 2.1.0 (2022-08-28)

* Fixes
* Security fix for [Misuse of public apis can result in private key exposure #13](https://github.com/potatosalad/erlang-libdecaf/issues/13) (see [report here](https://github.com/MystenLabs/ed25519-unsafe-libs)).
* Enhancements
* New Keypair API for Ed25519 and Ed448 operations (see [#13](https://github.com/potatosalad/erlang-libdecaf/issues/13)).
* Upstream update to [`ed448goldilocks` version `features-20220828`](https://github.com/potatosalad/ed448goldilocks/tree/features-20220828) (vendored as part of `libdecaf` app).

## 2.0.0 (2022-01-25)

* Fixes
Expand Down
14 changes: 13 additions & 1 deletion c_deps/ed448goldilocks/HISTORY.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
July 13, 2022:
Fix a security bug and an issue.

Point::steg_encode was leaving the 24 high bits of the buffer as zero.
It also ignored the size parameter. The size parameter has now been removed,
the zeros fixed and a test added to make sure that it is fixed.

Per https://github.com/MystenLabs/ed25519-unsafe-libs, deprecate eddsa signing
with separate pubkey and privkey input. Instead decaf_ed*_keypair_sign.

Release v1.0.2.

October 10, 2020:
A paper by Konstantinos Chalkias, François Garillot, and Valeria
Nikolaenko, to be found at:
Expand All @@ -10,7 +22,7 @@ October 10, 2020:
an attacker could modify an existing valid signature to create a
new signature that is still valid, but only for the same message.

Releave v1.0.1, correcting this flaw.
Release v1.0.1, correcting this flaw.

July 12, 2018:
Release 1.0 with Johan Pascal's build scripts.
Expand Down
2 changes: 1 addition & 1 deletion c_deps/ed448goldilocks/src/generator/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def fillin(template,data):
ret = ""
while True:
dollars = template.find("$(",position)
if dollars is -1: return ret + template[position:]
if dollars == -1: return ret + template[position:]
ret += template[position:dollars]
position = dollars + 2
parens = 1
Expand Down
4 changes: 2 additions & 2 deletions c_deps/ed448goldilocks/src/per_curve/eddsa.tmpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ extern "C" {
#define $(C_NS)_EDDSA_DECODE_RATIO ($(cofactor) / $(eddsa_encode_ratio))

#ifndef DECAF_EDDSA_NON_KEYPAIR_API_IS_DEPRECATED
/** If 1, add deprecation attribute to non-keypair API functions. For now, deprecate in Doxygen only. */
#define DECAF_EDDSA_NON_KEYPAIR_API_IS_DEPRECATED 0
/** If 1, add deprecation attribute to non-keypair API functions. Now deprecated. */
#define DECAF_EDDSA_NON_KEYPAIR_API_IS_DEPRECATED 1
#endif

/** @cond internal */
Expand Down
5 changes: 2 additions & 3 deletions c_deps/ed448goldilocks/src/per_curve/point.tmpl.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -551,12 +551,11 @@ public:
}

/** Steganographically encode this */
inline SecureBuffer steg_encode(Rng &rng, size_t size=STEG_BYTES) const /*throw(std::bad_alloc, LengthException)*/ {
if (size <= HASH_BYTES + 4 || size > 2*HASH_BYTES) throw LengthException();
inline SecureBuffer steg_encode(Rng &rng) const /*throw(std::bad_alloc, LengthException)*/ {
SecureBuffer out(STEG_BYTES);
decaf_error_t done;
do {
rng.read(Buffer(out).slice(HASH_BYTES-4,STEG_BYTES-HASH_BYTES+1));
rng.read(Buffer(out).slice(HASH_BYTES-4,STEG_BYTES-HASH_BYTES+4));
uint32_t hint = 0;
for (int i=0; i<4; i++) { hint |= uint32_t(out[HASH_BYTES-4+i])<<(8*i); }
done = invert_elligator(out, hint);
Expand Down
12 changes: 9 additions & 3 deletions c_deps/ed448goldilocks/test/test_decaf.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ static void print(const char *name, const Scalar &x) {

static void hexprint(const char *name, const SecureBuffer &buffer) {
printf(" %s = 0x", name);
for (auto i = buffer.rbegin(); i!= buffer.rend(); ++i) {
printf("%02x", *i);
for (int i=buffer.size()-1; i>=0; i--) {
printf("%02x", buffer[i]);
}
printf("\n");
}
Expand Down Expand Up @@ -292,7 +292,13 @@ static void test_elligator() {
}

Point t(rng);
point_check(test,t,t,t,0,0,t,Point::from_hash(t.steg_encode(rng)),"steg round-trip");
SecureBuffer bsteg = t.steg_encode(rng);
if (bsteg[Point::STEG_BYTES-1] == 0 && bsteg[Point::STEG_BYTES-2] == 0 && bsteg[Point::STEG_BYTES-3] == 0) {
test.fail();
printf(" Steg is nonuniform (probability of false failure = 2^-24)\n");
hexprint(" steg buffer:", bsteg);
}
point_check(test,t,t,t,0,0,t,Point::from_hash(bsteg),"steg round-trip");

FixedArrayBuffer<Point::HASH_BYTES> b3(rng), b4(b3);
t = Point::from_hash(b3);
Expand Down
4 changes: 2 additions & 2 deletions c_src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ LDLIBS += -L"$(ERL_INTERFACE_LIB_DIR)" \

# Dependencies.

ED448GOLDILOCKS_VSN ?= features-20220121
ED448GOLDILOCKS_GIT ?= git://github.com/potatosalad/ed448goldilocks.git
ED448GOLDILOCKS_VSN ?= features-20220828
ED448GOLDILOCKS_GIT ?= https://github.com/potatosalad/ed448goldilocks.git
ED448GOLDILOCKS_SRC_DIR ?= $(C_DEPS_DIR)/ed448goldilocks
ED448GOLDILOCKS_BUILD_DIR ?= $(C_DEPS_DIR)/ed448goldilocks-build
ED448GOLDILOCKS_INCLUDE_DIR ?= $(ED448GOLDILOCKS_BUILD_DIR)/src/GENERATED/include
Expand Down
157 changes: 157 additions & 0 deletions c_src/nif/ed25519.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// -*- mode: c; tab-width: 4; indent-tabs-mode: nil; st-rulers: [132] -*-
// vim: ts=4 sw=4 ft=c et

#include "ed25519.h"

/* Function Definitions */

/* libdecaf_nif:ed25519_derive_keypair/1 */

ERL_NIF_TERM
libdecaf_nif_ed25519_derive_keypair_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM out_term;
ErlNifBinary privkey;
libdecaf_nif_ed25519_keypair_t *keypair = NULL;

if (argc != 1) {
return EXCP_BADARG(env, "argc must be 1");
}

if (!enif_inspect_binary(env, argv[0], &privkey) || privkey.size != DECAF_EDDSA_25519_PRIVATE_BYTES) {
return EXCP_BADARG_F(env, "Privkey must be a binary of size %d-bytes", DECAF_EDDSA_25519_PRIVATE_BYTES);
}

keypair = (libdecaf_nif_ed25519_keypair_t *)(enif_alloc_resource(libdecaf_nif_ed25519_keypair_resource_type, sizeof(libdecaf_nif_ed25519_keypair_t)));
if (keypair == NULL) {
return EXCP_ERROR(env, "Failed to allocate libdecaf_nif_ed25519_keypair_t");
}
(void)decaf_ed25519_derive_keypair(keypair->inner, privkey.data);

out_term = enif_make_resource(env, (void *)keypair);
(void)enif_release_resource((void *)keypair);

return out_term;
}

/* libdecaf_nif:ed25519_keypair_extract_private_key/1 */

ERL_NIF_TERM
libdecaf_nif_ed25519_keypair_extract_private_key_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
libdecaf_nif_ed25519_keypair_t *keypair = NULL;
ERL_NIF_TERM out;
uint8_t *privkey = NULL;

if (argc != 1) {
return EXCP_BADARG(env, "argc must be 1");
}

if (!enif_get_resource(env, argv[0], libdecaf_nif_ed25519_keypair_resource_type, (void **)(&keypair))) {
return EXCP_BADARG(env, "Keypair reference is invalid");
}

privkey = (uint8_t *)(enif_make_new_binary(env, DECAF_EDDSA_25519_PRIVATE_BYTES, &out));
(void)decaf_ed25519_keypair_extract_private_key(privkey, keypair->inner);

return out;
}

/* libdecaf_nif:ed25519_keypair_extract_public_key/1 */

ERL_NIF_TERM
libdecaf_nif_ed25519_keypair_extract_public_key_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
libdecaf_nif_ed25519_keypair_t *keypair = NULL;
ERL_NIF_TERM out;
uint8_t *pubkey = NULL;

if (argc != 1) {
return EXCP_BADARG(env, "argc must be 1");
}

if (!enif_get_resource(env, argv[0], libdecaf_nif_ed25519_keypair_resource_type, (void **)(&keypair))) {
return EXCP_BADARG(env, "Keypair reference is invalid");
}

pubkey = (uint8_t *)(enif_make_new_binary(env, DECAF_EDDSA_25519_PUBLIC_BYTES, &out));
(void)decaf_ed25519_keypair_extract_public_key(pubkey, keypair->inner);

return out;
}

/* libdecaf_nif:ed25519_keypair_sign/4 */

ERL_NIF_TERM
libdecaf_nif_ed25519_keypair_sign_4(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
libdecaf_nif_ed25519_keypair_t *keypair = NULL;
ErlNifBinary message;
unsigned int prehashed;
ErlNifBinary context;
ERL_NIF_TERM out;
uint8_t *signature = NULL;

if (argc != 4) {
return EXCP_BADARG(env, "argc must be 4");
}

if (!enif_get_resource(env, argv[0], libdecaf_nif_ed25519_keypair_resource_type, (void **)(&keypair))) {
return EXCP_BADARG(env, "Keypair reference is invalid");
}
if (!enif_inspect_binary(env, argv[1], &message)) {
return EXCP_BADARG(env, "Message must be a binary");
}
if (!enif_get_uint(env, argv[2], &prehashed) || (prehashed != 0 && prehashed != 1)) {
return EXCP_BADARG(env, "Prehashed must be one of {0,1}");
}
if (enif_compare(ATOM(no_context), argv[3]) == 0) {
context.size = 0;
context.data = (unsigned char *)(DECAF_ED25519_NO_CONTEXT);
} else if (!enif_inspect_binary(env, argv[3], &context) || context.size > 255) {
return EXCP_BADARG(env, "Context must be either the atom 'no_context' or a binary of size <= 255-bytes");
}

signature = (uint8_t *)(enif_make_new_binary(env, DECAF_EDDSA_25519_SIGNATURE_BYTES, &out));
(void)decaf_ed25519_keypair_sign(signature, keypair->inner, message.data, message.size, prehashed, context.data, context.size);

return out;
}

/* libdecaf_nif:ed25519_keypair_sign_prehash/3 */

ERL_NIF_TERM
libdecaf_nif_ed25519_keypair_sign_prehash_3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
libdecaf_nif_ed25519_keypair_t *keypair = NULL;
ErlNifBinary message;
ErlNifBinary context;
decaf_ed25519_prehash_ctx_t hash;
ERL_NIF_TERM out;
uint8_t *signature = NULL;

if (argc != 3) {
return EXCP_BADARG(env, "argc must be 3");
}

if (!enif_get_resource(env, argv[0], libdecaf_nif_ed25519_keypair_resource_type, (void **)(&keypair))) {
return EXCP_BADARG(env, "Keypair reference is invalid");
}
if (!enif_inspect_binary(env, argv[1], &message)) {
return EXCP_BADARG(env, "Message must be a binary");
}
if (enif_compare(ATOM(no_context), argv[2]) == 0) {
context.size = 0;
context.data = (unsigned char *)(DECAF_ED25519_NO_CONTEXT);
} else if (!enif_inspect_binary(env, argv[2], &context) || context.size > 255) {
return EXCP_BADARG(env, "Context must be either the atom 'no_context' or a binary of size <= 255-bytes");
}

(void)decaf_ed25519_prehash_init(hash);
(void)decaf_ed25519_prehash_update(hash, message.data, message.size);
signature = (uint8_t *)(enif_make_new_binary(env, DECAF_EDDSA_25519_SIGNATURE_BYTES, &out));
(void)decaf_ed25519_keypair_sign_prehash(signature, keypair->inner, hash, context.data, context.size);
(void)decaf_ed25519_prehash_destroy(hash);

return out;
}
31 changes: 31 additions & 0 deletions c_src/nif/ed25519.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; st-rulers: [132] -*-
// vim: ts=4 sw=4 ft=c++ et

#ifndef LIBDECAF_NIF_ED25519_H
#define LIBDECAF_NIF_ED25519_H

#include "libdecaf_nif.h"

#include <decaf/ed255.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct libdecaf_nif_ed25519_keypair_s libdecaf_nif_ed25519_keypair_t;

struct libdecaf_nif_ed25519_keypair_s {
decaf_eddsa_25519_keypair_t inner;
};

extern ERL_NIF_TERM libdecaf_nif_ed25519_derive_keypair_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
extern ERL_NIF_TERM libdecaf_nif_ed25519_keypair_extract_private_key_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
extern ERL_NIF_TERM libdecaf_nif_ed25519_keypair_extract_public_key_1(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
extern ERL_NIF_TERM libdecaf_nif_ed25519_keypair_sign_4(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);
extern ERL_NIF_TERM libdecaf_nif_ed25519_keypair_sign_prehash_3(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]);

#ifdef __cplusplus
}
#endif

#endif
Loading

0 comments on commit 16ba07e

Please sign in to comment.