Skip to content

Commit

Permalink
Merge pull request #306 from pq-code-package/polyvec_add
Browse files Browse the repository at this point in the history
CBMC: Add spec + proof for `polyvec_basemul_acc_montgomery_cached` + `poly_add`
  • Loading branch information
mkannwischer authored Nov 5, 2024
2 parents 4eae16a + af5daa9 commit 183855e
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 31 deletions.
54 changes: 54 additions & 0 deletions cbmc/proofs/poly_add/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# SPDX-License-Identifier: Apache-2.0

include ../Makefile_params.common

HARNESS_ENTRY = harness
HARNESS_FILE = poly_add_harness

# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = poly_add

DEFINES +=
INCLUDES +=

REMOVE_FUNCTION_BODY +=
UNWINDSET +=

PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/mlkem/poly.c

CHECK_FUNCTION_CONTRACTS=$(MLKEM_NAMESPACE)poly_add
USE_FUNCTION_CONTRACTS=
APPLY_LOOP_CONTRACTS=on
USE_DYNAMIC_FRAMES=1

# Disable any setting of EXTERNAL_SAT_SOLVER, and choose SMT backend instead
EXTERNAL_SAT_SOLVER=
CBMCFLAGS=--smt2

FUNCTION_NAME = poly_add

# If this proof is found to consume huge amounts of RAM, you can set the
# EXPENSIVE variable. With new enough versions of the proof tools, this will
# restrict the number of EXPENSIVE CBMC jobs running at once. See the
# documentation in Makefile.common under the "Job Pools" heading for details.
# EXPENSIVE = true

# This function is large enough to need...
CBMC_OBJECT_BITS = 8

# If you require access to a file-local ("static") function or object to conduct
# your proof, set the following (and do not include the original source file
# ("mlkem/poly.c") in PROJECT_SOURCES).
# REWRITTEN_SOURCES = $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i
# include ../Makefile.common
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_SOURCE = $(SRCDIR)/mlkem/poly.c
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_FUNCTIONS = foo bar
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_OBJECTS = baz
# Care is required with variables on the left-hand side: REWRITTEN_SOURCES must
# be set before including Makefile.common, but any use of variables on the
# left-hand side requires those variables to be defined. Hence, _SOURCE,
# _FUNCTIONS, _OBJECTS is set after including Makefile.common.

include ../Makefile.common
3 changes: 3 additions & 0 deletions cbmc/proofs/poly_add/cbmc-proof.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: Apache-2.0

# This file marks this directory as containing a CBMC proof.
27 changes: 27 additions & 0 deletions cbmc/proofs/poly_add/poly_add_harness.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0 AND Apache-2.0

/*
* Insert copyright notice
*/

/**
* @file poly_add_harness.c
* @brief Implements the proof harness for basemul_cached function.
*/
#include "poly.h"

/*
* Insert project header files that
* - include the declaration of the function
* - include the types needed to declare function arguments
*/

/**
* @brief Starting point for formal analysis
*
*/
void harness(void) {
poly *r, *b;
poly_add(r, b);
}
54 changes: 54 additions & 0 deletions cbmc/proofs/polyvec_basemul_acc_montgomery_cached/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# SPDX-License-Identifier: Apache-2.0

include ../Makefile_params.common

HARNESS_ENTRY = harness
HARNESS_FILE = polyvec_basemul_acc_montgomery_cached_harness

# This should be a unique identifier for this proof, and will appear on the
# Litani dashboard. It can be human-readable and contain spaces if you wish.
PROOF_UID = polyvec_basemul_acc_montgomery_cached

DEFINES +=
INCLUDES +=

REMOVE_FUNCTION_BODY +=
UNWINDSET += $(MLKEM_NAMESPACE)polyvec_basemul_acc_montgomery_cached.0:4 # Largest value of MLKEM_K

PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
PROJECT_SOURCES += $(SRCDIR)/mlkem/polyvec.c

