@@ -11,6 +11,7 @@ import platform
11
11
import argparse
12
12
import os
13
13
import sys
14
+ import time
14
15
import logging
15
16
import subprocess
16
17
import json
@@ -125,6 +126,9 @@ def config_logger(verbose):
125
126
126
127
def logger (test_type , scheme , cross_prefix , opt ):
127
128
"""Emit line indicating the processing of the given test"""
129
+
130
+ test_desc = str (test_type )
131
+
128
132
compile_mode = "cross" if cross_prefix else "native"
129
133
if opt is None :
130
134
opt_label = ""
@@ -133,14 +137,14 @@ def logger(test_type, scheme, cross_prefix, opt):
133
137
else :
134
138
opt_label = " no_opt"
135
139
136
- if test_type .is_example ():
140
+ if isinstance ( test_type , TEST_TYPES ) and test_type .is_example ():
137
141
sz = 40
138
142
else :
139
143
sz = 18
140
144
141
145
return logging .getLogger (
142
146
"{0:<{1}} {2:<11} {3:<17}" .format (
143
- ( test_type . desc ()) ,
147
+ test_desc ,
144
148
sz ,
145
149
str (scheme ),
146
150
"({}{}):" .format (compile_mode , opt_label ),
@@ -175,6 +179,16 @@ class SCHEME(Enum):
175
179
if self == SCHEME .MLKEM1024 :
176
180
return "1024"
177
181
182
+ def from_k (k ):
183
+ if isinstance (k , str ):
184
+ k = int (k )
185
+ if k == 2 :
186
+ return SCHEME .MLKEM512
187
+ if k == 3 :
188
+ return SCHEME .MLKEM768
189
+ if k == 4 :
190
+ return SCHEME .MLKEM1024
191
+
178
192
179
193
class TEST_TYPES (Enum ):
180
194
FUNC = 1
@@ -216,6 +230,9 @@ class TEST_TYPES(Enum):
216
230
f"Could not find example { s } . Examples: { list (map (lambda e : str .lower (e .name ), TEST_TYPES .examples ()))} "
217
231
)
218
232
233
+ def __str__ (self ):
234
+ return self .desc ()
235
+
219
236
def desc (self ):
220
237
if self == TEST_TYPES .FUNC :
221
238
return "Functional Test"
@@ -664,20 +681,93 @@ class Tests:
664
681
self .check_fail ()
665
682
666
683
def cbmc (self ):
684
+
685
+ def list_proofs ():
686
+ cmd_str = ["./proofs/cbmc/list_proofs.sh" ]
687
+ p = subprocess .run (cmd_str , capture_output = True , universal_newlines = False )
688
+ proofs = filter (lambda s : s .strip () != "" , p .stdout .decode ().split ("\n " ))
689
+ return list (proofs )
690
+
691
+ if self .args .list_functions :
692
+ for p in list_proofs ():
693
+ print (p )
694
+ exit (0 )
695
+
696
+ def run_cbmc_single_step (mlkem_k , proofs ):
697
+ envvars = {"MLKEM_K" : mlkem_k }
698
+ scheme = SCHEME .from_k (mlkem_k )
699
+ num_proofs = len (proofs )
700
+ for i , func in enumerate (proofs ):
701
+ log = logger (f"CBMC ({ i + 1 } /{ num_proofs } )" , scheme , None , None )
702
+ log .info (f"Starting CBMC proof for { func } " )
703
+ start = time .time ()
704
+ if self .args .verbose is False :
705
+ extra_args = {
706
+ "stdout" : subprocess .DEVNULL ,
707
+ "stderr" : subprocess .DEVNULL ,
708
+ }
709
+ else :
710
+ extra_args = {}
711
+ try :
712
+ p = subprocess .run (
713
+ [
714
+ "python3" ,
715
+ "run-cbmc-proofs.py" ,
716
+ "--summarize" ,
717
+ "--no-coverage" ,
718
+ "-p" ,
719
+ func ,
720
+ ]
721
+ + self .make_j (),
722
+ cwd = "proofs/cbmc" ,
723
+ env = os .environ .copy () | envvars ,
724
+ capture_output = True ,
725
+ timeout = self .args .timeout ,
726
+ )
727
+ except subprocess .TimeoutExpired :
728
+ log .error (f" TIMEOUT (after { self .args .timeout } s)" )
729
+ log .error (p .stderr )
730
+ self .fail (f"CBMC proof for { func } " )
731
+ if self .args .fail_upon_error :
732
+ log .error (
733
+ "Aborting proofs, as requested by -f/--fail-upon-error"
734
+ )
735
+ exit (1 )
736
+ continue
737
+
738
+ end = time .time ()
739
+ dur = int (end - start )
740
+ if p .returncode != 0 :
741
+ log .error (f" FAILED (after { dur } s)" )
742
+ log .error (p .stderr .decode ())
743
+ self .fail (f"CBMC proof for { func } " )
744
+ else :
745
+ log .info (f" SUCCESS (after { dur } s)" )
746
+
667
747
def run_cbmc (mlkem_k ):
748
+ proofs = list_proofs ()
749
+ if self .args .start_with is not None :
750
+ try :
751
+ idx = proofs .index (self .args .start_with )
752
+ proofs = proofs [idx :]
753
+ except ValueError :
754
+ log .error (
755
+ "Could not find function {self.args.start_with}. Running all proofs"
756
+ )
757
+ if self .args .proof is not None :
758
+ proofs = self .args .proof
759
+
760
+ if self .args .single_step :
761
+ run_cbmc_single_step (mlkem_k , proofs )
762
+ return
668
763
envvars = {"MLKEM_K" : mlkem_k }
669
- p = subprocess .Popen (
670
- [
671
- "python3" ,
672
- "run-cbmc-proofs.py" ,
673
- "--summarize" ,
674
- "--no-coverage" ,
675
- ]
764
+ p = subprocess .run (
765
+ ["python3" , "run-cbmc-proofs.py" , "--summarize" , "--no-coverage" , "-p" ]
766
+ + proofs
676
767
+ self .make_j (),
677
768
cwd = "proofs/cbmc" ,
678
769
env = os .environ .copy () | envvars ,
679
770
)
680
- p .communicate ()
681
771
682
772
if p .returncode != 0 :
683
773
self .fail (f"CBMC proofs for k={ mlkem_k } " )
@@ -895,6 +985,50 @@ def cli():
895
985
default = "ALL" ,
896
986
)
897
987
988
+ cbmc_parser .add_argument (
989
+ "--single-step" ,
990
+ help = "Run one proof a time. This is useful for debugging" ,
991
+ action = "store_true" ,
992
+ default = False ,
993
+ )
994
+
995
+ cbmc_parser .add_argument (
996
+ "--start-with" ,
997
+ help = "When --single-step is set, start with given proof and proceed in alphabetical order" ,
998
+ default = None ,
999
+ )
1000
+
1001
+ cbmc_parser .add_argument (
1002
+ "-p" ,
1003
+ "--proof" ,
1004
+ nargs = '+' ,
1005
+ help = "Space separated list of functions for which to run the CBMC proofs." ,
1006
+ default = None ,
1007
+ )
1008
+
1009
+ cbmc_parser .add_argument (
1010
+ "--timeout" ,
1011
+ help = "Timeout for individual CBMC proofs, in seconds" ,
1012
+ type = int ,
1013
+ default = 3600 ,
1014
+ )
1015
+
1016
+ cbmc_parser .add_argument (
1017
+ "-f" ,
1018
+ "--fail-upon-error" ,
1019
+ help = "Stop upon first CBMC proof failure" ,
1020
+ action = "store_true" ,
1021
+ default = False ,
1022
+ )
1023
+
1024
+ cbmc_parser .add_argument (
1025
+ "-l" ,
1026
+ "--list-functions" ,
1027
+ help = "Don't run any proofs, but list all functions for which CBMC proofs are available" ,
1028
+ action = "store_true" ,
1029
+ default = False ,
1030
+ )
1031
+
898
1032
# func arguments
899
1033
func_parser = cmd_subparsers .add_parser (
900
1034
"func" ,
0 commit comments