Browse Source

Merge pull request #162 from PQClean/mqdss

MQDSS
tags/v0.0.1
Douglas Stebila 5 years ago
committed by GitHub
parent
commit
4270a54161
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1839 additions and 0 deletions
  1. +16
    -0
      crypto_sign/mqdss-48/META.yml
  2. +116
    -0
      crypto_sign/mqdss-48/clean/LICENSE
  3. +20
    -0
      crypto_sign/mqdss-48/clean/Makefile
  4. +19
    -0
      crypto_sign/mqdss-48/clean/Makefile.Microsoft_nmake
  5. +47
    -0
      crypto_sign/mqdss-48/clean/api.h
  6. +133
    -0
      crypto_sign/mqdss-48/clean/gf31.c
  7. +40
    -0
      crypto_sign/mqdss-48/clean/gf31.h
  8. +81
    -0
      crypto_sign/mqdss-48/clean/mq.c
  9. +18
    -0
      crypto_sign/mqdss-48/clean/mq.h
  10. +25
    -0
      crypto_sign/mqdss-48/clean/params.h
  11. +384
    -0
      crypto_sign/mqdss-48/clean/sign.c
  12. +16
    -0
      crypto_sign/mqdss-64/META.yml
  13. +116
    -0
      crypto_sign/mqdss-64/clean/LICENSE
  14. +20
    -0
      crypto_sign/mqdss-64/clean/Makefile
  15. +19
    -0
      crypto_sign/mqdss-64/clean/Makefile.Microsoft_nmake
  16. +47
    -0
      crypto_sign/mqdss-64/clean/api.h
  17. +133
    -0
      crypto_sign/mqdss-64/clean/gf31.c
  18. +40
    -0
      crypto_sign/mqdss-64/clean/gf31.h
  19. +81
    -0
      crypto_sign/mqdss-64/clean/mq.c
  20. +18
    -0
      crypto_sign/mqdss-64/clean/mq.h
  21. +25
    -0
      crypto_sign/mqdss-64/clean/params.h
  22. +384
    -0
      crypto_sign/mqdss-64/clean/sign.c
  23. +11
    -0
      test/duplicate_consistency/mqdss-64_clean.yml
  24. +30
    -0
      test/test_makefiles_present.py

+ 16
- 0
crypto_sign/mqdss-48/META.yml View File

@@ -0,0 +1,16 @@
name: MQDSS-48
type: signature
claimed-nist-level: 1
length-public-key: 46
length-secret-key: 16
length-signature: 20854
testvectors-sha256: 3350a80ccf4316b32ef13060fca8880d6802b7e61150fd36f021d1c52d8edb98
principal-submitter: Simona Samardjiska
auxiliary-submitters:
- Ming-Shing Chen
- Andreas Hülsing
- Joost Rijneveld
- Peter Schwabe
implementations:
- name: clean
version: https://github.com/joostrijneveld/MQDSS/commit/0c64d4d67a37051c1299a3049a5bb8984ca27ecc

+ 116
- 0
crypto_sign/mqdss-48/clean/LICENSE View File

@@ -0,0 +1,116 @@
CC0 1.0 Universal

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").

Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.

For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:

i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;

ii. moral rights retained by the original author(s) and/or performer(s);

iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;

iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;

v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;

vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and

vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.

4. Limitations and Disclaimers.

a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.

b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.

c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.

d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.

For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

+ 20
- 0
crypto_sign/mqdss-48/clean/Makefile View File

@@ -0,0 +1,20 @@
# This Makefile can be used with GNU Make or BSD Make

LIB=libmqdss-48_clean.a

HEADERS = params.h gf31.h mq.h api.h
OBJECTS = gf31.o mq.o sign.o

CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS)

all: $(LIB)

%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<

$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)

clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

+ 19
- 0
crypto_sign/mqdss-48/clean/Makefile.Microsoft_nmake View File

@@ -0,0 +1,19 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake

LIBRARY=libmqdss-48_clean.lib
OBJECTS=gf31.obj mq.obj sign.obj

CFLAGS=/nologo /I ..\..\..\common /W4 /WX

all: $(LIBRARY)

# Make sure objects are recompiled if headers change.
$(OBJECTS): *.h

$(LIBRARY): $(OBJECTS)
LIB.EXE /NOLOGO /WX /OUT:$@ $**

clean:
-DEL $(OBJECTS)
-DEL $(LIBRARY)

+ 47
- 0
crypto_sign/mqdss-48/clean/api.h View File

@@ -0,0 +1,47 @@
#ifndef PQCLEAN_MQDSS48_CLEAN_API_H
#define PQCLEAN_MQDSS48_CLEAN_API_H

#include <stddef.h>
#include <stdint.h>

#define PQCLEAN_MQDSS48_CLEAN_CRYPTO_ALGNAME "MQDSS-48"

#define PQCLEAN_MQDSS48_CLEAN_CRYPTO_SECRETKEYBYTES 16
#define PQCLEAN_MQDSS48_CLEAN_CRYPTO_PUBLICKEYBYTES 46
#define PQCLEAN_MQDSS48_CLEAN_CRYPTO_BYTES 20854

/*
* Generates an MQDSS key pair.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_keypair(
uint8_t *pk, uint8_t *sk);

/**
* Returns an array containing a detached signature.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_signature(
uint8_t *sig, size_t *siglen,
const uint8_t *m, size_t mlen, const uint8_t *sk);

/**
* Verifies a detached signature and message under a given public key.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_verify(
const uint8_t *sig, size_t siglen,
const uint8_t *m, size_t mlen, const uint8_t *pk);

/**
* Returns an array containing the signature followed by the message.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign(
uint8_t *sm, size_t *smlen,
const uint8_t *m, size_t mlen, const uint8_t *sk);

/**
* Verifies a given signature-message pair under a given public key.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_open(
uint8_t *m, size_t *mlen,
const uint8_t *sm, size_t smlen, const uint8_t *pk);

#endif

+ 133
- 0
crypto_sign/mqdss-48/clean/gf31.c View File

@@ -0,0 +1,133 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include "fips202.h"
#include "gf31.h"
#include "params.h"

/* This performs a full unique reduction mod 13 on x; x can be any unsigned
16-bit integer (i.e. in the range [0, 65535]) */
gf31 PQCLEAN_MQDSS48_CLEAN_mod31(gf31 x) {
gf31 t;

t = (gf31)(x & 31);
x >>= 5;
t = (gf31)(t + (x & 31));
x >>= 5;
t = (gf31)(t + (x & 31));
x >>= 5;
t = (gf31)(t + (x & 31));

t = (gf31)((t >> 5) + (t & 31));
t = (gf31)((t >> 5) + (t & 31));
return (gf31)((t != 31) * t);
}

/* Given a vector of N elements in the range [0, 31], this reduces the elements
to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS48_CLEAN_vgf31_unique(gf31 *out, const gf31 *in) {
int i;

for (i = 0; i < N; i++) {
out[i] = (gf31)((1 - (in[i] == 31)) * in[i]);
}
}

/* Given a vector of 16-bit integers (i.e. in [0, 65535], this reduces the
elements to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(gf31 *out, const gf31 *in) {
int i;

for (i = 0; i < N; i++) {
out[i] = PQCLEAN_MQDSS48_CLEAN_mod31(in[i]);
}
}

/* Given a seed, samples len gf31 elements (in the range [0, 30]), and places
them in a vector of 16-bit elements */
void PQCLEAN_MQDSS48_CLEAN_gf31_nrand(gf31 *out, int len, const unsigned char *seed, size_t seedlen) {
int i = 0, j;
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];

shake256_absorb(shakestate, seed, seedlen);

while (i < len) {
shake256_squeezeblocks(shakeblock, 1, shakestate);
for (j = 0; j < SHAKE256_RATE && i < len; j++) {
if ((shakeblock[j] & 31) != 31) {
out[i] = (shakeblock[j] & 31);
i++;
}
}
}
}

/* Given a seed, samples len gf31 elements, transposed into unsigned range,
i.e. in the range [-15, 15], and places them in an array of 8-bit integers.
This is used for the expansion of F, which wants packed elements. */
void PQCLEAN_MQDSS48_CLEAN_gf31_nrand_schar(signed char *out, int len, const unsigned char *seed, size_t seedlen) {
int i = 0, j;
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];

shake256_absorb(shakestate, seed, seedlen);

while (i < len) {
shake256_squeezeblocks(shakeblock, 1, shakestate);
for (j = 0; j < SHAKE256_RATE && i < len; j++) {
if ((shakeblock[j] & 31) != 31) {
out[i] = (signed char)(((signed char)shakeblock[j] & 31) - 15);
i++;
}
}
}
}