CHECK_FUNCTION_CONTRACTS=$(MLKEM_NAMESPACE)polyvec_basemul_acc_montgomery_cached
USE_FUNCTION_CONTRACTS=$(MLKEM_NAMESPACE)poly_basemul_montgomery_cached $(MLKEM_NAMESPACE)poly_add
APPLY_LOOP_CONTRACTS=on
USE_DYNAMIC_FRAMES=1

# Disable any setting of EXTERNAL_SAT_SOLVER, and choose SMT backend instead
EXTERNAL_SAT_SOLVER=
CBMCFLAGS=--smt2

FUNCTION_NAME = polyvec_basemul_acc_montgomery_cached

# If this proof is found to consume huge amounts of RAM, you can set the
# EXPENSIVE variable. With new enough versions of the proof tools, this will
# restrict the number of EXPENSIVE CBMC jobs running at once. See the
# documentation in Makefile.common under the "Job Pools" heading for details.
# EXPENSIVE = true

# This function is large enough to need...
CBMC_OBJECT_BITS = 8

# If you require access to a file-local ("static") function or object to conduct
# your proof, set the following (and do not include the original source file
# ("mlkem/poly.c") in PROJECT_SOURCES).
# REWRITTEN_SOURCES = $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i
# include ../Makefile.common
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_SOURCE = $(SRCDIR)/mlkem/poly.c
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_FUNCTIONS = foo bar
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_OBJECTS = baz
# Care is required with variables on the left-hand side: REWRITTEN_SOURCES must
# be set before including Makefile.common, but any use of variables on the
# left-hand side requires those variables to be defined. Hence, _SOURCE,
# _FUNCTIONS, _OBJECTS is set after including Makefile.common.

include ../Makefile.common
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: Apache-2.0

# This file marks this directory as containing a CBMC proof.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0 AND Apache-2.0

/*
* Insert copyright notice
*/

/**
* @file polyvec_basemul_acc_montgomery_cached_harness.c
* @brief Implements the proof harness for basemul_cached function.
*/
#include "polyvec.h"

/*
* Insert project header files that
* - include the declaration of the function
* - include the types needed to declare function arguments
*/

/**
* @brief Starting point for formal analysis
*
*/
void harness(void) {
poly *r;
polyvec *a, *b;
polyvec_mulcache *b_cached;

polyvec_basemul_acc_montgomery_cached(r, a, b, b_cached);
}
8 changes: 4 additions & 4 deletions mlkem/indcpa.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ void indcpa_keypair_derand(uint8_t pk[MLKEM_INDCPA_PUBLICKEYBYTES],
}

// Arithmetic cannot overflow, see static assertion at the top
polyvec_add(&pkpv, &pkpv, &e);
polyvec_add(&pkpv, &e);
polyvec_reduce(&pkpv);
polyvec_reduce(&skpv);

