1
1
mirror of https://github.com/henrydcase/pqc.git synced 2024-11-27 01:41:40 +00:00

MQDSS-48 before detached signatures

This commit is contained in:
Joost Rijneveld 2019-04-29 17:19:52 +02:00
parent a4c617bb0c
commit db6f71086f
No known key found for this signature in database
GPG Key ID: A4FE39CF49CBC553
9 changed files with 769 additions and 0 deletions

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: a14cb8e4f149493fc5979e465e09ce943e8d669186ff5c7c3d11239fa869def6
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

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)

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

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] = (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;
}
}

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

View File

@ -0,0 +1,85 @@
#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)));
}
}

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

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

View File

@ -0,0 +1,385 @@
#include <assert.h>
#include <stdint.h>
#include <stddef.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) {
(void)sig;
(void)siglen;
(void)m;
(void)mlen;
(void)sk;
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) {
(void)sig;
(void)siglen;
(void)m;
(void)mlen;
(void)pk;
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) {
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;
unsigned char b;
int i, j;
shake256(skbuf, SEED_BYTES * 4, sk, SEED_BYTES);
PQCLEAN_MQDSS48_CLEAN_gf31_nrand_schar(F, F_LEN, skbuf, SEED_BYTES);
assert(SIG_LEN > SEED_BYTES);
memcpy(sm + SIG_LEN - SEED_BYTES, sk, SEED_BYTES);
memcpy(sm + SIG_LEN, m, mlen);
H(sm, sm + SIG_LEN - SEED_BYTES, mlen + SEED_BYTES); // 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);
memcpy(sm + SIG_LEN - HASH_BYTES - PK_BYTES, pk, PK_BYTES);
memcpy(sm + SIG_LEN - HASH_BYTES, sm, HASH_BYTES);
H(D, sm + SIG_LEN - HASH_BYTES - PK_BYTES, mlen + PK_BYTES + HASH_BYTES);
sm += 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(sm, sigma0, HASH_BYTES);
sm += 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(sm, t1packed, NPACKED_BYTES * ROUNDS);
sm += NPACKED_BYTES * ROUNDS;
memcpy(sm, e1packed, MPACKED_BYTES * ROUNDS);
sm += 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(sm, r0+i*N, N);
} else if (b == 1) {
PQCLEAN_MQDSS48_CLEAN_gf31_npack(sm, r1+i*N, N);
}
memcpy(sm + NPACKED_BYTES, c + HASH_BYTES * (2*i + (1 - b)), HASH_BYTES);
memcpy(sm + NPACKED_BYTES + HASH_BYTES, rho + (i + b * ROUNDS) * HASH_BYTES, HASH_BYTES);
sm += NPACKED_BYTES + 2*HASH_BYTES;
}
*smlen = SIG_LEN + 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)
{
gf31 r[N];
gf31 t[N];
gf31 e[M];
signed char F[F_LEN];
gf31 pk_gf31[M];
unsigned char sig[SIG_LEN];
unsigned char *sigptr = sig;
// 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;
unsigned char b;
/* 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;
/* Create a copy of the signature so that m = sm is not an issue */
memcpy(sig, sm, SIG_LEN);
/* Put the message all the way at the end of the m buffer, so that we can
* prepend the required other inputs for the hash function. */
memcpy(m + SIG_LEN, sm + SIG_LEN, *mlen);
memcpy(m + SIG_LEN - PK_BYTES - HASH_BYTES, pk, PK_BYTES); // Copy pk to m.
memcpy(m + SIG_LEN - HASH_BYTES, sigptr, HASH_BYTES); // Copy R to m.
H(D, m + SIG_LEN - PK_BYTES - HASH_BYTES, *mlen + PK_BYTES + HASH_BYTES);
sigptr += 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, sigptr, HASH_BYTES);
shake256_absorb(shakestate, D_sigma0_h0_sigma1, 2 * HASH_BYTES);
shake256_squeezeblocks(shakeblock, 1, shakestate);
memcpy(h0, shakeblock, HASH_BYTES);
sigptr += HASH_BYTES;
memcpy(t1packed, sigptr, ROUNDS * NPACKED_BYTES);
sigptr += ROUNDS*NPACKED_BYTES;
memcpy(e1packed, sigptr, ROUNDS * MPACKED_BYTES);
sigptr += 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, sigptr, 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), sigptr + HASH_BYTES + NPACKED_BYTES, sigptr, 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), sigptr + HASH_BYTES + NPACKED_BYTES, sigptr, packbuf0);
}
memcpy(c + HASH_BYTES*(2*i + (1 - b)), sigptr + NPACKED_BYTES, HASH_BYTES);
sigptr += NPACKED_BYTES + 2*HASH_BYTES;
}
H(c, c, HASH_BYTES * ROUNDS * 2);
if (memcmp(c, sigma0, HASH_BYTES)) {
memset(m, 0, smlen);
*mlen = 0;
return 1;
}
/* If verification was successful, move the message to the right place. */
memmove(m, m + SIG_LEN, *mlen);
return 0;
}