Skip to content

Commit 4fdf64b

Browse files
committed
example: add the multilevel_build_no_stdlib example
- This commit add an example demonstrating how to build mlkem-native without the standard library (-nostdlib) - Create an example folder named `multilevel_build_no_stdlib` - Add the `example_no_stdlib_config.h` reference from `test/custom_stdlib_config.h`, this config provide custom implementations for mlk_memcpy and mlk_memset - Add the `-nostdlib` cflag during generating objects file. - Integrate this example to the `tests` script and ./Makefile Signed-off-by: willieyz <[email protected]>
1 parent 51d641a commit 4fdf64b

File tree

11 files changed

+896
-0
lines changed

11 files changed

+896
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,4 @@ clean:
224224
-make clean -C examples/monolithic_build_multilevel_native >/dev/null
225225
-make clean -C examples/multilevel_build >/dev/null
226226
-make clean -C examples/multilevel_build_native >/dev/null
227+
-make clean -C examples/multilevel_build_no_stdlib > /dev/null
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
2+
3+
build
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# (SPDX-License-Identifier: CC-BY-4.0)
2+
3+
.PHONY: build run clean mlkem512_objs mlkem768_objs mlkem1024_objs mlkem_objs size size_objs
4+
.DEFAULT_GOAL := all
5+
6+
Q ?= @
7+
# Append cross-prefix for cross compilation
8+
# Remove or ignore for native builds
9+
CC ?= gcc
10+
SIZE ?= size
11+
# When called from the root Makefile, CROSS_PREFIX has already been added here
12+
ifeq (,$(findstring $(CROSS_PREFIX),$(CC)))
13+
CC := $(CROSS_PREFIX)$(CC)
14+
endif
15+
16+
ifeq (,$(findstring $(CROSS_PREFIX),$(SIZE)))
17+
SIZE := $(CROSS_PREFIX)$(SIZE)
18+
endif
19+
20+
# Part A:
21+
#
22+
# mlkem-native source and header files
23+
#
24+
# If you are not concerned about minimizing for a specific backend,
25+
# you can just include _all_ source files into your build.
26+
MLK_SOURCE_ALL := $(wildcard \
27+
mlkem_native/mlkem/src/*.c \
28+
mlkem_native/mlkem/src/**/*.c \
29+
mlkem_native/mlkem/src/**/**/*.c \
30+
mlkem_native/mlkem/src/**/**/**/*.c)
31+
MLK_SOURCE:=$(foreach S,$(MLK_SOURCE_ALL),\
32+
$(if $(findstring /native/,$S),,$S))
33+
34+
INC=-Imlkem_native -Imlkem_native/mlkem -Imlkem_native/mlkem/src
35+
36+
BUILD_DIR=build
37+
MLKEM512_DIR = $(BUILD_DIR)/mlkem512
38+
MLKEM768_DIR = $(BUILD_DIR)/mlkem768
39+
MLKEM1024_DIR = $(BUILD_DIR)/mlkem1024
40+
41+
MLKEM512_OBJS=$(patsubst %,$(MLKEM512_DIR)/%.o,$(MLK_SOURCE))
42+
MLKEM768_OBJS=$(patsubst %,$(MLKEM768_DIR)/%.o,$(MLK_SOURCE))
43+
MLKEM1024_OBJS=$(patsubst %,$(MLKEM1024_DIR)/%.o,$(MLK_SOURCE))
44+
45+
$(MLKEM512_OBJS): $(MLKEM512_DIR)/%.c.o: %.c
46+
$(Q)[ -d $(@D) ] || mkdir -p $(@D)
47+
$(Q)$(CC) -nostdlib -DMLK_CONFIG_MULTILEVEL_WITH_SHARED -DMLK_CONFIG_PARAMETER_SET=512 $(INC) $(CFLAGS) -c $^ -o $@
48+
49+
$(MLKEM768_OBJS): $(MLKEM768_DIR)/%.c.o: %.c
50+
$(Q)[ -d $(@D) ] || mkdir -p $(@D)
51+
$(Q)$(CC) -nostdlib -DMLK_CONFIG_MULTILEVEL_NO_SHARED -DMLK_CONFIG_PARAMETER_SET=768 $(INC) $(CFLAGS) -c $^ -o $@
52+
53+
$(MLKEM1024_OBJS): $(MLKEM1024_DIR)/%.c.o: %.c
54+
$(Q)[ -d $(@D) ] || mkdir -p $(@D)
55+
$(Q)$(CC) -nostdlib -DMLK_CONFIG_MULTILEVEL_NO_SHARED -DMLK_CONFIG_PARAMETER_SET=1024 $(INC) $(CFLAGS) -c $^ -o $@
56+
57+
mlkem512_objs: $(MLKEM512_OBJS)
58+
mlkem768_objs: $(MLKEM768_OBJS)
59+
mlkem1024_objs: $(MLKEM1024_OBJS)
60+
mlkem_objs: mlkem512_objs mlkem768_objs mlkem1024_objs
61+
62+
# Part B:
63+
#
64+
# Random number generator
65+
#
66+
# !!! WARNING !!!
67+
#
68+
# The randombytes() implementation used here is for TESTING ONLY.
69+
# You MUST NOT use this implementation outside of testing.
70+
#
71+
# !!! WARNING !!!
72+
RNG_SOURCE=$(wildcard test_only_rng/*.c)
73+
74+
# Part C:
75+
#
76+
# Your application source code
77+
APP_SOURCE=$(wildcard *.c)
78+
79+
BIN=test_binary
80+
81+
CFLAGS := \
82+
-Wall \
83+
-Wextra \
84+
-Werror \
85+
-Wmissing-prototypes \
86+
-Wshadow \
87+
-Werror \
88+
-Wpointer-arith \
89+
-Wredundant-decls \
90+
-Wno-long-long \
91+
-Wno-unknown-pragmas \
92+
-Wno-unused-command-line-argument \
93+
-fomit-frame-pointer \
94+
-DMLK_CONFIG_NAMESPACE_PREFIX=mlkem \
95+
-std=c99 \
96+
-pedantic \
97+
-MMD \
98+
-O3 \
99+
$(CFLAGS)
100+
101+
BINARY_NAME_FULL=$(BUILD_DIR)/$(BIN)
102+
103+
CFLAGS += -DMLK_CONFIG_FILE="\"example_no_stdlib_config.h\""
104+
105+
$(BINARY_NAME_FULL): $(APP_SOURCE) $(RNG_SOURCE) $(MLKEM512_OBJS) $(MLKEM768_OBJS) $(MLKEM1024_OBJS)
106+
echo "$@"
107+
mkdir -p $(BUILD_DIR)
108+
$(CC) $(CFLAGS) $(INC) $^ -o $@
109+
110+
all: build size_objs
111+
112+
build: $(BINARY_NAME_FULL)
113+
114+
run: $(BINARY_NAME_FULL)
115+
$(EXEC_WRAPPER) ./$(BINARY_NAME_FULL)
116+
117+
size: build
118+
@echo "=== Size info for $(BINARY_NAME_FULL) ==="
119+
$(Q)$(SIZE) $(BINARY_NAME_FULL)
120+
121+
size_objs: size
122+
$(Q)echo "=== Object size summary ==="
123+
$(Q)$(SIZE) $(shell find $(BUILD_DIR)/mlkem512 -name '*.o') | (read header; echo "$$header"; awk '$$5 != 0' | sort -k5 -n -r)
124+
$(Q)$(SIZE) $(shell find $(BUILD_DIR)/mlkem768 -name '*.o') | (read header; echo "$$header"; awk '$$5 != 0' | sort -k5 -n -r)
125+
$(Q)$(SIZE) $(shell find $(BUILD_DIR)/mlkem1024 -name '*.o') | (read header; echo "$$header"; awk '$$5 != 0' | sort -k5 -n -r)
126+
127+
clean:
128+
rm -rf $(BUILD_DIR)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[//]: # (SPDX-License-Identifier: CC-BY-4.0)
2+
3+
# Multi-level build
4+
5+
This directory contains a minimal example for how to build mlkem-native with support for all 3 security levels
6+
MLKEM-512, MLKEM-768, and MLKEM-1024, and so that level-independent code is shared. In this example, only the C-backend
7+
of mlkem-native is used.
8+
9+
The library is built 3 times in different build directories `build/mlkem{512,768,1024}`. For the MLKEM-512 build, we set
10+
`MLK_CONFIG_MULTILEVEL_WITH_SHARED` to force the inclusion of all level-independent code in the
11+
MLKEM512-build. For MLKEM-768 and MLKEM-1024, we set `MLK_CONFIG_MULTILEVEL_NO_SHARED` to not include any
12+
level-independent code. Finally, we use the common namespace prefix `mlkem` as `MLK_CONFIG_NAMESPACE_PREFIX` for all three
13+
builds; the suffix 512/768/1024 will be added to level-dependent functions automatically.
14+
15+
## Usage
16+
17+
Build this example with `make build`, run with `make run`.
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
* Copyright (c) The mlkem-native project authors
3+
* SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
4+
*/
5+
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <string.h>
9+
10+
#include "mlkem_native_all.h"
11+
#include "test_only_rng/notrandombytes.h"
12+
13+
#define CHECK(x) \
14+
do \
15+
{ \
16+
int rc; \
17+
rc = (x); \
18+
if (!rc) \
19+
{ \
20+
fprintf(stderr, "ERROR (%s,%d)\n", __FILE__, __LINE__); \
21+
return 1; \
22+
} \
23+
} while (0)
24+
25+
static int test_keys_mlkem512(void)
26+
{
27+
/* The PCT modifies the PRNG state, so the KAT tests don't work.
28+
* We run KAT tests only for disabled PCT. */
29+
#if !defined(MLK_CONFIG_KEYGEN_PCT)
30+
const uint8_t expected_key[] = {
31+
0x77, 0x6c, 0x74, 0xdf, 0x30, 0x1f, 0x8d, 0x82, 0x52, 0x5e, 0x8e,
32+
0xbb, 0xb4, 0x00, 0x95, 0xcd, 0x2e, 0x92, 0xdf, 0x6d, 0xc9, 0x33,
33+
0xe7, 0x86, 0x62, 0x59, 0xf5, 0x31, 0xc7, 0x35, 0x0a, 0xd5};
34+
#endif /* !MLK_CONFIG_KEYGEN_PCT */
35+
36+
uint8_t pk[MLKEM512_PUBLICKEYBYTES];
37+
uint8_t sk[MLKEM512_SECRETKEYBYTES];
38+
uint8_t ct[MLKEM512_CIPHERTEXTBYTES];
39+
uint8_t key_a[MLKEM512_BYTES];
40+
uint8_t key_b[MLKEM512_BYTES];
41+
42+
/* WARNING: Test-only
43+
* Normally, you would want to seed a PRNG with trustworthy entropy here. */
44+
randombytes_reset();
45+
46+
/* Alice generates a public key */
47+
CHECK(mlkem512_keypair(pk, sk) == 0);
48+
49+
/* Bob derives a secret key and creates a response */
50+
CHECK(mlkem512_enc(ct, key_b, pk) == 0);
51+
52+
/* Alice uses Bobs response to get her shared key */
53+
CHECK(mlkem512_dec(key_a, ct, sk) == 0);
54+
55+
CHECK(memcmp(key_a, key_b, MLKEM512_BYTES) == 0);
56+
57+
printf("Shared secret: ");
58+
{
59+
size_t i;
60+
for (i = 0; i < sizeof(key_a); i++)
61+
{
62+
printf("%02x", key_a[i]);
63+
}
64+
}
65+
printf("\n");
66+
67+
#if !defined(MLK_CONFIG_KEYGEN_PCT)
68+
/* Check against hardcoded result to make sure that
69+
* we integrated custom FIPS202 correctly */
70+
CHECK(memcmp(key_a, expected_key, sizeof(key_a)) == 0);
71+
#else
72+
printf(
73+
"[WARNING] Skipping KAT test since PCT is enabled and modifies PRNG\n");
74+
#endif
75+
76+
printf("[MLKEM-512] OK\n");
77+
return 0;
78+
}
79+
80+
static int test_keys_mlkem768(void)
81+
{
82+
/* The PCT modifies the PRNG state, so the KAT tests don't work.
83+
* We run KAT tests only for disabled PCT. */
84+
#if !defined(MLK_CONFIG_KEYGEN_PCT)
85+
const uint8_t expected_key[] = {
86+
0xe9, 0x13, 0x77, 0x84, 0x0e, 0x6b, 0x66, 0x94, 0xea, 0xa9, 0xf0,
87+
0x1c, 0x97, 0xff, 0x68, 0x87, 0x4e, 0x8b, 0x0c, 0x52, 0x0b, 0x00,
88+
0xc2, 0xcd, 0xe3, 0x7c, 0x4f, 0xc2, 0x39, 0x62, 0x6e, 0x70};
89+
#endif /* !MLK_CONFIG_KEYGEN_PCT */
90+
91+
uint8_t pk[MLKEM768_PUBLICKEYBYTES];
92+
uint8_t sk[MLKEM768_SECRETKEYBYTES];
93+
uint8_t ct[MLKEM768_CIPHERTEXTBYTES];
94+
uint8_t key_a[MLKEM768_BYTES];
95+
uint8_t key_b[MLKEM768_BYTES];
96+
97+
/* WARNING: Test-only
98+
* Normally, you would want to seed a PRNG with trustworthy entropy here. */
99+
randombytes_reset();
100+
101+
/* Alice generates a public key */
102+
CHECK(mlkem768_keypair(pk, sk) == 0);
103+
104+
/* Bob derives a secret key and creates a response */
105+
CHECK(mlkem768_enc(ct, key_b, pk) == 0);
106+
107+
/* Alice uses Bobs response to get her shared key */
108+
CHECK(mlkem768_dec(key_a, ct, sk) == 0);
109+
110+
CHECK(memcmp(key_a, key_b, MLKEM768_BYTES) == 0);
111+
112+
printf("Shared secret: ");
113+
{
114+
size_t i;
115+
for (i = 0; i < sizeof(key_a); i++)
116+
{
117+
printf("%02x", key_a[i]);
118+
}
119+
}
120+
printf("\n");
121+
122+
#if !defined(MLK_CONFIG_KEYGEN_PCT)
123+
/* Check against hardcoded result to make sure that
124+
* we integrated custom FIPS202 correctly */
125+
CHECK(memcmp(key_a, expected_key, sizeof(key_a)) == 0);
126+
#else
127+
printf(
128+
"[WARNING] Skipping KAT test since PCT is enabled and modifies PRNG\n");
129+
#endif
130+
131+
printf("[MLKEM-768] OK\n");
132+
return 0;
133+
}
134+
135+
136+
static int test_keys_mlkem1024(void)
137+
{
138+
/* The PCT modifies the PRNG state, so the KAT tests don't work.
139+
* We run KAT tests only for disabled PCT. */
140+
#if !defined(MLK_CONFIG_KEYGEN_PCT)
141+
const uint8_t expected_key[] = {
142+
0x5d, 0x9e, 0x23, 0x5f, 0xcc, 0xb2, 0xb3, 0x49, 0x9a, 0x5f, 0x49,
143+
0x0a, 0x56, 0xe3, 0xf0, 0xd3, 0xfd, 0x9b, 0x58, 0xbd, 0xa2, 0x8b,
144+
0x69, 0x0f, 0x91, 0xb5, 0x7b, 0x88, 0xa5, 0xa8, 0x0b, 0x90};
145+
#endif /* !MLK_CONFIG_KEYGEN_PCT */
146+
uint8_t pk[MLKEM1024_PUBLICKEYBYTES];
147+
uint8_t sk[MLKEM1024_SECRETKEYBYTES];
148+
uint8_t ct[MLKEM1024_CIPHERTEXTBYTES];
149+
uint8_t key_a[MLKEM1024_BYTES];
150+
uint8_t key_b[MLKEM1024_BYTES];
151+
152+
/* WARNING: Test-only
153+
* Normally, you would want to seed a PRNG with trustworthy entropy here. */
154+
randombytes_reset();
155+
156+
/* Alice generates a public key */
157+
CHECK(mlkem1024_keypair(pk, sk) == 0);
158+
159+
/* Bob derives a secret key and creates a response */
160+
CHECK(mlkem1024_enc(ct, key_b, pk) == 0);
161+
162+
/* Alice uses Bobs response to get her shared key */
163+
CHECK(mlkem1024_dec(key_a, ct, sk) == 0);
164+
165+
CHECK(memcmp(key_a, key_b, MLKEM1024_BYTES) == 0);
166+
167+
printf("Shared secret: ");
168+
{
169+
size_t i;
170+
for (i = 0; i < sizeof(key_a); i++)
171+
{
172+
printf("%02x", key_a[i]);
173+
}
174+
}
175+
printf("\n");
176+
177+
#if !defined(MLK_CONFIG_KEYGEN_PCT)
178+
/* Check against hardcoded result to make sure that
179+
* we integrated custom FIPS202 correctly */
180+
CHECK(memcmp(key_a, expected_key, sizeof(key_a)) == 0);
181+
#else
182+
printf(
183+
"[WARNING] Skipping KAT test since PCT is enabled and modifies PRNG\n");
184+
#endif
185+
186+
printf("[MLKEM-1024] OK\n");
187+
return 0;
188+
}
189+
190+
int main(void)
191+
{
192+
if (test_keys_mlkem512() != 0)
193+
{
194+
return 1;
195+
}
196+
197+
if (test_keys_mlkem768() != 0)
198+
{
199+
return 1;
200+
}
201+
202+
if (test_keys_mlkem1024() != 0)
203+
{
204+
return 1;
205+
}
206+
207+
return 0;
208+
}

0 commit comments

Comments
 (0)