/* Unpacks an array of packed GF31 elements to one element per gf31.
Assumes that there is sufficient empty space available at the end of the
array to unpack. Can perform in-place. */
void PQCLEAN_MQDSS48_CLEAN_gf31_nunpack(gf31 *out, const unsigned char *in, unsigned int n) {
size_t i;
unsigned int j = ((n * 5) >> 3) - 1;
unsigned int d = 0;

for (i = n; i > 0; i--) {
out[i - 1] = (gf31)((in[j] >> d) & 31);
d += 5;
if (d > 8) {
d -= 8;
j--;
out[i - 1] = (gf31)(out[i - 1] ^ ((in[j] << (5 - d)) & 31));
}
}
}

/* Packs an array of GF31 elements from gf31's to concatenated 5-bit values.
Assumes that there is sufficient space available to unpack.
Can perform in-place. */
void PQCLEAN_MQDSS48_CLEAN_gf31_npack(unsigned char *out, const gf31 *in, unsigned int n) {
unsigned int i = 0;
unsigned int j;
int d = 3;

for (j = 0; j < n; j++) {
assert(in[j] < 31);
}

/* There will be ceil(5n / 8) output blocks */
memset(out, 0, ((5 * n + 7) & (unsigned int)~7) >> 3);

for (j = 0; j < n; j++) {
if (d < 0) {
d += 8;
out[i] = (unsigned char)((out[i] & (255 << (d - 3))) |
((in[j] >> (8 - d)) & ~(255 << (d - 3))));
i++;
}
out[i] = (unsigned char)((out[i] & ~(31 << d)) | ((in[j] << d) & (31 << d)));
d -= 5;
}
}

+ 40
- 0
crypto_sign/mqdss-48/clean/gf31.h View File

@@ -0,0 +1,40 @@
#ifndef MQDSS_GF31_H
#define MQDSS_GF31_H

#include <stddef.h>
#include <stdint.h>

typedef uint16_t gf31;

/* This performs a full unique reduction mod 13 on x; x can be any unsigned
16-bit integer (i.e. in the range [0, 65535]) */
gf31 PQCLEAN_MQDSS48_CLEAN_mod31(gf31 x);

/* Given a vector of elements in the range [0, 31], this reduces the elements
to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS48_CLEAN_vgf31_unique(gf31 *out, const gf31 *in);

/* Given a vector of 16-bit integers (i.e. in [0, 65535], this reduces the
elements to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(gf31 *out, const gf31 *in);

/* Given a seed, samples len gf31 elements (in the range [0, 30]), and places
them in a vector of 16-bit elements */
void PQCLEAN_MQDSS48_CLEAN_gf31_nrand(gf31 *out, int len, const unsigned char *seed, size_t seedlen);

/* Given a seed, samples len gf31 elements, transposed into unsigned range,
i.e. in the range [-15, 15], and places them in an array of 8-bit integers.
This is used for the expansion of F, which wants packed elements. */
void PQCLEAN_MQDSS48_CLEAN_gf31_nrand_schar(signed char *out, int len, const unsigned char *seed, size_t seedlen);

/* Unpacks an array of packed GF31 elements to one element per gf31.
Assumes that there is sufficient empty space available at the end of the
array to unpack. Can perform in-place. */
void PQCLEAN_MQDSS48_CLEAN_gf31_nunpack(gf31 *out, const unsigned char *in, unsigned int n);

/* Packs an array of GF31 elements from gf31's to concatenated 5-bit values.
Assumes that there is sufficient space available to unpack.
Can perform in-place. */
void PQCLEAN_MQDSS48_CLEAN_gf31_npack(unsigned char *out, const gf31 *in, unsigned int n);

#endif

+ 81
- 0
crypto_sign/mqdss-48/clean/mq.c View File

@@ -0,0 +1,81 @@
#include "mq.h"
#include "params.h"

/* Computes all products x_i * x_j, returns in reduced form */
inline static
void generate_quadratic_terms( gf31 *xij, const gf31 *x ) {
int i, j, k;
k = 0;
for (i = 0; i < N; i++) {
for (j = 0; j <= i; j++) {
xij[k] = PQCLEAN_MQDSS48_CLEAN_mod31((gf31)(x[i] * x[j]));
k++;
}
}
}

/* Computes all terms (x_i * y_j) + (x_j * y_i), returns in reduced form */
inline static
void generate_xiyj_p_xjyi_terms( gf31 *xij, const gf31 *x, const gf31 *y ) {
int i, j, k;
k = 0;
for (i = 0; i < N; i++) {
for (j = 0; j <= i; j++) {
xij[k] = PQCLEAN_MQDSS48_CLEAN_mod31((gf31)(x[i] * y[j] + x[j] * y[i]));
k++;
}
}
}

/* Evaluates the MQ function on a vector of N gf31 elements x (expected to be
in reduced 5-bit representation). Expects the coefficients in F to be in
signed representation (i.e. [-15, 15], packed bytewise).
Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS48_CLEAN_MQ(gf31 *fx, const gf31 *x, const signed char *F) {
int i, j;
gf31 _xij[N * (N + 1) >> 1];
int r[M] = {0};

generate_quadratic_terms(_xij, x);

for (i = 0; i < N; i += 2) {
for (j = 0; j < M; j++) {
r[j] += ((int)x[i]) * ((int)F[i * M + 2 * j]) +
((int)x[i + 1]) * ((int)F[i * M + 2 * j + 1]);
}
}

for (i = 0; i < (N * (N + 1)) >> 1; i += 2) {
for (j = 0; j < M; j++) {
r[j] += ((int)_xij[i]) * ((int)F[N * M + i * M + 2 * j]) +
((int)_xij[i + 1]) * ((int)F[N * M + i * M + 2 * j + 1]);
}
}

for (i = 0; i < M; i++) {
fx[i] = PQCLEAN_MQDSS48_CLEAN_mod31((gf31)((r[i] >> 15) + (r[i] & 0x7FFF)));
}
}

/* Evaluates the bilinear polar form of the MQ function (i.e. G) on a vector of
N gf31 elements x (expected to be in reduced 5-bit representation). Expects
the coefficients in F to be in signed representation (i.e. [-15, 15], packed
bytewise). Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS48_CLEAN_G(gf31 *fx, const gf31 *x, const gf31 *y, const signed char *F) {
int i, j;
gf31 _xij[N * (N + 1) >> 1];
int r[M] = {0};

generate_xiyj_p_xjyi_terms(_xij, x, y);

for (i = 0; i < (N * (N + 1)) >> 1; i += 2) {
for (j = 0; j < M; j++) {
r[j] += ((int)_xij[i]) * ((int)F[N * M + i * M + 2 * j]) +
((int)_xij[i + 1]) * ((int)F[N * M + i * M + 2 * j + 1]);
}
}

for (i = 0; i < M; i++) {
fx[i] = PQCLEAN_MQDSS48_CLEAN_mod31((gf31)((r[i] >> 15) + (r[i] & 0x7FFF)));
}
}

+ 18
- 0
crypto_sign/mqdss-48/clean/mq.h View File

@@ -0,0 +1,18 @@
#ifndef MQDSS_MQ_H
#define MQDSS_MQ_H

#include "gf31.h"

/* Evaluates the MQ function on a vector of N gf31 elements x (expected to be
in reduced 5-bit representation). Expects the coefficients in F to be in
signed representation (i.e. [-15, 15], packed bytewise).
Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS48_CLEAN_MQ(gf31 *fx, const gf31 *x, const signed char *F);

/* Evaluates the bilinear polar form of the MQ function (i.e. G) on a vector of
N gf31 elements x (expected to be in reduced 5-bit representation). Expects
the coefficients in F to be in signed representation (i.e. [-15, 15], packed
bytewise). Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS48_CLEAN_G(gf31 *fx, const gf31 *x, const gf31 *y, const signed char *F);

#endif

+ 25
- 0
crypto_sign/mqdss-48/clean/params.h View File

@@ -0,0 +1,25 @@
#ifndef MQDSS_PARAMS_H
#define MQDSS_PARAMS_H

#define N 48
#define M N
#define F_LEN (M * (((N * (N + 1)) >> 1) + N)) /* Number of elements in F */

#define ROUNDS 135

/* Number of bytes that N, M and F_LEN elements require when packed into a byte
array, 5-bit elements packed continuously. */
/* Assumes N and M to be multiples of 8 */
#define NPACKED_BYTES ((N * 5) >> 3)
#define MPACKED_BYTES ((M * 5) >> 3)
#define FPACKED_BYTES ((F_LEN * 5) >> 3)

#define HASH_BYTES 32
#define SEED_BYTES 16
#define PK_BYTES (SEED_BYTES + MPACKED_BYTES)
#define SK_BYTES SEED_BYTES

// R, sigma_0, ROUNDS * (t1, r{0,1}, e1, c, rho)
#define SIG_LEN (2 * HASH_BYTES + ROUNDS * (2*NPACKED_BYTES + MPACKED_BYTES + HASH_BYTES + HASH_BYTES))

#endif

+ 384
- 0
crypto_sign/mqdss-48/clean/sign.c View File

@@ -0,0 +1,384 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include "api.h"
#include "fips202.h"
#include "gf31.h"
#include "mq.h"
#include "params.h"
#include "randombytes.h"

