@@ -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"
@@ -662,20 +679,99 @@ class Tests:
662
679
self .check_fail ()
663
680
664
681
def cbmc (self ):
682
+
683
+ def list_proofs ():
684
+ cmd_str = ["./proofs/cbmc/list_proofs.sh" ]
685
+ p = subprocess .run (cmd_str , capture_output = True , universal_newlines = False )
686
+ proofs = filter (lambda s : s .strip () != "" , p .stdout .decode ().split ("\n " ))
687
+ return list (proofs )
688
+
689
+ if self .args .list_functions :
690
+ for p in list_proofs ():
691
+ print (p )
692
+ exit (0 )
693
+
694
+ def run_cbmc_single_step (mlkem_k , proofs ):
695
+ envvars = {"MLKEM_K" : mlkem_k }
696
+ scheme = SCHEME .from_k (mlkem_k )
697
+ num_proofs = len (proofs )
698
+ for i , func in enumerate (proofs ):
699
+ log = logger (f"CBMC ({ i + 1 } /{ num_proofs } )" , scheme , None , None )
700
+ log .info (f"Starting CBMC proof for { func } " )
701
+ start = time .time ()
702
+ if self .args .verbose is False :
703
+ extra_args = {
704
+ "stdout" : subprocess .DEVNULL ,
705
+ "stderr" : subprocess .DEVNULL ,
706
+ }
707
+ else :
708
+ extra_args = {}
709
+ try :
710
+ p = subprocess .run (
711
+ [
712
+ "python3" ,
713
+ "run-cbmc-proofs.py" ,
714
+ "--summarize" ,
715
+ "--no-coverage" ,
716
+ "-p" ,
717
+ func ,
718
+ ]
719
+ + self .make_j (),
720
+ cwd = "proofs/cbmc" ,
721
+ env = os .environ .copy () | envvars ,
722
+ capture_output = True ,
723
+ timeout = self .args .timeout ,
724
+ )
725
+ except subprocess .TimeoutExpired :
726
+ log .error (f" TIMEOUT (after { self .args .timeout } s)" )
727
+ log .error (p .stderr )
728
+ self .fail (f"CBMC proof for { func } " )
729
+ if self .args .fail_upon_error :
730
+ log .error (
731
+ "Aborting proofs, as requested by -f/--fail-upon-error"
732
+ )
733
+ exit (1 )
734
+ continue
735
+
736
+ end = time .time ()
737
+ dur = int (end - start )
738
+ if p .returncode != 0 :
739
+ log .error (f" FAILED (after { dur } s)" )
740
+ log .error (p .stderr .decode ())
741
+ self .fail (f"CBMC proof for { func } " )
742
+ else :
743
+ log .info (f" SUCCESS (after { dur } s)" )
744
+
665
745
def run_cbmc (mlkem_k ):
746
+ proofs = list_proofs ()
747
+ if self .args .start_with is not None :
748
+ try :
749
+ idx = proofs .index (self .args .start_with )
750
+ proofs = proofs [idx :]
751
+ except ValueError :
752
+ log .error (
753
+ "Could not find function {self.args.start_with}. Running all proofs"
754
+ )
755
+ if self .args .proof is not None :
756
+ try :
757
+ idx = proofs .index (self .args .proof )
758
+ proofs = [proofs [idx ]]
759
+ except ValueError :
760
+ log .error (
761
+ "Could not find function {self.args.}. Running all proofs"
762
+ )
763
+
764
+ if self .args .single_step :
765
+ run_cbmc_single_step (mlkem_k , proofs )
766
+ return
666
767
envvars = {"MLKEM_K" : mlkem_k }
667
- p = subprocess .Popen (
668
- [
669
- "python3" ,
670
- "run-cbmc-proofs.py" ,
671
- "--summarize" ,
672
- "--no-coverage" ,
673
- ]
768
+ p = subprocess .run (
769
+ ["python3" , "run-cbmc-proofs.py" , "--summarize" , "--no-coverage" , "-p" ]
770
+ + proofs
674
771
+ self .make_j (),
675
772
cwd = "proofs/cbmc" ,
676
773
env = os .environ .copy () | envvars ,
677
774
)
678
- p .communicate ()
679
775
680
776
if p .returncode != 0 :
681
777
self .fail (f"CBMC proofs for k={ mlkem_k } " )
@@ -893,6 +989,49 @@ def cli():
893
989
default = "ALL" ,
894
990
)
895
991
992
+ cbmc_parser .add_argument (
993
+ "--single-step" ,
994
+ help = "Run one proof a time. This is useful for debugging" ,
995
+ action = "store_true" ,
996
+ default = False ,
997
+ )
998
+
999
+ cbmc_parser .add_argument (
1000
+ "--start-with" ,
1001
+ help = "When --single-step is set, start with given proof and proceed in alphabetical order" ,
1002
+ default = None ,
1003
+ )
1004
+
1005
+ cbmc_parser .add_argument (
1006
+ "-p" ,
1007
+ "--proof" ,
1008
+ help = "Only run the proof for the specified function." ,
1009
+ default = None ,
1010
+ )
1011
+
1012
+ cbmc_parser .add_argument (
1013
+ "--timeout" ,
1014
+ help = "Timeout for individual CBMC proofs, in seconds" ,
1015
+ type = int ,
1016
+ default = 3600 ,
1017
+ )
1018
+
1019
+ cbmc_parser .add_argument (
1020
+ "-f" ,
1021
+ "--fail-upon-error" ,
1022
+ help = "Stop upon first CBMC proof failure" ,
1023
+ action = "store_true" ,
1024
+ default = False ,
1025
+ )
1026
+
1027
+ cbmc_parser .add_argument (
1028
+ "-l" ,
1029
+ "--list-functions" ,
1030
+ help = "Don't run any proofs, but list all functions for which CBMC proofs are available" ,
1031
+ action = "store_true" ,
1032
+ default = False ,
1033
+ )
1034
+
896
1035
# func arguments
897
1036
func_parser = cmd_subparsers .add_parser (
898
1037
"func" ,
0 commit comments