Commit 131dd1a6 authored by benoit's avatar benoit
Browse files

space reduction = 16 pages

parent 52a0a42c
......@@ -4,11 +4,11 @@ SOURCES= code-tweetnacl.tex collection.bib conclusion.tex coq.tex highlevel.tex
PREVIOUS= csf-supplementary/previous.tex csf-supplementary/usenix-*.tex csf-supplementary/tweetverif-USENIX.pdf csf-supplementary/tweetverif-SP.pdf
tweetverif.pdf: $(SOURCES)
latex
pdflatex tweetverif.tex
bibtex tweetverif
pdflatex tweetverif.tex
pdflatex tweetverif.tex
./latexrun.py tweetverif.tex
# pdflatex tweetverif.tex
# bibtex tweetverif
# pdflatex tweetverif.tex
# pdflatex tweetverif.tex
csf-supplementary/previous.pdf: $(PREVIOUS)
make -C csf-supplementary
......
......@@ -35,6 +35,6 @@ As follow, we provide the explanations of the above changes to TweetNaCl's code.
\item lines 79-82: VST does not support \TNaCle{for} loops over \TNaCle{i64}, we convert it into an \TNaCle{int}.\\
In the function calls of \TNaCle{sel25519}, the specifications requires the use of \TNaCle{int}, the value of \TNaCle{r} being either \TNaCle{0} or \TNaCle{1}, we consider this change safe.
\item Lines 90-101: The \TNaCle{for} loop does not add any benefits to the code. By removing it we simplify the source and the verification steps as we do not need to deal with pointer arithmetic. As a result, \TNaCle{x} can be limited to only 16 \TNaCle{i64}, \ie \TNaCle{gf}.
\item Lines 90-101: The \TNaCle{for} loop does not add any benefits to the code. By removing it we simplify the source and the verification steps as we do not need to deal with pointer arithmetic. Thus, \TNaCle{x} is limited to only 16 \TNaCle{i64}, \ie \TNaCle{gf}.
\end{itemize}
......@@ -15,11 +15,11 @@ In our case we rely on:
used by Coq must be consistent in order to trust the proofs. As an axiom,
we assume that the functional extensionality is also consistent with that logic.
$$\forall x, f(x) = g(x) \implies f = g$$
\begin{lstlisting}[language=Coq,belowskip=-0.25 \baselineskip]
Lemma f_ext: forall (A B:Type),
forall (f g: A -> B),
(forall x, f(x) = g(x)) -> f = g.
\end{lstlisting}
% \begin{lstlisting}[language=Coq,belowskip=-0.25 \baselineskip]
% Lemma f_ext: forall (A B:Type),
% forall (f g: A -> B),
% (forall x, f(x) = g(x)) -> f = g.
% \end{lstlisting}
\item \textbf{Verifiable Software Toolchain}. This framework developed at
Princeton allows a user to prove that a Clight code matches pure Coq
......@@ -165,3 +165,4 @@ mathematical definitions as given in~\cite[Sec.~2]{Ber06}.
Therefore in addition to proving the mathematical correctness of TweetNaCl,
we also increases the trust of other works such as
\cite{zinzindohoue2017hacl,Erbsen2016SystematicSO} which rely on RFC~7748.
\vspace{-0.3cm}
\ No newline at end of file
......@@ -3,7 +3,6 @@
\subsection{Montgomery Ladder}
\label{subsubsec:coq-ladder}
~
Generic definition of the ladder:
\begin{lstlisting}[language=Coq]
......@@ -91,7 +90,6 @@ end.
\subsection{RFC in Coq}
\label{subsubsec:RFC-Coq}
~
Instantiation of the Class \Coqe{Ops} with operations over \Z and modulo \p.
\begin{lstlisting}[language=Coq]
Definition modP (x:Z) : Z :=
......
......@@ -48,6 +48,7 @@ $a^2 - 4$ is not a square in $\K$, we prove the correctness of the ladder (\tref
\begin{figure}[h]
\centering
\include{tikz/highlevel1}
\vspace{-0.5cm}
\caption{Overview of the proof of Montgomery ladder's correctness}
\label{tikz:ProofHighLevel1}
\end{figure}
......@@ -193,13 +194,13 @@ After having verified the group properties, it follows that the bijection is a g
\begin{lemma}
\label{lemma:bij-ecc}
Let $M_{a,b}$ be a Montgomery curve, define
%\vspace{-0.3em}
\vspace{-0.3em}
$$a' = \frac{3-a^2}{3b^2} \text{\ \ \ \ and\ \ \ \ } b' = \frac{2a^3 - 9a}{27b^3}.$$
then $E_{a',b'}$ is a Weierstra{\ss} curve, and the mapping
$\varphi : M_{a,b} \mapsto E_{a',b'}$ defined as:
%\vspace{-0.5em}
\vspace{-0.5em}
\begin{align*}
\varphi(\Oinf_M) & = \Oinf_E \\
\varphi(\Oinf_M) & = \Oinf_E \\[-0.5ex]
\varphi( (x , y) ) & = \left( \frac{x}{b} + \frac{a}{3b} , \frac{y}{b} \right)
\end{align*}
is a group isomorphism between elliptic curves.
......@@ -255,7 +256,7 @@ Hypothesis mcu_no_square :
We define $\chi$ and $\chi_0$ to return the \xcoord of points on a curve.
\begin{dfn}
Let $\chi : M_{a,b}(\K) \mapsto \K \cup \{\infty\}$ and $\chi_0 : M_{a,b}(\K) \mapsto \K$ such that
%\vspace{-0.5em}
\vspace{-0.5em}
\begin{align*}
\chi((x,y)) & = x, & \chi(\Oinf) & = \infty, & & \text{and} \\[-0.5ex]
\chi_0((x,y)) & = x, & \chi_0(\Oinf) & = 0.
......@@ -269,7 +270,7 @@ Using projective coordinates we prove the formula for differential addition.% (\
let $X_1, Z_1, X_2, Z_2, X_4, Z_4 \in \K$, such that $(X_1,Z_1) \neq (0,0)$,
$(X_2,Z_2) \neq (0,0)$, $X_4 \neq 0$ and $Z_4 \neq 0$.
Define
%\vspace{-0.5em}
\vspace{-0.5em}
\begin{align*}
X_3 & = Z_4((X_1 - Z_1)(X_2+Z_2) + (X_1+Z_1)(X_2-Z_2))^2 \\[-0.5ex]
Z_3 & = X_4((X_1 - Z_1)(X_2+Z_2) - (X_1+Z_1)(X_2-Z_2))^2
......@@ -287,8 +288,8 @@ Similarly, we also prove the formula for point doubling.% (\lref{lemma:xDBL}).
Let $M_{a,b}$ be a Montgomery curve such that $a^2-4$ is not a square in \K, and
let $X_1, Z_1 \in \K$, such that $(X_1,Z_1) \neq (0,0)$. Define
\begin{align*}
c & = (X_1 + Z_1)^2 - (X_1 - Z_1)^2 \\
X_3 & = (X_1 + Z_1)^2(X_1-Z_1)^2 \\
c & = (X_1 + Z_1)^2 - (X_1 - Z_1)^2 \\[-0.5ex]
X_3 & = (X_1 + Z_1)^2(X_1-Z_1)^2 \\[-0.5ex]
Z_3 & = c\Big((X_1 + Z_1)^2+\frac{a-2}{4}\times c\Big),
\end{align*}
then for any point $P_1$ in $M_{a,b}(\K)$ such that $X_1/Z_1 = \chi(P_1)$,
......@@ -355,6 +356,7 @@ The white tiles are definitions while green tiles are important lemmas and theor
\begin{figure}[h]
\centering
\include{tikz/highlevel2}
\vspace{-0.5cm}
\caption{Proof dependencies for the correctness of X25519.}
\label{tikz:ProofHighLevel2}
\end{figure}
......@@ -514,12 +516,12 @@ We then specialize the basic operations in order to speed up the verification
of formulas by using rewrite rules:
\begin{equation*}
\begin{split}
(a,0) + (b,0) &= (a+b, 0)\\
(a,0) + (b,0) &= (a+b, 0)\\[-0.5ex]
(a, 0)^{-1} &= (a^{-1}, 0)
\end{split}
\qquad
\begin{split}
(a,0) \cdot (b,0) &= (a \cdot b, 0)\\
(a,0) \cdot (b,0) &= (a \cdot b, 0)\\[-0.5ex]
(0,a)^{-1} &= (0,(2\cdot a)^{-1})
\end{split}
\end{equation*}
......@@ -558,16 +560,16 @@ We now study the case of the scalar multiplication and show similar proofs.
\label{lemma:proj}
For all $n \in \N$, for all point $P\in\F{p}\times\F{p}$ on the curve
$M_{486662,1}(\F{p})$ (respectively on the quadratic twist $M_{486662,2}(\F{p})$), we have
%\vspace{-0.3em}
\vspace{-0.3em}
\begin{align*}
P & \in M_{486662,1}(\F{p}) & \implies \varphi_c(n \cdot P) & = n \cdot \varphi_c(P), & & \text{and} \\
P & \in M_{486662,1}(\F{p}) & \implies \varphi_c(n \cdot P) & = n \cdot \varphi_c(P), & & \text{and} \\[-0.5ex]
P & \in M_{486662,2}(\F{p}) & \implies \varphi_t(n \cdot P) & = n \cdot \varphi_t(P).
\end{align*}
\end{lemma}
Notice that
%\vspace{-0.5em}
\vspace{-0.5em}
\begin{align*}
\forall P \in M_{486662,1}(\F{p}), & & \psi(\chi_0(\varphi_c(P))) & = \chi_0(P), & & \text{and} \\
\forall P \in M_{486662,1}(\F{p}), & & \psi(\chi_0(\varphi_c(P))) & = \chi_0(P), & & \text{and} \\[-0.5ex]
\forall P \in M_{486662,2}(\F{p}), & & \psi(\chi_0(\varphi_t(P))) & = \chi_0(P).
\end{align*}
......
#!/usr/bin/env python3
# Copyright (c) 2013, 2014 Austin Clements
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import os
import errno
import argparse
import shlex
import json
import subprocess
import re
import collections
import hashlib
import shutil
import curses
import filecmp
import io
import traceback
import time
try:
import fcntl
except ImportError:
# Non-UNIX platform
fcntl = None
def debug(string, *args):
if debug.enabled:
print(string.format(*args), file=sys.stderr)
debug.enabled = False
def debug_exc():
if debug.enabled:
traceback.print_exc()
def main():
# Parse command-line
arg_parser = argparse.ArgumentParser(
description='''A 21st century LaTeX wrapper,
%(prog)s runs latex (and bibtex) the right number of times so you
don't have to,
strips the log spew to make errors visible,
and plays well with standard build tools.''')
arg_parser.add_argument(
'-o', metavar='FILE', dest='output', default=None,
help='Output file name (default: derived from input file)')
arg_parser.add_argument(
'--latex-cmd', metavar='CMD', default='pdflatex',
help='Latex command (default: %(default)s)')
arg_parser.add_argument(
'--latex-args', metavar='ARGS', type=arg_parser_shlex,
help='Additional command-line arguments for latex.'
' This will be parsed and split using POSIX shell rules.')
arg_parser.add_argument(
'--bibtex-cmd', metavar='CMD', default='bibtex',
help='Bibtex command (default: %(default)s)')
arg_parser.add_argument(
'--bibtex-args', metavar='ARGS', type=arg_parser_shlex,
help='Additional command-line arguments for bibtex')
arg_parser.add_argument(
'--max-iterations', metavar='N', type=int, default=10,
help='Max number of times to run latex before giving up'
' (default: %(default)s)')
arg_parser.add_argument(
'-W', metavar='(no-)CLASS',
action=ArgParserWarnAction, dest='nowarns', default=set(['underfull']),
help='Enable/disable warning from CLASS, which can be any package name, '
'LaTeX warning class (e.g., font), bad box type '
'(underfull, overfull, loose, tight), or "all"')
arg_parser.add_argument(
'-O', metavar='DIR', dest='obj_dir', default='latex.out',
help='Directory for intermediate files and control database '
'(default: %(default)s)')
arg_parser.add_argument(
'--color', choices=('auto', 'always', 'never'), default='auto',
help='When to colorize messages')
arg_parser.add_argument(
'--verbose-cmds', action='store_true', default=False,
help='Print commands as they are executed')
arg_parser.add_argument(
'--debug', action='store_true',
help='Enable detailed debug output')
actions = arg_parser.add_argument_group('actions')
actions.add_argument(
'--clean-all', action='store_true', help='Delete output files')
actions.add_argument(
'file', nargs='?', help='.tex file to compile')
args = arg_parser.parse_args()
if not any([args.clean_all, args.file]):
arg_parser.error('at least one action is required')
args.latex_args = args.latex_args or []
args.bibtex_args = args.bibtex_args or []
verbose_cmd.enabled = args.verbose_cmds
debug.enabled = args.debug
# A note about encodings: POSIX encoding is a mess; TeX encoding
# is a disaster. Our goal is to make things no worse, so we want
# byte-accurate round-tripping of TeX messages. Since TeX
# messages are *basically* text, we use strings and
# surrogateescape'ing for both input and output. I'm not fond of
# setting surrogateescape globally, but it's far easier than
# dealing with every place we pass TeX output through.
# Conveniently, JSON can round-trip surrogateescape'd strings, so
# our control database doesn't need special handling.
sys.stdout = io.TextIOWrapper(
sys.stdout.buffer, encoding=sys.stdout.encoding,
errors='surrogateescape', line_buffering=sys.stdout.line_buffering)
sys.stderr = io.TextIOWrapper(
sys.stderr.buffer, encoding=sys.stderr.encoding,
errors='surrogateescape', line_buffering=sys.stderr.line_buffering)
Message.setup_color(args.color)
# Open control database.
dbpath = os.path.join(args.obj_dir, '.latexrun.db')
if not os.path.exists(dbpath) and os.path.exists('.latexrun.db'):
# The control database used to live in the source directory.
# Support this for backwards compatibility.
dbpath = '.latexrun.db'
try:
db = DB(dbpath)
except (ValueError, OSError) as e:
print('error opening {}: {}'.format(e.filename if hasattr(e, 'filename')
else dbpath, e),
file=sys.stderr)
debug_exc()
sys.exit(1)
# Clean
if args.clean_all:
try:
db.do_clean(args.obj_dir)
except OSError as e:
print(e, file=sys.stderr)
debug_exc()
sys.exit(1)
# Build
if not args.file:
return
task_commit = None
try:
task_latex = LaTeX(db, args.file, args.latex_cmd, args.latex_args,
args.obj_dir, args.nowarns)
task_commit = LaTeXCommit(db, task_latex, args.output)
task_bibtex = BibTeX(db, task_latex, args.bibtex_cmd, args.bibtex_args,
args.nowarns, args.obj_dir)
tasks = [task_latex, task_commit, task_bibtex]
stable = run_tasks(tasks, args.max_iterations)
# Print final task output and gather exit status
status = 0
for task in tasks:
status = max(task.report(), status)
if not stable:
print('error: files are still changing after {} iterations; giving up'
.format(args.max_iterations), file=sys.stderr)
status = max(status, 1)
except TaskError as e:
print(str(e), file=sys.stderr)
debug_exc()
status = 1
# Report final status, if interesting
fstatus = 'There were errors' if task_commit is None else task_commit.status
if fstatus:
output = args.output
if output is None:
if task_latex.get_outname() is not None:
output = os.path.basename(task_latex.get_outname())
else:
output = 'output'
if Message._color:
terminfo.send('bold', ('setaf', 1))
print('{}; {} not updated'.format(fstatus, output))
if Message._color:
terminfo.send('sgr0')
sys.exit(status)
def arg_parser_shlex(string):
"""Argument parser for shell token lists."""
try:
return shlex.split(string)
except ValueError as e:
raise argparse.ArgumentTypeError(str(e)) from None
class ArgParserWarnAction(argparse.Action):
def __call__(self, parser, namespace, value, option_string=None):
nowarn = getattr(namespace, self.dest)
if value == 'all':
nowarn.clear()
elif value.startswith('no-'):
nowarn.add(value[3:])
else:
nowarn.discard(value)
setattr(namespace, self.dest, nowarn)
def verbose_cmd(args, cwd=None, env=None):
if verbose_cmd.enabled:
cmd = ' '.join(map(shlex.quote, args))
if cwd is not None:
cmd = '(cd {} && {})'.format(shlex.quote(cwd), cmd)
if env is not None:
for k, v in env.items():
if os.environ.get(k) != v:
cmd = '{}={} {}'.format(k, shlex.quote(v), cmd)
print(cmd, file=sys.stderr)
verbose_cmd.enabled = False
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
class DB:
"""A latexrun control database."""
_VERSION = 'latexrun-db-v2'
def __init__(self, filename):
self.__filename = filename
# Make sure database directory exists
if os.path.dirname(self.__filename):
os.makedirs(os.path.dirname(self.__filename), exist_ok=True)
# Lock the database if possible. We don't release this lock
# until the process exits.
lockpath = self.__filename + '.lock'
if fcntl is not None:
lockfd = os.open(lockpath, os.O_CREAT|os.O_WRONLY|os.O_CLOEXEC, 0o666)
# Note that this is actually an fcntl lock, not a lockf
# lock. Don't be fooled.
fcntl.lockf(lockfd, fcntl.LOCK_EX, 1)
try:
fp = open(filename, 'r')
except FileNotFoundError:
debug('creating new database')
self.__val = {'version': DB._VERSION}
else:
debug('loading database')
self.__val = json.load(fp)
if 'version' not in self.__val:
raise ValueError('file exists, but does not appear to be a latexrun database'.format(filename))
if self.__val['version'] != DB._VERSION:
raise ValueError('unknown database version {!r}'
.format(self.__val['version']))
def commit(self):
debug('committing database')
# Atomically commit database
tmp_filename = self.__filename + '.tmp'
with open(tmp_filename, 'w') as fp:
json.dump(self.__val, fp, indent=2, separators=(',', ': '))
fp.flush()
os.fsync(fp.fileno())
os.rename(tmp_filename, self.__filename)
def get_summary(self, task_id):
"""Return the recorded summary for the given task or None."""
return self.__val.get('tasks', {}).get(task_id)
def set_summary(self, task_id, summary):
"""Set the summary for the given task."""
self.__val.setdefault('tasks', {})[task_id] = summary
def add_clean(self, filename):
"""Add an output file to be cleaned.
Unlike the output files recorded in the task summaries,
cleanable files strictly accumulate until a clean is
performed.
"""
self.__val.setdefault('clean', {})[filename] = hash_cache.get(filename)
def do_clean(self, obj_dir=None):
"""Remove output files and delete database.
If obj_dir is not None and it is empty after all files are
removed, it will also be removed.
"""
for f, want_hash in self.__val.get('clean', {}).items():
have_hash = hash_cache.get(f)
if have_hash is not None:
if want_hash == have_hash:
debug('unlinking {}', f)
hash_cache.invalidate(f)
os.unlink(f)
else:
print('warning: {} has changed; not removing'.format(f),
file=sys.stderr)
self.__val = {'version': DB._VERSION}
try:
os.unlink(self.__filename)
except FileNotFoundError:
pass
if obj_dir is not None:
try:
os.rmdir(obj_dir)
except OSError:
pass
class HashCache:
"""Cache of file hashes.
As latexrun reaches fixed-point, it hashes the same files over and
over, many of which never change. Since hashing is somewhat
expensive, we keep a simple cache of these hashes.
"""
def __init__(self):
self.__cache = {}
def get(self, filename):
"""Return the hash of filename, or * if it was clobbered."""
try:
with open(filename, 'rb') as fp:
st = os.fstat(fp.fileno())
key = (st.st_dev, st.st_ino)
if key in self.__cache:
return self.__cache[key]
debug('hashing {}', filename)
h = hashlib.sha256()
while True:
block = fp.read(256*1024)
if not len(block):
break
h.update(block)
self.__cache[key] = h.hexdigest()
return self.__cache[key]
except (FileNotFoundError, IsADirectoryError):
return None
def clobber(self, filename):
"""If filename's hash is not known, record an invalid hash.
This can be used when filename was overwritten before we were
necessarily able to obtain its hash. filename must exist.
"""
st = os.stat(filename)
key = (st.st_dev, st.st_ino)
if key not in self.__cache:
self.__cache[key] = '*'
def invalidate(self, filename):
try:
st = os.stat(filename)
except OSError as e:
# Pessimistically wipe the whole cache
debug('wiping hash cache ({})', e)
self.__cache.clear()
else:
key = (st.st_dev, st.st_ino)
if key in self.__cache:
del self.__cache[key]
hash_cache = HashCache()
class _Terminfo:
def __init__(self):
self.__tty = os.isatty(sys.stdout.fileno())
if self.__tty:
curses.setupterm()
self.__ti = {}
def __ensure(self, cap):
if cap not in self.__ti:
if not self.__tty:
string = None
else:
string = curses.tigetstr(cap)
if string is None or b'$<' in string:
# Don't have this capability or it has a pause
string = None
self.__ti[cap] = string
return self.__ti[cap]
def has(self, *caps):
return all(self.__ensure(cap) is not None for cap in caps)
def send(self, *caps):
# Flush TextIOWrapper to the binary IO buffer
sys.stdout.flush()
for cap in caps:
# We should use curses.putp here, but it's broken in
# Python3 because it writes directly to C's buffered
# stdout and there's no way to flush that.
if isinstance(cap, tuple):
s = curses.tparm(self.__ensure(cap[0]), *cap[1:])
else:
s = self.__ensure(cap)
sys.stdout.buffer.write(s)
terminfo = _Terminfo()
class Progress:
_enabled = None
def __init__(self, prefix):
self.__prefix = prefix
if Progress._enabled is None:
Progress._enabled = (not debug.enabled) and \
terminfo.has('cr', 'el', 'rmam', 'smam')
def __enter__(self):
self.last = ''
self.update('')
return self
def __exit__(self, typ, value, traceback):
if Progress._enabled:
# Beginning of line and clear
terminfo.send('cr', 'el')
sys.stdout.flush()
def update(self, msg):
if not Progress._enabled:
return
out = '[' + self.__prefix + ']'
if msg:
out += ' ' + msg
if out != self.last:
# Beginning of line, clear line, disable wrap
terminfo.send('cr', 'el', 'rmam')
sys.stdout.write(out)
# Enable wrap
terminfo.send('smam')