/* Takes an array of len bytes and computes a hash digest.
This is used as a hash function in the Fiat-Shamir transform. */
static void H(unsigned char *out, const unsigned char *in, const size_t len) {
shake256(out, HASH_BYTES, in, len);
}

/* Takes two arrays of N packed elements and an array of M packed elements,
and computes a HASH_BYTES commitment. */
static void com_0(unsigned char *c,
const unsigned char *rho,
const unsigned char *inn, const unsigned char *inn2,
const unsigned char *inm) {
unsigned char buffer[HASH_BYTES + 2 * NPACKED_BYTES + MPACKED_BYTES];
memcpy(buffer, rho, HASH_BYTES);
memcpy(buffer + HASH_BYTES, inn, NPACKED_BYTES);
memcpy(buffer + HASH_BYTES + NPACKED_BYTES, inn2, NPACKED_BYTES);
memcpy(buffer + HASH_BYTES + 2 * NPACKED_BYTES, inm, MPACKED_BYTES);
shake256(c, HASH_BYTES, buffer, HASH_BYTES + 2 * NPACKED_BYTES + MPACKED_BYTES);
}

/* Takes an array of N packed elements and an array of M packed elements,
and computes a HASH_BYTES commitment. */
static void com_1(unsigned char *c,
const unsigned char *rho,
const unsigned char *inn, const unsigned char *inm) {
unsigned char buffer[HASH_BYTES + NPACKED_BYTES + MPACKED_BYTES];
memcpy(buffer, rho, HASH_BYTES);
memcpy(buffer + HASH_BYTES, inn, NPACKED_BYTES);
memcpy(buffer + HASH_BYTES + NPACKED_BYTES, inm, MPACKED_BYTES);
shake256(c, HASH_BYTES, buffer, HASH_BYTES + NPACKED_BYTES + MPACKED_BYTES);
}