Expand Down Expand Up @@ -395,9 +395,9 @@ void indcpa_enc(uint8_t c[MLKEM_INDCPA_BYTES],
poly_invntt_tomont(&v);

// Arithmetic cannot overflow, see static assertion at the top
polyvec_add(&b, &b, &ep);
poly_add(&v, &v, &epp);
poly_add(&v, &v, &k);
polyvec_add(&b, &ep);
poly_add(&v, &epp);
poly_add(&v, &k);

polyvec_reduce(&b);
poly_reduce(&v);
Expand Down
27 changes: 13 additions & 14 deletions mlkem/poly.c
Original file line number Diff line number Diff line change
Expand Up @@ -496,20 +496,19 @@ void poly_reduce(poly *r) {
}
#endif /* MLKEM_USE_NATIVE_POLY_REDUCE */

/*************************************************
* Name: poly_add
*
* Description: Add two polynomials; no modular reduction is performed
*
* Arguments: - poly *r: pointer to output polynomial
* - const poly *a: pointer to first input polynomial
* - const poly *b: pointer to second input polynomial
**************************************************/
void poly_add(poly *r, const poly *a, const poly *b) {
unsigned int i;
for (i = 0; i < MLKEM_N; i++) {
r->coeffs[i] = a->coeffs[i] + b->coeffs[i];
}
void poly_add(poly *r, const poly *b) {
int i;
for (i = 0; i < MLKEM_N; i++)
// clang-format off
ASSIGNS(i, OBJECT_WHOLE(r))
INVARIANT(i >= 0 && i <= MLKEM_N)
INVARIANT(FORALL(int, k0, i, MLKEM_N - 1, r->coeffs[k0] == LOOP_ENTRY(*r).coeffs[k0]))
INVARIANT(FORALL(int, k1, 0, i - 1, r->coeffs[k1] == LOOP_ENTRY(*r).coeffs[k1] + b->coeffs[k1]))
DECREASES(MLKEM_N - i)
// clang-format on
{
r->coeffs[i] = r->coeffs[i] + b->coeffs[i];
}
}

/*************************************************
Expand Down
26 changes: 25 additions & 1 deletion mlkem/poly.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,31 @@ ENSURES(ARRAY_IN_BOUNDS(int, k, 0, MLKEM_N - 1, r->coeffs, 0, MLKEM_Q - 1));
// clang-format on

#define poly_add MLKEM_NAMESPACE(poly_add)
void poly_add(poly *r, const poly *a, const poly *b);
/************************************************************
* Name: poly_add
*
* Description: Adds two polynomials in place
*
* Arguments: - r: Pointer to input-output polynomial to be added to.
* - b: Pointer to input polynomial that should be added
* to r. Must be disjoint from r.
*
* The coefficients of r and b must be so that the addition does
* not overflow. Otherwise, the behaviour of this function is undefined.
*
************************************************************/
// REF-CHANGE:
// The reference implementation uses a 3-argument poly_add.
// We specialize to the accumulator form to avoid reasoning about aliasing.
void poly_add(poly *r, const poly *b) // clang-format off
REQUIRES(IS_FRESH(r, sizeof(poly)))
REQUIRES(IS_FRESH(b, sizeof(poly)))
REQUIRES(FORALL(int, k0, 0, MLKEM_N - 1, (int32_t) r->coeffs[k0] + b->coeffs[k0] <= INT16_MAX))
REQUIRES(FORALL(int, k1, 0, MLKEM_N - 1, (int32_t) r->coeffs[k1] + b->coeffs[k1] >= INT16_MIN))
ENSURES(FORALL(int, k, 0, MLKEM_N - 1, r->coeffs[k] == OLD(*r).coeffs[k] + b->coeffs[k]))
ASSIGNS(OBJECT_WHOLE(r));
// clang-format on

#define poly_sub MLKEM_NAMESPACE(poly_sub)
void poly_sub(poly *r, const poly *a, const poly *b);

Expand Down
37 changes: 27 additions & 10 deletions mlkem/polyvec.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,18 +226,35 @@ void polyvec_basemul_acc_montgomery_cached(poly *r, const polyvec *a,
POLYVEC_BOUND(b, NTT_BOUND);
POLYVEC_BOUND(b_cache, MLKEM_Q);

unsigned int i;
int i;
poly t;

poly_basemul_montgomery_cached(r, &a->vec[0], &b->vec[0], &b_cache->vec[0]);
for (i = 1; i < MLKEM_K; i++) {
poly_basemul_montgomery_cached(&t, &a->vec[i], &b->vec[i],
&b_cache->vec[i]);
poly_add(r, r, &t);
// abs bounds: < (i+1) * 3/2 * q
}

// abs bounds: < MLKEM_K * 3/2 * q <= 4 * 3/2 * q = 19974
for (i = 1; i < MLKEM_K; i++)
// clang-format off
ASSIGNS(i, t, OBJECT_WHOLE(r))
INVARIANT(i >= 1 && i <= MLKEM_K)
INVARIANT(ARRAY_IN_BOUNDS(int, k, 0, MLKEM_N - 1, r->coeffs,
i * (-3 * HALF_Q + 1), i * (3 * HALF_Q - 1)))
DECREASES(MLKEM_K - i)
// clang-format on
{
poly_basemul_montgomery_cached(&t, &a->vec[i], &b->vec[i],
&b_cache->vec[i]);
poly_add(r, &t);
// abs bounds: < (i+1) * 3/2 * q
}

// Those bounds are true for the C implementation, but not needed
// in the higher level bounds reasoning. It is thus best to omit
// them from the spec to not unnecessarily constraint native implementations.
ASSERT(
ARRAY_IN_BOUNDS(int, k, 0, MLKEM_N - 1, r->coeffs,
MLKEM_K * (-3 * HALF_Q + 1), MLKEM_K * (3 * HALF_Q - 1)),
"polyvec_basemul_acc_montgomery_cached output bounds");
// TODO: Integrate CBMC assertion into POLY_BOUND if CBMC is set
POLY_BOUND(r, MLKEM_K * 3 * HALF_Q);
}
#else /* !MLKEM_USE_NATIVE_POLYVEC_BASEMUL_ACC_MONTGOMERY_CACHED */
void polyvec_basemul_acc_montgomery_cached(poly *r, const polyvec *a,
Expand Down Expand Up @@ -310,9 +327,9 @@ void polyvec_reduce(polyvec *r) {
* - const polyvec *a: pointer to first input vector of polynomials
* - const polyvec *b: pointer to second input vector of polynomials
**************************************************/
void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b) {
void polyvec_add(polyvec *r, const polyvec *b) {
unsigned int i;
for (i = 0; i < MLKEM_K; i++) {
poly_add(&r->vec[i], &a->vec[i], &b->vec[i]);
poly_add(&r->vec[i], &b->vec[i]);
}
}
32 changes: 30 additions & 2 deletions mlkem/polyvec.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,37 @@ void polyvec_basemul_acc_montgomery(poly *r, const polyvec *a,
// REF-CHANGE: This function does not exist in the reference implementation
#define polyvec_basemul_acc_montgomery_cached \
MLKEM_NAMESPACE(polyvec_basemul_acc_montgomery_cached)
/*************************************************
* Name: polyvec_basemul_acc_montgomery_cached
*
* Description: Scalar product of two vectors of polynomials in NTT domain,
* using mulcache for second operand.
*
* Bounds:
* - a is assumed to be coefficient-wise < q in absolute value.
* - No bounds guarantees for the coefficients in the result.
*
* Arguments: - poly *r: pointer to output polynomial
* - const polyvec *a: pointer to first input polynomial vector
* - const polyvec *b: pointer to second input polynomial vector
* - const polyvec_mulcache *b_cache: pointer to mulcache
* for second input polynomial vector. Can be computed
* via polyvec_mulcache_compute().
**************************************************/
void polyvec_basemul_acc_montgomery_cached(poly *r, const polyvec *a,
const polyvec *b,
const polyvec_mulcache *b_cache);
const polyvec_mulcache *b_cache)
// clang-format off
REQUIRES(IS_FRESH(r, sizeof(poly)))
REQUIRES(IS_FRESH(a, sizeof(polyvec)))
REQUIRES(IS_FRESH(b, sizeof(polyvec)))
REQUIRES(IS_FRESH(b_cache, sizeof(polyvec_mulcache)))
// Input is coefficient-wise < q in absolute value
REQUIRES(FORALL(int, k1, 0, MLKEM_K - 1,
ARRAY_IN_BOUNDS(int, k2, 0, MLKEM_N - 1,
a->vec[k1].coeffs, -(MLKEM_Q - 1), (MLKEM_Q - 1))))
ASSIGNS(OBJECT_WHOLE(r));
// clang-format on

// REF-CHANGE: This function does not exist in the reference implementation
#define polyvec_mulcache_compute MLKEM_NAMESPACE(polyvec_mulcache_compute)
Expand Down Expand Up @@ -80,6 +108,6 @@ ASSIGNS(OBJECT_WHOLE(x));
void polyvec_reduce(polyvec *r);

#define polyvec_add MLKEM_NAMESPACE(polyvec_add)
void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b);
void polyvec_add(polyvec *r, const polyvec *b);

#endif

0 comments on commit 183855e

Please sign in to comment.