Skip to content

Commit

Permalink
Add proof artefacts for poly_tobytes() (#235)
Browse files Browse the repository at this point in the history
* Add proof artefacts for poly_tobytes()

Code updated to add contracts, and make type
conversions more explicit for CBMC.

Signed-off-by: Rod Chapman <rodchap@amazon.com>

* Address review comments on poly.c/poly_tobytes() proof

1. Remove needless masks where CBMC can deduce the range.

2. Shorten loop invariant range constraint on i onto a single line.

Signed-off-by: Rod Chapman <rodchap@amazon.com>

* Remove needless casts to (uint8_t)

Signed-off-by: Rod Chapman <rodchap@amazon.com>

---------

Signed-off-by: Rod Chapman <rodchap@amazon.com>
  • Loading branch information
rod-chapman authored Oct 16, 2024
1 parent 09b50fe commit 59a0b06
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 11 deletions.
54 changes: 54 additions & 0 deletions cbmc/proofs/poly_tobytes/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_tobytes_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_tobytes

DEFINES +=
INCLUDES +=

REMOVE_FUNCTION_BODY +=
UNWINDSET +=

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

CHECK_FUNCTION_CONTRACTS=$(KYBER_NAMESPACE)poly_tobytes
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=--bitwuzla

FUNCTION_NAME = $(KYBER_NAMESPACE)poly_tobytes

# 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_tobytes/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.
36 changes: 26 additions & 10 deletions mlkem/poly.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,17 +198,33 @@ void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES]) {
#if !defined(MLKEM_USE_NATIVE_POLY_TOBYTES)
void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a) {
POLY_UBOUND(a, KYBER_Q);
unsigned int i;
uint16_t t0, t1;

for (i = 0; i < KYBER_N / 2; i++) {
t0 = a->coeffs[2 * i];
t1 = a->coeffs[2 * i + 1];
// REF-CHANGE: Precondition change, we assume unsigned canonical data
r[3 * i + 0] = (t0 >> 0);
r[3 * i + 1] = (t0 >> 8) | (t1 << 4);
r[3 * i + 2] = (t1 >> 4);
}
for (unsigned int i = 0; i < KYBER_N / 2; i++)
// clang-format off
ASSIGNS(i, OBJECT_WHOLE(r))
INVARIANT(i >= 0 && i <= KYBER_N / 2)
DECREASES(KYBER_N / 2 - i)
// clang-format on
{
const uint16_t t0 = a->coeffs[2 * i];
const uint16_t t1 = a->coeffs[2 * i + 1];
// REF-CHANGE: Precondition change, we assume unsigned canonical data

// t0 and t1 are both < KYBER_Q, so contain at most 12 bits each of
// significant data, so these can be packed into 24 bits or exactly
// 3 bytes, as follows.

// Least significant bits 0 - 7 of t0.
r[3 * i + 0] = t0 & 0xFF;

// Most significant bits 8 - 11 of t0 become the least significant
// nibble of the second byte. The least significant 4 bits
// of t1 become the upper nibble of the second byte.
r[3 * i + 1] = (t0 >> 8) | ((t1 << 4) & 0xF0);

// Bits 4 - 11 of t1 become the third byte.
r[3 * i + 2] = t1 >> 4;
}
}
#else /* MLKEM_USE_NATIVE_POLY_TOBYTES */
void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a) {
Expand Down
7 changes: 6 additions & 1 deletion mlkem/poly.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,12 @@ void poly_decompress(poly *r, const uint8_t a[KYBER_POLYCOMPRESSEDBYTES])
(KYBER_Q - 1)));

#define poly_tobytes KYBER_NAMESPACE(poly_tobytes)
void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a);
void poly_tobytes(uint8_t r[KYBER_POLYBYTES], const poly *a)
REQUIRES(a != NULL && IS_FRESH(a, sizeof(poly)))
REQUIRES(ARRAY_IN_BOUNDS(int, k, 0, (KYBER_N - 1), a->coeffs, 0,
(KYBER_Q - 1))) ASSIGNS(OBJECT_WHOLE(r));


#define poly_frombytes KYBER_NAMESPACE(poly_frombytes)
void poly_frombytes(poly *r, const uint8_t a[KYBER_POLYBYTES]);

Expand Down

0 comments on commit 59a0b06

Please sign in to comment.