/*
* Generates an MQDSS key pair.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk) {
signed char F[F_LEN];
unsigned char skbuf[SEED_BYTES * 2];
gf31 sk_gf31[N];
gf31 pk_gf31[M];

// Expand sk to obtain a seed for F and the secret input s.
// We also expand to obtain a value for sampling r0, t0 and e0 during
// signature generation, but that is not relevant here.
randombytes(sk, SEED_BYTES);
shake256(skbuf, SEED_BYTES * 2, sk, SEED_BYTES);

memcpy(pk, skbuf, SEED_BYTES);
PQCLEAN_MQDSS48_CLEAN_gf31_nrand_schar(F, F_LEN, pk, SEED_BYTES);
PQCLEAN_MQDSS48_CLEAN_gf31_nrand(sk_gf31, N, skbuf + SEED_BYTES, SEED_BYTES);
PQCLEAN_MQDSS48_CLEAN_MQ(pk_gf31, sk_gf31, F);
PQCLEAN_MQDSS48_CLEAN_vgf31_unique(pk_gf31, pk_gf31);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(pk + SEED_BYTES, pk_gf31, M);

return 0;
}

/**
* Returns an array containing a detached signature.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_signature(
uint8_t *sig, size_t *siglen,
const uint8_t *m, size_t mlen, const uint8_t *sk) {

signed char F[F_LEN];
unsigned char skbuf[SEED_BYTES * 4];
gf31 pk_gf31[M];
unsigned char pk[SEED_BYTES + MPACKED_BYTES];
// Concatenated for convenient hashing.
unsigned char D_sigma0_h0_sigma1[HASH_BYTES * 3 + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES)];
unsigned char *D = D_sigma0_h0_sigma1;
unsigned char *sigma0 = D_sigma0_h0_sigma1 + HASH_BYTES;
unsigned char *h0 = D_sigma0_h0_sigma1 + 2 * HASH_BYTES;
unsigned char *t1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES;
unsigned char *e1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES + ROUNDS * NPACKED_BYTES;
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];
unsigned char h1[((ROUNDS + 7) & ~7) >> 3];
unsigned char rnd_seed[HASH_BYTES + SEED_BYTES];
unsigned char rho[2 * ROUNDS * HASH_BYTES];
unsigned char *rho0 = rho;
unsigned char *rho1 = rho + ROUNDS * HASH_BYTES;
gf31 sk_gf31[N];
gf31 rnd[(2 * N + M) * ROUNDS]; // Concatenated for easy RNG.
gf31 *r0 = rnd;
gf31 *t0 = rnd + N * ROUNDS;
gf31 *e0 = rnd + 2 * N * ROUNDS;
gf31 r1[N * ROUNDS];
gf31 t1[N * ROUNDS];
gf31 e1[M * ROUNDS];
gf31 gx[M * ROUNDS];
unsigned char packbuf0[NPACKED_BYTES];
unsigned char packbuf1[NPACKED_BYTES];
unsigned char packbuf2[MPACKED_BYTES];
unsigned char c[HASH_BYTES * ROUNDS * 2];
gf31 alpha;
int alpha_count = 0;
int b;
int i, j;
uint64_t s_inc[26];

shake256(skbuf, SEED_BYTES * 4, sk, SEED_BYTES);

PQCLEAN_MQDSS48_CLEAN_gf31_nrand_schar(F, F_LEN, skbuf, SEED_BYTES);

shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, sk, SEED_BYTES);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(sig, HASH_BYTES, s_inc); // Compute R.

memcpy(pk, skbuf, SEED_BYTES);
PQCLEAN_MQDSS48_CLEAN_gf31_nrand(sk_gf31, N, skbuf + SEED_BYTES, SEED_BYTES);
PQCLEAN_MQDSS48_CLEAN_MQ(pk_gf31, sk_gf31, F);
PQCLEAN_MQDSS48_CLEAN_vgf31_unique(pk_gf31, pk_gf31);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(pk + SEED_BYTES, pk_gf31, M);

shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, pk, PK_BYTES);
shake256_inc_absorb(s_inc, sig, HASH_BYTES);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(D, HASH_BYTES, s_inc);

sig += HASH_BYTES; // Compensate for prefixed R.

memcpy(rnd_seed, skbuf + 2 * SEED_BYTES, SEED_BYTES);
memcpy(rnd_seed + SEED_BYTES, D, HASH_BYTES);
shake256(rho, 2 * ROUNDS * HASH_BYTES, rnd_seed, SEED_BYTES + HASH_BYTES);

memcpy(rnd_seed, skbuf + 3 * SEED_BYTES, SEED_BYTES);
memcpy(rnd_seed + SEED_BYTES, D, HASH_BYTES);
PQCLEAN_MQDSS48_CLEAN_gf31_nrand(rnd, (2 * N + M) * ROUNDS, rnd_seed, SEED_BYTES + HASH_BYTES);

for (i = 0; i < ROUNDS; i++) {
for (j = 0; j < N; j++) {
r1[j + i * N] = (gf31)(31 + sk_gf31[j] - r0[j + i * N]);
}
PQCLEAN_MQDSS48_CLEAN_G(gx + i * M, t0 + i * N, r1 + i * N, F);
}
for (i = 0; i < ROUNDS * M; i++) {
gx[i] = (gf31)(gx[i] + e0[i]);
}
for (i = 0; i < ROUNDS; i++) {
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf0, r0 + i * N, N);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf1, t0 + i * N, N);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf2, e0 + i * M, M);
com_0(c + HASH_BYTES * (2 * i + 0), rho0 + i * HASH_BYTES, packbuf0, packbuf1, packbuf2);
PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(r1 + i * N, r1 + i * N);
PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(gx + i * M, gx + i * M);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf0, r1 + i * N, N);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf1, gx + i * M, M);
com_1(c + HASH_BYTES * (2 * i + 1), rho1 + i * HASH_BYTES, packbuf0, packbuf1);
}

H(sigma0, c, HASH_BYTES * ROUNDS * 2); // Compute sigma_0.
shake256_absorb(shakestate, D_sigma0_h0_sigma1, 2 * HASH_BYTES);
shake256_squeezeblocks(shakeblock, 1, shakestate);

memcpy(h0, shakeblock, HASH_BYTES);

memcpy(sig, sigma0, HASH_BYTES);
sig += HASH_BYTES; // Compensate for sigma_0.

for (i = 0; i < ROUNDS; i++) {
do {
alpha = shakeblock[alpha_count] & 31;
alpha_count++;
if (alpha_count == SHAKE256_RATE) {
alpha_count = 0;
shake256_squeezeblocks(shakeblock, 1, shakestate);
}
} while (alpha == 31);
for (j = 0; j < N; j++) {
t1[i * N + j] = (gf31)(alpha * r0[j + i * N] - t0[j + i * N] + 31);
}
PQCLEAN_MQDSS48_CLEAN_MQ(e1 + i * M, r0 + i * N, F);
for (j = 0; j < N; j++) {
e1[i * N + j] = (gf31)(alpha * e1[j + i * M] - e0[j + i * M] + 31);
}
PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(t1 + i * N, t1 + i * N);
PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(e1 + i * N, e1 + i * N);
}
PQCLEAN_MQDSS48_CLEAN_gf31_npack(t1packed, t1, N * ROUNDS);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(e1packed, e1, M * ROUNDS);

memcpy(sig, t1packed, NPACKED_BYTES * ROUNDS);
sig += NPACKED_BYTES * ROUNDS;
memcpy(sig, e1packed, MPACKED_BYTES * ROUNDS);
sig += MPACKED_BYTES * ROUNDS;

shake256(h1, ((ROUNDS + 7) & ~7) >> 3, D_sigma0_h0_sigma1, 3 * HASH_BYTES + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES));

for (i = 0; i < ROUNDS; i++) {
b = (h1[(i >> 3)] >> (i & 7)) & 1;
if (b == 0) {
PQCLEAN_MQDSS48_CLEAN_gf31_npack(sig, r0 + i * N, N);
} else if (b == 1) {
PQCLEAN_MQDSS48_CLEAN_gf31_npack(sig, r1 + i * N, N);
}
memcpy(sig + NPACKED_BYTES, c + HASH_BYTES * (2 * i + (1 - b)), HASH_BYTES);
memcpy(sig + NPACKED_BYTES + HASH_BYTES, rho + (i + b * ROUNDS) * HASH_BYTES, HASH_BYTES);
sig += NPACKED_BYTES + 2 * HASH_BYTES;
}

*siglen = SIG_LEN;
return 0;
}

/**
* Verifies a detached signature and message under a given public key.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_verify(
const uint8_t *sig, size_t siglen,
const uint8_t *m, size_t mlen, const uint8_t *pk) {

gf31 r[N];
gf31 t[N];
gf31 e[M];
signed char F[F_LEN];
gf31 pk_gf31[M];
// Concatenated for convenient hashing.
unsigned char D_sigma0_h0_sigma1[HASH_BYTES * 3 + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES)];
unsigned char *D = D_sigma0_h0_sigma1;
unsigned char *sigma0 = D_sigma0_h0_sigma1 + HASH_BYTES;
unsigned char *h0 = D_sigma0_h0_sigma1 + 2 * HASH_BYTES;
unsigned char *t1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES;
unsigned char *e1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES + ROUNDS * NPACKED_BYTES;
unsigned char h1[((ROUNDS + 7) & ~7) >> 3];
unsigned char c[HASH_BYTES * ROUNDS * 2];
memset(c, 0, HASH_BYTES * 2);
gf31 x[N];
gf31 y[M];
gf31 z[M];
unsigned char packbuf0[NPACKED_BYTES];
unsigned char packbuf1[MPACKED_BYTES];
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];
int i, j;
gf31 alpha;
int alpha_count = 0;
int b;
uint64_t s_inc[26];

if (siglen != SIG_LEN) {
return -1;
}

shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, pk, PK_BYTES);
shake256_inc_absorb(s_inc, sig, HASH_BYTES);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(D, HASH_BYTES, s_inc);

sig += HASH_BYTES;

PQCLEAN_MQDSS48_CLEAN_gf31_nrand_schar(F, F_LEN, pk, SEED_BYTES);
pk += SEED_BYTES;
PQCLEAN_MQDSS48_CLEAN_gf31_nunpack(pk_gf31, pk, M);

memcpy(sigma0, sig, HASH_BYTES);

shake256_absorb(shakestate, D_sigma0_h0_sigma1, 2 * HASH_BYTES);
shake256_squeezeblocks(shakeblock, 1, shakestate);

memcpy(h0, shakeblock, HASH_BYTES);

sig += HASH_BYTES;

memcpy(t1packed, sig, ROUNDS * NPACKED_BYTES);
sig += ROUNDS * NPACKED_BYTES;
memcpy(e1packed, sig, ROUNDS * MPACKED_BYTES);
sig += ROUNDS * MPACKED_BYTES;

shake256(h1, ((ROUNDS + 7) & ~7) >> 3, D_sigma0_h0_sigma1, 3 * HASH_BYTES + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES));

for (i = 0; i < ROUNDS; i++) {
do {
alpha = shakeblock[alpha_count] & 31;
alpha_count++;
if (alpha_count == SHAKE256_RATE) {
alpha_count = 0;
shake256_squeezeblocks(shakeblock, 1, shakestate);
}
} while (alpha == 31);
b = (h1[(i >> 3)] >> (i & 7)) & 1;

PQCLEAN_MQDSS48_CLEAN_gf31_nunpack(r, sig, N);
PQCLEAN_MQDSS48_CLEAN_gf31_nunpack(t, t1packed + NPACKED_BYTES * i, N);
PQCLEAN_MQDSS48_CLEAN_gf31_nunpack(e, e1packed + MPACKED_BYTES * i, M);

if (b == 0) {
PQCLEAN_MQDSS48_CLEAN_MQ(y, r, F);
for (j = 0; j < N; j++) {
x[j] = (gf31)(alpha * r[j] - t[j] + 31);
}
for (j = 0; j < N; j++) {
y[j] = (gf31)(alpha * y[j] - e[j] + 31);
}
PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(x, x);
PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(y, y);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf0, x, N);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf1, y, M);
com_0(c + HASH_BYTES * (2 * i + 0), sig + HASH_BYTES + NPACKED_BYTES, sig, packbuf0, packbuf1);
} else {
PQCLEAN_MQDSS48_CLEAN_MQ(y, r, F);
PQCLEAN_MQDSS48_CLEAN_G(z, t, r, F);
for (j = 0; j < N; j++) {
y[j] = (gf31)(alpha * (31 + pk_gf31[j] - y[j]) - z[j] - e[j] + 62);
}
PQCLEAN_MQDSS48_CLEAN_vgf31_shorten_unique(y, y);
PQCLEAN_MQDSS48_CLEAN_gf31_npack(packbuf0, y, M);
com_1(c + HASH_BYTES * (2 * i + 1), sig + HASH_BYTES + NPACKED_BYTES, sig, packbuf0);
}
memcpy(c + HASH_BYTES * (2 * i + (1 - b)), sig + NPACKED_BYTES, HASH_BYTES);
sig += NPACKED_BYTES + 2 * HASH_BYTES;
}

H(c, c, HASH_BYTES * ROUNDS * 2);
if (memcmp(c, sigma0, HASH_BYTES) != 0) {
return -1;
}

return 0;
}

/**
* Returns an array containing the signature followed by the message.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign(
uint8_t *sm, size_t *smlen,
const uint8_t *m, size_t mlen, const uint8_t *sk) {
size_t siglen;

PQCLEAN_MQDSS48_CLEAN_crypto_sign_signature(
sm, &siglen, m, mlen, sk);

memmove(sm + SIG_LEN, m, mlen);
*smlen = siglen + mlen;

return 0;
}

/**
* Verifies a given signature-message pair under a given public key.
*/
int PQCLEAN_MQDSS48_CLEAN_crypto_sign_open(
uint8_t *m, size_t *mlen,
const uint8_t *sm, size_t smlen, const uint8_t *pk) {
/* The API caller does not necessarily know what size a signature should be
but MQDSS signatures are always exactly SIG_LEN. */
if (smlen < SIG_LEN) {
memset(m, 0, smlen);
*mlen = 0;
return -1;
}

*mlen = smlen - SIG_LEN;

if (PQCLEAN_MQDSS48_CLEAN_crypto_sign_verify(
sm, SIG_LEN, sm + SIG_LEN, *mlen, pk)) {
memset(m, 0, smlen);
*mlen = 0;
return -1;
}

/* If verification was successful, move the message to the right place. */
memmove(m, sm + SIG_LEN, *mlen);

return 0;
}

+ 16
- 0
crypto_sign/mqdss-64/META.yml View File

@@ -0,0 +1,16 @@
name: MQDSS-64
type: signature
claimed-nist-level: 3
length-public-key: 64
length-secret-key: 24
length-signature: 43728
testvectors-sha256: 1edd33ca64b14f60f153b84dd25c7064cfa9b7dbf1bb5c4296f343377cb0c864
principal-submitter: Simona Samardjiska
auxiliary-submitters:
- Ming-Shing Chen
- Andreas Hülsing
- Joost Rijneveld
- Peter Schwabe
implementations:
- name: clean
version: https://github.com/joostrijneveld/MQDSS/commit/0c64d4d67a37051c1299a3049a5bb8984ca27ecc

+ 116
- 0
crypto_sign/mqdss-64/clean/LICENSE View File

@@ -0,0 +1,116 @@
CC0 1.0 Universal

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator and
subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").

Certain owners wish to permanently relinquish those rights to a Work for the
purpose of contributing to a commons of creative, cultural and scientific
works ("Commons") that the public can reliably and without fear of later
claims of infringement build upon, modify, incorporate in other works, reuse
and redistribute as freely as possible in any form whatsoever and for any
purposes, including without limitation commercial purposes. These owners may
contribute to the Commons to promote the ideal of a free culture and the
further production of creative, cultural and scientific works, or to gain
reputation or greater distribution for their Work in part through the use and
efforts of others.

