Writing code is hard; proving it correct is even harder. As the scale of
verified software projects reaches new heights, the problem of efficiently
verifying large amounts of software becomes more and more salient. Nowhere is
this issue more evident than in the context of verified cryptographic
libraries. To achieve feature-parity and be competitive with unverified
cryptographic libraries, a very large number of algorithms and APIs need to be
verified. However, the task is oftentimes repetitive, and factoring out
commonality between algorithms is fraught with difficulties, requiring until
now a significant amount of manual effort.
This paper shows how a judicious combination of known functional programming
techniques leads to an order-of-magnitude improvement in the amount of verified
code produced by the popular HACL* cryptographic library, without compromising
performance. We review three techniques that build upon each other, in order of
increasing sophistication. First, we use dependent types to crisply capture the
specification and state machine of a block algorithm, a cryptographic notion
that was until now only informally and imprecisely specified. Next, we rely on
partial evaluation to author a higher-order, stateful functor that transforms
any unsafe block API into a safe counterpart. Finally, we rely on elaborator
reflection to automate the very process of authoring a functor, using a
code-rewriting tactic. This culminates in a style akin to templatized C++ code,
but relying on a userland tactic and partial evaluation, rather than built-in
compiler support