For these and/or other purposes and motivations, and without any expectation
of additional consideration or compensation, the person associating CC0 with a
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
and publicly distribute the Work under its terms, with knowledge of his or her
Copyright and Related Rights in the Work and the meaning and intended legal
effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not limited
to, the following:

i. the right to reproduce, adapt, distribute, perform, display, communicate,
and translate a Work;

ii. moral rights retained by the original author(s) and/or performer(s);

iii. publicity and privacy rights pertaining to a person's image or likeness
depicted in a Work;

iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;

v. rights protecting the extraction, dissemination, use and reuse of data in
a Work;

vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation thereof,
including any amended or successor version of such directive); and

vii. other similar, equivalent or corresponding rights throughout the world
based on applicable law or treaty, and any national implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention of,
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
and Related Rights and associated claims and causes of action, whether now
known or unknown (including existing as well as future claims and causes of
action), in the Work (i) in all territories worldwide, (ii) for the maximum
duration provided by applicable law or treaty (including future time
extensions), (iii) in any current or future medium and for any number of
copies, and (iv) for any purpose whatsoever, including without limitation
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
the Waiver for the benefit of each member of the public at large and to the
detriment of Affirmer's heirs and successors, fully intending that such Waiver
shall not be subject to revocation, rescission, cancellation, termination, or
any other legal or equitable action to disrupt the quiet enjoyment of the Work
by the public as contemplated by Affirmer's express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason be
judged legally invalid or ineffective under applicable law, then the Waiver
shall be preserved to the maximum extent permitted taking into account
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
is so judged Affirmer hereby grants to each affected person a royalty-free,
non transferable, non sublicensable, non exclusive, irrevocable and
unconditional license to exercise Affirmer's Copyright and Related Rights in
the Work (i) in all territories worldwide, (ii) for the maximum duration
provided by applicable law or treaty (including future time extensions), (iii)
in any current or future medium and for any number of copies, and (iv) for any
purpose whatsoever, including without limitation commercial, advertising or
promotional purposes (the "License"). The License shall be deemed effective as
of the date CC0 was applied by Affirmer to the Work. Should any part of the
License for any reason be judged legally invalid or ineffective under
applicable law, such partial invalidity or ineffectiveness shall not
invalidate the remainder of the License, and in such case Affirmer hereby
affirms that he or she will not (i) exercise any of his or her remaining
Copyright and Related Rights in the Work or (ii) assert any associated claims
and causes of action with respect to the Work, in either case contrary to
Affirmer's express Statement of Purpose.

4. Limitations and Disclaimers.

a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.

b. Affirmer offers the Work as-is and makes no representations or warranties
of any kind concerning the Work, express, implied, statutory or otherwise,
including without limitation warranties of title, merchantability, fitness
for a particular purpose, non infringement, or the absence of latent or
other defects, accuracy, or the present or absence of errors, whether or not
discoverable, all to the greatest extent permissible under applicable law.

c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without limitation
any person's Copyright and Related Rights in the Work. Further, Affirmer
disclaims responsibility for obtaining any necessary consents, permissions
or other rights required for any use of the Work.

d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to this
CC0 or use of the Work.

For more information, please see
<http://creativecommons.org/publicdomain/zero/1.0/>

+ 20
- 0
crypto_sign/mqdss-64/clean/Makefile View File

@@ -0,0 +1,20 @@
# This Makefile can be used with GNU Make or BSD Make

LIB=libmqdss-64_clean.a

HEADERS = params.h gf31.h mq.h api.h
OBJECTS = gf31.o mq.o sign.o

CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS)

all: $(LIB)

%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<

$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)

clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

+ 19
- 0
crypto_sign/mqdss-64/clean/Makefile.Microsoft_nmake View File

@@ -0,0 +1,19 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake

LIBRARY=libmqdss-64_clean.lib
OBJECTS=gf31.obj mq.obj sign.obj

CFLAGS=/nologo /I ..\..\..\common /W4 /WX

all: $(LIBRARY)

# Make sure objects are recompiled if headers change.
$(OBJECTS): *.h

$(LIBRARY): $(OBJECTS)
LIB.EXE /NOLOGO /WX /OUT:$@ $**

clean:
-DEL $(OBJECTS)
-DEL $(LIBRARY)

+ 47
- 0
crypto_sign/mqdss-64/clean/api.h View File

@@ -0,0 +1,47 @@
#ifndef PQCLEAN_MQDSS64_CLEAN_API_H
#define PQCLEAN_MQDSS64_CLEAN_API_H

#include <stddef.h>
#include <stdint.h>

#define PQCLEAN_MQDSS64_CLEAN_CRYPTO_ALGNAME "MQDSS-64"

#define PQCLEAN_MQDSS64_CLEAN_CRYPTO_SECRETKEYBYTES 24
#define PQCLEAN_MQDSS64_CLEAN_CRYPTO_PUBLICKEYBYTES 64
#define PQCLEAN_MQDSS64_CLEAN_CRYPTO_BYTES 43728

/*
* Generates an MQDSS key pair.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_keypair(
uint8_t *pk, uint8_t *sk);

/**
* Returns an array containing a detached signature.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_signature(
uint8_t *sig, size_t *siglen,
const uint8_t *m, size_t mlen, const uint8_t *sk);

/**
* Verifies a detached signature and message under a given public key.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_verify(
const uint8_t *sig, size_t siglen,
const uint8_t *m, size_t mlen, const uint8_t *pk);

/**
* Returns an array containing the signature followed by the message.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign(
uint8_t *sm, size_t *smlen,
const uint8_t *m, size_t mlen, const uint8_t *sk);

/**
* Verifies a given signature-message pair under a given public key.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_open(
uint8_t *m, size_t *mlen,
const uint8_t *sm, size_t smlen, const uint8_t *pk);

#endif

+ 133
- 0
crypto_sign/mqdss-64/clean/gf31.c View File

@@ -0,0 +1,133 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include "fips202.h"
#include "gf31.h"
#include "params.h"

/* This performs a full unique reduction mod 13 on x; x can be any unsigned
16-bit integer (i.e. in the range [0, 65535]) */
gf31 PQCLEAN_MQDSS64_CLEAN_mod31(gf31 x) {
gf31 t;

t = (gf31)(x & 31);
x >>= 5;
t = (gf31)(t + (x & 31));
x >>= 5;
t = (gf31)(t + (x & 31));
x >>= 5;
t = (gf31)(t + (x & 31));

t = (gf31)((t >> 5) + (t & 31));
t = (gf31)((t >> 5) + (t & 31));
return (gf31)((t != 31) * t);
}

/* Given a vector of N elements in the range [0, 31], this reduces the elements
to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS64_CLEAN_vgf31_unique(gf31 *out, const gf31 *in) {
int i;

for (i = 0; i < N; i++) {
out[i] = (gf31)((1 - (in[i] == 31)) * in[i]);
}
}

/* Given a vector of 16-bit integers (i.e. in [0, 65535], this reduces the
elements to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(gf31 *out, const gf31 *in) {
int i;

for (i = 0; i < N; i++) {
out[i] = PQCLEAN_MQDSS64_CLEAN_mod31(in[i]);
}
}

/* Given a seed, samples len gf31 elements (in the range [0, 30]), and places
them in a vector of 16-bit elements */
void PQCLEAN_MQDSS64_CLEAN_gf31_nrand(gf31 *out, int len, const unsigned char *seed, size_t seedlen) {
int i = 0, j;
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];

shake256_absorb(shakestate, seed, seedlen);

while (i < len) {
shake256_squeezeblocks(shakeblock, 1, shakestate);
for (j = 0; j < SHAKE256_RATE && i < len; j++) {
if ((shakeblock[j] & 31) != 31) {
out[i] = (shakeblock[j] & 31);
i++;
}
}
}
}

/* Given a seed, samples len gf31 elements, transposed into unsigned range,
i.e. in the range [-15, 15], and places them in an array of 8-bit integers.
This is used for the expansion of F, which wants packed elements. */
void PQCLEAN_MQDSS64_CLEAN_gf31_nrand_schar(signed char *out, int len, const unsigned char *seed, size_t seedlen) {
int i = 0, j;
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];

shake256_absorb(shakestate, seed, seedlen);

while (i < len) {
shake256_squeezeblocks(shakeblock, 1, shakestate);
for (j = 0; j < SHAKE256_RATE && i < len; j++) {
if ((shakeblock[j] & 31) != 31) {
out[i] = (signed char)(((signed char)shakeblock[j] & 31) - 15);
i++;
}
}
}
}

/* Unpacks an array of packed GF31 elements to one element per gf31.
Assumes that there is sufficient empty space available at the end of the
array to unpack. Can perform in-place. */
void PQCLEAN_MQDSS64_CLEAN_gf31_nunpack(gf31 *out, const unsigned char *in, unsigned int n) {
size_t i;
unsigned int j = ((n * 5) >> 3) - 1;
unsigned int d = 0;

for (i = n; i > 0; i--) {
out[i - 1] = (gf31)((in[j] >> d) & 31);
d += 5;
if (d > 8) {
d -= 8;
j--;
out[i - 1] = (gf31)(out[i - 1] ^ ((in[j] << (5 - d)) & 31));
}
}
}

/* Packs an array of GF31 elements from gf31's to concatenated 5-bit values.
Assumes that there is sufficient space available to unpack.
Can perform in-place. */
void PQCLEAN_MQDSS64_CLEAN_gf31_npack(unsigned char *out, const gf31 *in, unsigned int n) {
unsigned int i = 0;
unsigned int j;
int d = 3;

for (j = 0; j < n; j++) {
assert(in[j] < 31);
}

/* There will be ceil(5n / 8) output blocks */
memset(out, 0, ((5 * n + 7) & (unsigned int)~7) >> 3);

for (j = 0; j < n; j++) {
if (d < 0) {
d += 8;
out[i] = (unsigned char)((out[i] & (255 << (d - 3))) |
((in[j] >> (8 - d)) & ~(255 << (d - 3))));
i++;
}
out[i] = (unsigned char)((out[i] & ~(31 << d)) | ((in[j] << d) & (31 << d)));
d -= 5;
}
}

+ 40
- 0
crypto_sign/mqdss-64/clean/gf31.h View File

@@ -0,0 +1,40 @@
#ifndef MQDSS_GF31_H
#define MQDSS_GF31_H

#include <stddef.h>
#include <stdint.h>

typedef uint16_t gf31;

/* This performs a full unique reduction mod 13 on x; x can be any unsigned
16-bit integer (i.e. in the range [0, 65535]) */
gf31 PQCLEAN_MQDSS64_CLEAN_mod31(gf31 x);

/* Given a vector of elements in the range [0, 31], this reduces the elements
to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS64_CLEAN_vgf31_unique(gf31 *out, const gf31 *in);

/* Given a vector of 16-bit integers (i.e. in [0, 65535], this reduces the
elements to the range [0, 30] by mapping 31 to 0 (i.e reduction mod 31) */
void PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(gf31 *out, const gf31 *in);

/* Given a seed, samples len gf31 elements (in the range [0, 30]), and places
them in a vector of 16-bit elements */
void PQCLEAN_MQDSS64_CLEAN_gf31_nrand(gf31 *out, int len, const unsigned char *seed, size_t seedlen);

/* Given a seed, samples len gf31 elements, transposed into unsigned range,
i.e. in the range [-15, 15], and places them in an array of 8-bit integers.
This is used for the expansion of F, which wants packed elements. */
void PQCLEAN_MQDSS64_CLEAN_gf31_nrand_schar(signed char *out, int len, const unsigned char *seed, size_t seedlen);

/* Unpacks an array of packed GF31 elements to one element per gf31.
Assumes that there is sufficient empty space available at the end of the
array to unpack. Can perform in-place. */
void PQCLEAN_MQDSS64_CLEAN_gf31_nunpack(gf31 *out, const unsigned char *in, unsigned int n);

/* Packs an array of GF31 elements from gf31's to concatenated 5-bit values.
Assumes that there is sufficient space available to unpack.
Can perform in-place. */
void PQCLEAN_MQDSS64_CLEAN_gf31_npack(unsigned char *out, const gf31 *in, unsigned int n);

#endif

+ 81
- 0
crypto_sign/mqdss-64/clean/mq.c View File

@@ -0,0 +1,81 @@
#include "mq.h"
#include "params.h"

/* Computes all products x_i * x_j, returns in reduced form */
inline static
void generate_quadratic_terms( gf31 *xij, const gf31 *x ) {
int i, j, k;
k = 0;
for (i = 0; i < N; i++) {
for (j = 0; j <= i; j++) {
xij[k] = PQCLEAN_MQDSS64_CLEAN_mod31((gf31)(x[i] * x[j]));
k++;
}
}
}

/* Computes all terms (x_i * y_j) + (x_j * y_i), returns in reduced form */
inline static
void generate_xiyj_p_xjyi_terms( gf31 *xij, const gf31 *x, const gf31 *y ) {
int i, j, k;
k = 0;
for (i = 0; i < N; i++) {
for (j = 0; j <= i; j++) {
xij[k] = PQCLEAN_MQDSS64_CLEAN_mod31((gf31)(x[i] * y[j] + x[j] * y[i]));
k++;
}
}
}

/* Evaluates the MQ function on a vector of N gf31 elements x (expected to be
in reduced 5-bit representation). Expects the coefficients in F to be in
signed representation (i.e. [-15, 15], packed bytewise).
Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS64_CLEAN_MQ(gf31 *fx, const gf31 *x, const signed char *F) {
int i, j;
gf31 _xij[N * (N + 1) >> 1];
int r[M] = {0};

generate_quadratic_terms(_xij, x);

for (i = 0; i < N; i += 2) {
for (j = 0; j < M; j++) {
r[j] += ((int)x[i]) * ((int)F[i * M + 2 * j]) +
((int)x[i + 1]) * ((int)F[i * M + 2 * j + 1]);
}
}

for (i = 0; i < (N * (N + 1)) >> 1; i += 2) {
for (j = 0; j < M; j++) {
r[j] += ((int)_xij[i]) * ((int)F[N * M + i * M + 2 * j]) +
((int)_xij[i + 1]) * ((int)F[N * M + i * M + 2 * j + 1]);
}
}

for (i = 0; i < M; i++) {
fx[i] = PQCLEAN_MQDSS64_CLEAN_mod31((gf31)((r[i] >> 15) + (r[i] & 0x7FFF)));
}
}

/* Evaluates the bilinear polar form of the MQ function (i.e. G) on a vector of
N gf31 elements x (expected to be in reduced 5-bit representation). Expects
the coefficients in F to be in signed representation (i.e. [-15, 15], packed
bytewise). Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS64_CLEAN_G(gf31 *fx, const gf31 *x, const gf31 *y, const signed char *F) {
int i, j;
gf31 _xij[N * (N + 1) >> 1];
int r[M] = {0};

generate_xiyj_p_xjyi_terms(_xij, x, y);

for (i = 0; i < (N * (N + 1)) >> 1; i += 2) {
for (j = 0; j < M; j++) {
r[j] += ((int)_xij[i]) * ((int)F[N * M + i * M + 2 * j]) +
((int)_xij[i + 1]) * ((int)F[N * M + i * M + 2 * j + 1]);
}
}

for (i = 0; i < M; i++) {
fx[i] = PQCLEAN_MQDSS64_CLEAN_mod31((gf31)((r[i] >> 15) + (r[i] & 0x7FFF)));
}
}

+ 18
- 0
crypto_sign/mqdss-64/clean/mq.h View File

@@ -0,0 +1,18 @@
#ifndef MQDSS_MQ_H
#define MQDSS_MQ_H

#include "gf31.h"

/* Evaluates the MQ function on a vector of N gf31 elements x (expected to be
in reduced 5-bit representation). Expects the coefficients in F to be in
signed representation (i.e. [-15, 15], packed bytewise).
Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS64_CLEAN_MQ(gf31 *fx, const gf31 *x, const signed char *F);

/* Evaluates the bilinear polar form of the MQ function (i.e. G) on a vector of
N gf31 elements x (expected to be in reduced 5-bit representation). Expects
the coefficients in F to be in signed representation (i.e. [-15, 15], packed
bytewise). Outputs M gf31 elements in unique 16-bit representation as fx. */
void PQCLEAN_MQDSS64_CLEAN_G(gf31 *fx, const gf31 *x, const gf31 *y, const signed char *F);

#endif

+ 25
- 0
crypto_sign/mqdss-64/clean/params.h View File

@@ -0,0 +1,25 @@
#ifndef MQDSS_PARAMS_H
#define MQDSS_PARAMS_H

#define N 64
#define M N
#define F_LEN (M * (((N * (N + 1)) >> 1) + N)) /* Number of elements in F */

#define ROUNDS 202

/* Number of bytes that N, M and F_LEN elements require when packed into a byte
array, 5-bit elements packed continuously. */
/* Assumes N and M to be multiples of 8 */
#define NPACKED_BYTES ((N * 5) >> 3)
#define MPACKED_BYTES ((M * 5) >> 3)
#define FPACKED_BYTES ((F_LEN * 5) >> 3)

#define HASH_BYTES 48
#define SEED_BYTES 24
#define PK_BYTES (SEED_BYTES + MPACKED_BYTES)
#define SK_BYTES SEED_BYTES

// R, sigma_0, ROUNDS * (t1, r{0,1}, e1, c, rho)
#define SIG_LEN (2 * HASH_BYTES + ROUNDS * (2*NPACKED_BYTES + MPACKED_BYTES + HASH_BYTES + HASH_BYTES))

#endif

+ 384
- 0
crypto_sign/mqdss-64/clean/sign.c View File

@@ -0,0 +1,384 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include "api.h"
#include "fips202.h"
#include "gf31.h"
#include "mq.h"
#include "params.h"
#include "randombytes.h"

/* Takes an array of len bytes and computes a hash digest.
This is used as a hash function in the Fiat-Shamir transform. */
static void H(unsigned char *out, const unsigned char *in, const size_t len) {
shake256(out, HASH_BYTES, in, len);
}

/* Takes two arrays of N packed elements and an array of M packed elements,
and computes a HASH_BYTES commitment. */
static void com_0(unsigned char *c,
const unsigned char *rho,
const unsigned char *inn, const unsigned char *inn2,
const unsigned char *inm) {
unsigned char buffer[HASH_BYTES + 2 * NPACKED_BYTES + MPACKED_BYTES];
memcpy(buffer, rho, HASH_BYTES);
memcpy(buffer + HASH_BYTES, inn, NPACKED_BYTES);
memcpy(buffer + HASH_BYTES + NPACKED_BYTES, inn2, NPACKED_BYTES);
memcpy(buffer + HASH_BYTES + 2 * NPACKED_BYTES, inm, MPACKED_BYTES);
shake256(c, HASH_BYTES, buffer, HASH_BYTES + 2 * NPACKED_BYTES + MPACKED_BYTES);
}

/* Takes an array of N packed elements and an array of M packed elements,
and computes a HASH_BYTES commitment. */
static void com_1(unsigned char *c,
const unsigned char *rho,
const unsigned char *inn, const unsigned char *inm) {
unsigned char buffer[HASH_BYTES + NPACKED_BYTES + MPACKED_BYTES];
memcpy(buffer, rho, HASH_BYTES);
memcpy(buffer + HASH_BYTES, inn, NPACKED_BYTES);
memcpy(buffer + HASH_BYTES + NPACKED_BYTES, inm, MPACKED_BYTES);
shake256(c, HASH_BYTES, buffer, HASH_BYTES + NPACKED_BYTES + MPACKED_BYTES);
}

/*
* Generates an MQDSS key pair.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_keypair(uint8_t *pk, uint8_t *sk) {
signed char F[F_LEN];
unsigned char skbuf[SEED_BYTES * 2];
gf31 sk_gf31[N];
gf31 pk_gf31[M];

// Expand sk to obtain a seed for F and the secret input s.
// We also expand to obtain a value for sampling r0, t0 and e0 during
// signature generation, but that is not relevant here.
randombytes(sk, SEED_BYTES);
shake256(skbuf, SEED_BYTES * 2, sk, SEED_BYTES);

memcpy(pk, skbuf, SEED_BYTES);
PQCLEAN_MQDSS64_CLEAN_gf31_nrand_schar(F, F_LEN, pk, SEED_BYTES);
PQCLEAN_MQDSS64_CLEAN_gf31_nrand(sk_gf31, N, skbuf + SEED_BYTES, SEED_BYTES);
PQCLEAN_MQDSS64_CLEAN_MQ(pk_gf31, sk_gf31, F);
PQCLEAN_MQDSS64_CLEAN_vgf31_unique(pk_gf31, pk_gf31);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(pk + SEED_BYTES, pk_gf31, M);

return 0;
}

/**
* Returns an array containing a detached signature.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_signature(
uint8_t *sig, size_t *siglen,
const uint8_t *m, size_t mlen, const uint8_t *sk) {

signed char F[F_LEN];
unsigned char skbuf[SEED_BYTES * 4];
gf31 pk_gf31[M];
unsigned char pk[SEED_BYTES + MPACKED_BYTES];
// Concatenated for convenient hashing.
unsigned char D_sigma0_h0_sigma1[HASH_BYTES * 3 + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES)];
unsigned char *D = D_sigma0_h0_sigma1;
unsigned char *sigma0 = D_sigma0_h0_sigma1 + HASH_BYTES;
unsigned char *h0 = D_sigma0_h0_sigma1 + 2 * HASH_BYTES;
unsigned char *t1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES;
unsigned char *e1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES + ROUNDS * NPACKED_BYTES;
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];
unsigned char h1[((ROUNDS + 7) & ~7) >> 3];
unsigned char rnd_seed[HASH_BYTES + SEED_BYTES];
unsigned char rho[2 * ROUNDS * HASH_BYTES];
unsigned char *rho0 = rho;
unsigned char *rho1 = rho + ROUNDS * HASH_BYTES;
gf31 sk_gf31[N];
gf31 rnd[(2 * N + M) * ROUNDS]; // Concatenated for easy RNG.
gf31 *r0 = rnd;
gf31 *t0 = rnd + N * ROUNDS;
gf31 *e0 = rnd + 2 * N * ROUNDS;
gf31 r1[N * ROUNDS];
gf31 t1[N * ROUNDS];
gf31 e1[M * ROUNDS];
gf31 gx[M * ROUNDS];
unsigned char packbuf0[NPACKED_BYTES];
unsigned char packbuf1[NPACKED_BYTES];
unsigned char packbuf2[MPACKED_BYTES];
unsigned char c[HASH_BYTES * ROUNDS * 2];
gf31 alpha;
int alpha_count = 0;
int b;
int i, j;
uint64_t s_inc[26];

shake256(skbuf, SEED_BYTES * 4, sk, SEED_BYTES);

PQCLEAN_MQDSS64_CLEAN_gf31_nrand_schar(F, F_LEN, skbuf, SEED_BYTES);

shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, sk, SEED_BYTES);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(sig, HASH_BYTES, s_inc); // Compute R.

memcpy(pk, skbuf, SEED_BYTES);
PQCLEAN_MQDSS64_CLEAN_gf31_nrand(sk_gf31, N, skbuf + SEED_BYTES, SEED_BYTES);
PQCLEAN_MQDSS64_CLEAN_MQ(pk_gf31, sk_gf31, F);
PQCLEAN_MQDSS64_CLEAN_vgf31_unique(pk_gf31, pk_gf31);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(pk + SEED_BYTES, pk_gf31, M);

shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, pk, PK_BYTES);
shake256_inc_absorb(s_inc, sig, HASH_BYTES);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(D, HASH_BYTES, s_inc);

sig += HASH_BYTES; // Compensate for prefixed R.

memcpy(rnd_seed, skbuf + 2 * SEED_BYTES, SEED_BYTES);
memcpy(rnd_seed + SEED_BYTES, D, HASH_BYTES);
shake256(rho, 2 * ROUNDS * HASH_BYTES, rnd_seed, SEED_BYTES + HASH_BYTES);

memcpy(rnd_seed, skbuf + 3 * SEED_BYTES, SEED_BYTES);
memcpy(rnd_seed + SEED_BYTES, D, HASH_BYTES);
PQCLEAN_MQDSS64_CLEAN_gf31_nrand(rnd, (2 * N + M) * ROUNDS, rnd_seed, SEED_BYTES + HASH_BYTES);

for (i = 0; i < ROUNDS; i++) {
for (j = 0; j < N; j++) {
r1[j + i * N] = (gf31)(31 + sk_gf31[j] - r0[j + i * N]);
}
PQCLEAN_MQDSS64_CLEAN_G(gx + i * M, t0 + i * N, r1 + i * N, F);
}
for (i = 0; i < ROUNDS * M; i++) {
gx[i] = (gf31)(gx[i] + e0[i]);
}
for (i = 0; i < ROUNDS; i++) {
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf0, r0 + i * N, N);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf1, t0 + i * N, N);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf2, e0 + i * M, M);
com_0(c + HASH_BYTES * (2 * i + 0), rho0 + i * HASH_BYTES, packbuf0, packbuf1, packbuf2);
PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(r1 + i * N, r1 + i * N);
PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(gx + i * M, gx + i * M);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf0, r1 + i * N, N);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf1, gx + i * M, M);
com_1(c + HASH_BYTES * (2 * i + 1), rho1 + i * HASH_BYTES, packbuf0, packbuf1);
}

H(sigma0, c, HASH_BYTES * ROUNDS * 2); // Compute sigma_0.
shake256_absorb(shakestate, D_sigma0_h0_sigma1, 2 * HASH_BYTES);
shake256_squeezeblocks(shakeblock, 1, shakestate);

memcpy(h0, shakeblock, HASH_BYTES);

memcpy(sig, sigma0, HASH_BYTES);
sig += HASH_BYTES; // Compensate for sigma_0.

for (i = 0; i < ROUNDS; i++) {
do {
alpha = shakeblock[alpha_count] & 31;
alpha_count++;
if (alpha_count == SHAKE256_RATE) {
alpha_count = 0;
shake256_squeezeblocks(shakeblock, 1, shakestate);
}
} while (alpha == 31);
for (j = 0; j < N; j++) {
t1[i * N + j] = (gf31)(alpha * r0[j + i * N] - t0[j + i * N] + 31);
}
PQCLEAN_MQDSS64_CLEAN_MQ(e1 + i * M, r0 + i * N, F);
for (j = 0; j < N; j++) {
e1[i * N + j] = (gf31)(alpha * e1[j + i * M] - e0[j + i * M] + 31);
}
PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(t1 + i * N, t1 + i * N);
PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(e1 + i * N, e1 + i * N);
}
PQCLEAN_MQDSS64_CLEAN_gf31_npack(t1packed, t1, N * ROUNDS);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(e1packed, e1, M * ROUNDS);

memcpy(sig, t1packed, NPACKED_BYTES * ROUNDS);
sig += NPACKED_BYTES * ROUNDS;
memcpy(sig, e1packed, MPACKED_BYTES * ROUNDS);
sig += MPACKED_BYTES * ROUNDS;

shake256(h1, ((ROUNDS + 7) & ~7) >> 3, D_sigma0_h0_sigma1, 3 * HASH_BYTES + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES));

for (i = 0; i < ROUNDS; i++) {
b = (h1[(i >> 3)] >> (i & 7)) & 1;
if (b == 0) {
PQCLEAN_MQDSS64_CLEAN_gf31_npack(sig, r0 + i * N, N);
} else if (b == 1) {
PQCLEAN_MQDSS64_CLEAN_gf31_npack(sig, r1 + i * N, N);
}
memcpy(sig + NPACKED_BYTES, c + HASH_BYTES * (2 * i + (1 - b)), HASH_BYTES);
memcpy(sig + NPACKED_BYTES + HASH_BYTES, rho + (i + b * ROUNDS) * HASH_BYTES, HASH_BYTES);
sig += NPACKED_BYTES + 2 * HASH_BYTES;
}

*siglen = SIG_LEN;
return 0;
}

/**
* Verifies a detached signature and message under a given public key.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_verify(
const uint8_t *sig, size_t siglen,
const uint8_t *m, size_t mlen, const uint8_t *pk) {

gf31 r[N];
gf31 t[N];
gf31 e[M];
signed char F[F_LEN];
gf31 pk_gf31[M];
// Concatenated for convenient hashing.
unsigned char D_sigma0_h0_sigma1[HASH_BYTES * 3 + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES)];
unsigned char *D = D_sigma0_h0_sigma1;
unsigned char *sigma0 = D_sigma0_h0_sigma1 + HASH_BYTES;
unsigned char *h0 = D_sigma0_h0_sigma1 + 2 * HASH_BYTES;
unsigned char *t1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES;
unsigned char *e1packed = D_sigma0_h0_sigma1 + 3 * HASH_BYTES + ROUNDS * NPACKED_BYTES;
unsigned char h1[((ROUNDS + 7) & ~7) >> 3];
unsigned char c[HASH_BYTES * ROUNDS * 2];
memset(c, 0, HASH_BYTES * 2);
gf31 x[N];
gf31 y[M];
gf31 z[M];
unsigned char packbuf0[NPACKED_BYTES];
unsigned char packbuf1[MPACKED_BYTES];
uint64_t shakestate[25] = {0};
unsigned char shakeblock[SHAKE256_RATE];
int i, j;
gf31 alpha;
int alpha_count = 0;
int b;
uint64_t s_inc[26];

if (siglen != SIG_LEN) {
return -1;
}

shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, pk, PK_BYTES);
shake256_inc_absorb(s_inc, sig, HASH_BYTES);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(D, HASH_BYTES, s_inc);

sig += HASH_BYTES;

PQCLEAN_MQDSS64_CLEAN_gf31_nrand_schar(F, F_LEN, pk, SEED_BYTES);
pk += SEED_BYTES;
PQCLEAN_MQDSS64_CLEAN_gf31_nunpack(pk_gf31, pk, M);

memcpy(sigma0, sig, HASH_BYTES);

shake256_absorb(shakestate, D_sigma0_h0_sigma1, 2 * HASH_BYTES);
shake256_squeezeblocks(shakeblock, 1, shakestate);

memcpy(h0, shakeblock, HASH_BYTES);

sig += HASH_BYTES;

memcpy(t1packed, sig, ROUNDS * NPACKED_BYTES);
sig += ROUNDS * NPACKED_BYTES;
memcpy(e1packed, sig, ROUNDS * MPACKED_BYTES);
sig += ROUNDS * MPACKED_BYTES;

shake256(h1, ((ROUNDS + 7) & ~7) >> 3, D_sigma0_h0_sigma1, 3 * HASH_BYTES + ROUNDS * (NPACKED_BYTES + MPACKED_BYTES));

for (i = 0; i < ROUNDS; i++) {
do {
alpha = shakeblock[alpha_count] & 31;
alpha_count++;
if (alpha_count == SHAKE256_RATE) {
alpha_count = 0;
shake256_squeezeblocks(shakeblock, 1, shakestate);
}
} while (alpha == 31);
b = (h1[(i >> 3)] >> (i & 7)) & 1;

PQCLEAN_MQDSS64_CLEAN_gf31_nunpack(r, sig, N);
PQCLEAN_MQDSS64_CLEAN_gf31_nunpack(t, t1packed + NPACKED_BYTES * i, N);
PQCLEAN_MQDSS64_CLEAN_gf31_nunpack(e, e1packed + MPACKED_BYTES * i, M);

if (b == 0) {
PQCLEAN_MQDSS64_CLEAN_MQ(y, r, F);
for (j = 0; j < N; j++) {
x[j] = (gf31)(alpha * r[j] - t[j] + 31);
}
for (j = 0; j < N; j++) {
y[j] = (gf31)(alpha * y[j] - e[j] + 31);
}
PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(x, x);
PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(y, y);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf0, x, N);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf1, y, M);
com_0(c + HASH_BYTES * (2 * i + 0), sig + HASH_BYTES + NPACKED_BYTES, sig, packbuf0, packbuf1);
} else {
PQCLEAN_MQDSS64_CLEAN_MQ(y, r, F);
PQCLEAN_MQDSS64_CLEAN_G(z, t, r, F);
for (j = 0; j < N; j++) {
y[j] = (gf31)(alpha * (31 + pk_gf31[j] - y[j]) - z[j] - e[j] + 62);
}
PQCLEAN_MQDSS64_CLEAN_vgf31_shorten_unique(y, y);
PQCLEAN_MQDSS64_CLEAN_gf31_npack(packbuf0, y, M);
com_1(c + HASH_BYTES * (2 * i + 1), sig + HASH_BYTES + NPACKED_BYTES, sig, packbuf0);
}
memcpy(c + HASH_BYTES * (2 * i + (1 - b)), sig + NPACKED_BYTES, HASH_BYTES);
sig += NPACKED_BYTES + 2 * HASH_BYTES;
}

H(c, c, HASH_BYTES * ROUNDS * 2);
if (memcmp(c, sigma0, HASH_BYTES) != 0) {
return -1;
}

return 0;
}

/**
* Returns an array containing the signature followed by the message.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign(
uint8_t *sm, size_t *smlen,
const uint8_t *m, size_t mlen, const uint8_t *sk) {
size_t siglen;

PQCLEAN_MQDSS64_CLEAN_crypto_sign_signature(
sm, &siglen, m, mlen, sk);

memmove(sm + SIG_LEN, m, mlen);
*smlen = siglen + mlen;

return 0;
}

/**
* Verifies a given signature-message pair under a given public key.
*/
int PQCLEAN_MQDSS64_CLEAN_crypto_sign_open(
uint8_t *m, size_t *mlen,
const uint8_t *sm, size_t smlen, const uint8_t *pk) {
/* The API caller does not necessarily know what size a signature should be
but MQDSS signatures are always exactly SIG_LEN. */
if (smlen < SIG_LEN) {
memset(m, 0, smlen);
*mlen = 0;
return -1;
}

*mlen = smlen - SIG_LEN;

if (PQCLEAN_MQDSS64_CLEAN_crypto_sign_verify(
sm, SIG_LEN, sm + SIG_LEN, *mlen, pk)) {
memset(m, 0, smlen);
*mlen = 0;
return -1;
}

/* If verification was successful, move the message to the right place. */
memmove(m, sm + SIG_LEN, *mlen);

return 0;
}

+ 11
- 0
test/duplicate_consistency/mqdss-64_clean.yml View File

@@ -0,0 +1,11 @@
consistency_checks:
- source:
scheme: mqdss-48
implementation: clean
files:
- gf31.c
- gf31.h
- LICENSE
- mq.c
- mq.h
- sign.c

+ 30
- 0
test/test_makefiles_present.py View File

@@ -0,0 +1,30 @@
"""
Checks that a Makefile and a Makefile.Microsoft_nmake file is present for every
implementation of the specified scheme.
"""

import os
import pqclean
import helpers


def test_makefiles():
for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations:
yield check_makefiles, implementation


@helpers.filtered_test
def check_makefiles(implementation):
p1 = os.path.join(implementation.path(), 'Makefile')
p2 = os.path.join(implementation.path(), 'Makefile.Microsoft_nmake')
assert(os.path.isfile(p1) and os.path.isfile(p2))


if __name__ == '__main__':
try:
import nose2
nose2.main()
except ImportError:
import nose
nose.runmodule()

Loading…
Cancel
Save