\documentclass[aspectratio=169,obeyspaces,spaces,hyphens,dvipsnames]{beamer}
\usepackage[utf8]{inputenc}
\usepackage{minted}
\usepackage{hyperref}
\usepackage{xcolor}
\usepackage{pgfplots}
\usepackage{tikz}

\mode<presentation>
\usetheme{Bootlin}

\def\signed #1{{\leavevmode\unskip\nobreak\hfil\penalty50\hskip2em
  \hbox{}\nobreak\hfil(#1)%
  \parfillskip=0pt \finalhyphendemerits=0 \endgraf}}

\newsavebox\mybox
\newenvironment{aquote}[1]
  {\savebox\mybox{#1}\begin{quotation}}
  {\signed{\usebox\mybox}\end{quotation}}

\title{An overview of the crypto subsystem}
\conference{Embedded Linux Conference Europe 2017}
\authors{Boris Brezillon}
\email{boris@bootlin.com}
\institute{Bootlin}
\slidesurl{http://bootlin.com/pub/conferences/2017/elce/brezillon-crypto-framework}

\begin{document}

\addtocontents{toc}{\protect\setcounter{tocdepth}{-1}}
\section{An overview of the crypto subsystem}
\addtocontents{toc}{\protect\setcounter{tocdepth}{2}}

\begin{frame}{Boris Brezillon}
  \begin{itemize}
  \item Embedded Linux engineer and trainer at Bootlin
    \begin{itemize}
    \item Embedded Linux {\bf development}: kernel and driver
      development, system integration, boot time and power
      consumption optimization, consulting, etc.
    \item Embedded Linux, Linux driver development, Yocto Project / OpenEmbedded
      and Buildroot {\bf training courses}, with materials freely
      available under a Creative Commons license.
    \item \url{http://bootlin.com}
    \end{itemize}
  \item Contributions
    \begin{itemize}
    \item {\bf Maintainer of the NAND subsystem}
    \item {\bf Kernel support for various ARM SoCs}
    \item {\bf Contributed Marvell's crypto engine driver}
    \end{itemize}
  \item Living in {\bf Toulouse}, south west of France
  \end{itemize}
\end{frame}

\begin{frame}{What is this talk about?}
  \begin{itemize}
  \item Short introduction to some cryptographic concepts
  \item Overview of services provided by the crypto subsystem and how
        to use it
  \item Overview of the driver side of the crypto framework (how to
        implement a driver for a simple crypto engine)
  \item Random thoughts about the crypto framework
  \end{itemize}
\end{frame}

\subsection{Introduction to generic cryptographic concepts}

\begin{frame}{Cryptography? What is this?}
  \begin{itemize}
  \item Do you know Alice and Bob? If you do, take a break and come
	back in five minutes
  \item Cryptography is about protecting communications between two or
	more participants
  \item Covers three main concepts:
    \begin{itemize}
    \item {\bf Confidentiality}: content of the communication cannot be
          spied on
    \item {\bf Data integrity}: content of the communication cannot be
          altered without participants noticing
  \item {\bf Authentication}: message origin can be checked
    \end{itemize}
  \item Achieved by manipulating input/output messages and adding
        meta-data to them
  \end{itemize}
\end{frame}

\begin{frame}{Cryptography: Hash (or Digest)}
  \begin{itemize}
  \item Used to guarantee {\bf Data Integrity}
  \item Operates on a random number of input data
  \item Generates a 'unique' fixed-size signature for a specific
        input
  \item Examples: \code{SHA1}, \code{MD5}, ...
  \item Main criteria for a hash algorithm:
    \begin{itemize}
    \item Keep the probability of collision as low as possible
          (ideally null)
    \item Make it impossible to re-generate data from its hash
    \item A small modification in the data should generate a completely
          different hash
    \end{itemize}
  \end{itemize}
\end{frame}

\begin{frame}{Cryptography: Cipher}
  \begin{itemize}
  \item Used to guarantee {\bf Confidentiality}
  \item Transform the data so that someone external to the group can't
        read it
  \item Requires one or several {\bf key(s)} to encrypt/decrypt data
  \item Ciphers can be {\bf stream} or {\bf block} oriented
    \begin{itemize}
    \item {\bf Stream Ciphers}: operate on a stream of data
    \item {\bf Block Ciphers}: operate on fixed-size blocks
    \end{itemize}
  \item Ciphers can be {\bf symmetric} or {\bf asymmetric}
    \begin{itemize}
    \item {\bf Symmetric Ciphers}: the same key (called private key) is
          shared among all  participants and is used to both encrypt
          and decrypt data
    \item {\bf Asymmetric Ciphers}: a pair of public/private key is
          used. The public key can be shared with anyone and is used to
          encrypt messages sent to the owner of the private key.
          The private key is then used to decrypt messages.
    \end{itemize}
  \item Examples: \code{AES}, \code{RSA}, \code{...}
  \end{itemize}
\end{frame}

\begin{frame}{Cryptography: Block Cipher Mode}
  \begin{itemize}
  \item Block ciphers can only be used to encrypt/decrypt a single
        block of data
  \item We need a solution to handle an arbitrary number of blocks
  \item Block cipher mode is just a protocol describing how to do that
  \item Most modes require an {\bf Initialization Vector} ({\bf IV}) to
        obfuscate the transmitted data
\item Examples: \code{ECB (Electronic Codebook)}, \code{CBC (Cipher Chaining Block)}, \code{...}
  \end{itemize}
\end{frame}

\begin{frame}{Cryptography: MAC and HMAC}
  \begin{itemize}
  \item MAC stands for Message Authentication Codes
  \item Mechanism used to authenticate the sender of a message
  \item Uses a key and a transformation algorithm to generate
        authentication data
  \item MAC can be based on Hash algorithms =$>$ HMAC
  \end{itemize}
\end{frame}

\begin{frame}{Cryptography: AEAD}
  \begin{itemize}
  \item AEAD stands for Authenticated Encryption with Associated Data
  \item Combines everything in a single step (Authentication,
        Confidentiality and Data Integrity)
  \item Is usually based on existing Cipher, Hash/HMAC algorithms
  \end{itemize}
\end{frame}

\begin{frame}{Cryptography: Better introduction than mine}
  \begin{itemize}
  \item Interested in more advanced description of these concepts?
  \item Watch Gilad Ben Yossef's talk: \url{https://youtu.be/dnGbhvweNb8}, \url{https://goo.gl/x5ikkv}
  \end{itemize}
\end{frame}

\subsection{Linux Crypto Framework: Common Principles}

\begin{frame}{Linux Crypto Framework: Basic Concepts}
  \begin{itemize}
  \item Every crypto algorithm is about transforming input data into
        something else
  \begin{itemize}
    \item {\bf Transformation implementation}: represents an
          implementation of a specific algorithm (\code{struct crypto_alg})
    \item {\bf Transformation object}: an instance of a specific
          algorithm (\code{struct crypto_tfm})
  \end{itemize}
  \item Everything inherits from \code{struct crypto_alg} and
        \code{struct crypto_tfm} either directly on indirectly
  \end{itemize}
\end{frame}

\begin{frame}{Linux Crypto Framework: Basic Concepts}
  \begin{itemize}
  \item Supports a whole bunch of algorithms
  \item Here are some of them, spot the odd one
    \begin{itemize}
    \item {\bf Cipher}
    \item {\bf Hash}
    \item {\bf AEAD}
    \item {\bf HMAC}
    \item {\bf Compression}
    \end{itemize}
  \item A transformation algorithm can be a template using basic
        building blocks
  \item Examples:
    \begin{itemize}
    \item {\bf hmac(sha1)}: HMAC using SHA1 hash
    \item {\bf cbc(aes)}: CBC using AES
    \item {\bf authenc(hmac(sha1),cbc(aes))}: AEAD using HMAC based on
          SHA1 and CBC based on AES
    \end{itemize}
  \end{itemize}
\end{frame}

\subsection{Linux Crypto Framework: How to use it}

\begin{frame}{Using the crypto framework}
  \begin{center}
  \includegraphics[scale=0.22]{use-linux-crypto-framework.pdf}
  \end{center}
\end{frame}

\begin{frame}{Dummy user example}
  \begin{itemize}
  \item Disclaimer: a lot of things have been deliberately omitted to
        keep the example simple
    \begin{itemize}
    \item No error checking
    \item Code is not separated in sub-functions
    \item We only provide a simple example for a basic cipher: ecb(aes)
    \item Headers inclusion has been omitted
    \item There's no input/output parameter checking
    \item ...
    \end{itemize}
  \item To sum-up: do not use this example as a base for your
        developments, it's just here to show the different steps
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Dummy user implementation}
  \begin{columns}
    \column{0.5\textwidth}
    \begin{block}{}
      \fontsize{6}{6}\selectfont
      \begin{minted}{c}
struct encrypt_ctx {
	struct crypto_skcipher *tfm;
	struct skcipher_request *req;
	struct completion complete;
	int err;
};

static void encrypt_cb(struct crypto_async_request *req,
		       int error)
{
	struct crypto_ctx *ctx = req->data;

	if (error == -EINPROGRESS)
	      return;

	ctx->err = error;
	complete(&ctx->completion);
}

static void init(struct encrypt_ctx *ctx)
{
	/* Create a CBC(AES) algorithm instance: */
	ctx->tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
	/* Create a request and assign it a callback: */
	ctx->req = skcipher_request_alloc(ctx->tfm,
					  GFP_KERNEL);
	skcipher_request_set_callback(req,
				CRYPTO_TFM_REQ_MAY_BACKLOG,
				encrypt_cb, ctx);
	init_completion(&ctx.completion);
}
      \end{minted}
    \end{block}
    \column{0.5\textwidth}
    \begin{block}{}
      \fontsize{6}{6}\selectfont
      \begin{minted}{c}
static void cleanup(struct encrypt_ctx *ctx)
{
	skcipher_request_free(ctx->req);
	crypto_free_skcipher(ctx->tfm);
}

int encrypt(void *key, void *data, unsigned int size)
{
	struct encrypt_ctx ctx;
	struct scatterlist sg;
	int ret;

	init(&ctx);

	/* Set the private key: */
	crypto_skcipher_setkey(ctx.tfm, key, 32);

	/*Now assign the src/dst buffer and encrypt data: */
	sg_init_one(&sg, data, size);
	skcipher_request_set_crypt(ctx.req, &sg, &sg, len,
				   NULL);
	ret = crypto_skcipher_encrypt(ctx.req);
	if (ret == -EINPROGRESS || ret == -EBUSY) {
		wait_for_completion(&ctx.completion);
		ret = ctx.err;
	}

	cleanup(&ctx);

	return ctx.err;
}
      \end{minted}
    \end{block}
  \end{columns}
\end{frame}

\begin{frame}{In-kernel crypto users}
  \begin{itemize}
  \item Disk encryption: {\bf dm-crypt}
  \item Network protocols:
    \begin{itemize}
    \item {\bf IPSec}
    \item {\bf 802.11}
    \item {\bf 802.15.4}
    \item {\bf Bluetooth}
    \item {\bf ...}
    \end{itemize}
  \item File systems
  \item Device drivers
  \item \code{git grep "crypto_alloc_"} and you'll find a lot more
  \end{itemize}
\end{frame}

\begin{frame}{Using kernel crypto features from userspace}
  \begin{itemize}
  \item People have pushed for this for quite some time
  \item Motivation for this:
    \begin{itemize}
    \item Have a single base of code instead of duplicating it in
          userspace
    \item Use hardware crypto engines that are only exposed to the
          kernel world
    \end{itemize}
  \item Two competing solutions:
    \begin{itemize}
    \item \code{cryptodev}: is an out-of-tree solution, ported from the
          BSD world
    \item \code{AF_ALG}: the in-tree/official solution
    \end{itemize}
  \end{itemize}
\end{frame}

\begin{frame}{\code{cryptodev} vs \code{AF_ALG}}
  \begin{itemize}
  \item \code{cryptodev}:
    \begin{itemize}
    \item The first solution to have emerged
    \item Is said to have more performance than \code{AF_ALG}
    \item Still maintained as an out-of-tree kernel module
    \item Interfaces with the in-kernel crypto framework
    \item Exposes a device under /dev/crypto
    \item Uses ioctls to setup the crypto context
    \item Natively supported in OpenSSL
    \end{itemize}
  \item \code{AF_ALG}:
    \begin{itemize}
    \item Supported in mainline (appeared in Linux 2.6.38)
    \item Manipulated through a \code{netlink} socket
    \item Supported in OpenSSL as an out-of-tree module
    \end{itemize}
  \item {\bf Which one should I use???}
  \end{itemize}
\end{frame}

\definecolor{bblue}{HTML}{4F81BD}
\definecolor{rred}{HTML}{C0504D}
\definecolor{ggreen}{HTML}{9BBB59}
\definecolor{ppurple}{HTML}{9F4C7C}

\begin{frame}{OpenSSL Speed Test: Sequential}
  \begin{tikzpicture}
    \begin{axis}[
      width  = 0.85*\textwidth,
      height = 6cm,
      major x tick style = transparent,
      ybar,
      bar width=12pt,
      ymajorgrids = true,
      ylabel = {Speed in Kbytes/sec},
      ylabel style={yshift=0.8cm},
      symbolic x coords={16b,1kb,8kb},
      xtick = data,
      scaled y ticks = false,
      enlarge x limits = 0.25,
      ymin = 0, ymax = 60000,
      legend cell align=left,
      legend style = {
        at={(1,1.05)},
        anchor=south east,
        column sep=1ex
      }
    ]
      \addplot[style={bblue,fill=bblue,mark=none}]
        coordinates {(16b, 181.39) (1kb, 7244.12) (8kb, 40828.93)};
      \addplot[style={rred,fill=rred,mark=none}]
        coordinates {(16b, 98.14) (1kb, 5999.62) (8kb, 33745.05)};
    \legend{Cryptodev (Marvell CESA),AF\_ALG (Marvell CESA)}
    \end{axis}
  \end{tikzpicture}
\end{frame}

\begin{frame}{OpenSSL Speed Test: Sequential}
  \begin{tikzpicture}
    \begin{axis}[
      width  = 0.85*\textwidth,
      height = 6cm,
      major x tick style = transparent,
      ybar,
      bar width=12pt,
      ymajorgrids = true,
      ylabel = {Speed in Kbytes/sec},
      ylabel style={yshift=0.8cm},
      symbolic x coords={16b,1kb,8kb},
      xtick = data,
      scaled y ticks = false,
      enlarge x limits = 0.25,
      ymin = 0, ymax = 60000,
      legend cell align=left,
      legend style = {
        at={(1,1.05)},
        anchor=south east,
        column sep=1ex
      }
    ]
      \addplot[style={bblue,fill=bblue,mark=none}]
        coordinates {(16b, 181.39) (1kb, 7244.12) (8kb, 40828.93)};
      \addplot[style={rred,fill=rred,mark=none}]
        coordinates {(16b, 98.14) (1kb, 5999.62) (8kb, 33745.05)};
      \addplot[style={ggreen,fill=ggreen,mark=none}]
        coordinates {(16b, 33257.70) (1kb, 54681.26) (8kb, 54829.06)};
    \legend{Cryptodev (Marvell CESA),AF\_ALG (Marvell CESA), No Hardware Engine}
    \end{axis}
  \end{tikzpicture}
\end{frame}

\begin{frame}{OpenSSL Speed Test: Parallelization (128 Threads)}
  \begin{tikzpicture}
    \begin{axis}[
      width  = 0.85*\textwidth,
      height = 6cm,
      major x tick style = transparent,
      ybar,
      bar width=12pt,
      ymajorgrids = true,
      ylabel = {Speed in Kbytes/sec},
      ylabel style={yshift=0.8cm},
      symbolic x coords={16b,1kb,8kb},
      xtick = data,
      scaled y ticks = false,
      enlarge x limits = 0.25,
      ymin = 0, ymax = 330000,
      legend cell align=left,
      legend style = {
        at={(1,1.05)},
        anchor=south east,
        column sep=1ex
      }
    ]
      \addplot[style={bblue,fill=bblue,mark=none}]
        coordinates {(16b, 690.37) (1kb, 35690.82) (8kb, 213906.86)};
      \addplot[style={rred,fill=rred,mark=none}]
        coordinates {(16b, 743.54) (1kb, 33422.57) (8kb, 311597.62)};
      \addplot[style={ggreen,fill=ggreen,mark=none}]
        coordinates {(16b, 135369.41) (1kb, 218534.05) (8kb, 274946.58)};
    \legend{Cryptodev (Marvell CESA),AF\_ALG (Marvell CESA), No Hardware Engine}
    \end{axis}
  \end{tikzpicture}
\end{frame}

\begin{frame}{\code{cryptodev} vs \code{AF_ALG}: Conclusion}
  \begin{itemize}
  \item {\bf Which one should I use???}
  \item \code{cryptodev} shows better results, but...
  \item ... both \code{cryptodev} and \code{AF_ALG} are outperformed
	by the software userspace solution
  \item And we don't gain that much in term of CPU load when offloading
        to the hardware engine (60\% vs 100\%)
  \item Of course those results are likely to be dependent on the
        crypto engine and its driver
  \item In any case
    \begin{itemize}
    \item Think twice before using a hardware crypto engine from userspace
    \item Do the test before taking a decision
    \end{itemize}
  \end{itemize}
\end{frame}

\subsection{Linux Crypto Framework: How to develop a crypto engine driver}

\begin{frame}{How to develop a crypto engine driver}
  \begin{itemize}
  \item The crypto framework does not distinguish hardware engines from
	software implementation
  \item Developing a crypto engine driver is just about registering a
        \code{crypto_alg} to the crypto subsystem
  \item Identify the type of algorithm you want to add support for and
        the associated \code{crypto_alg} interface
        (\code{skcipher_alg}, \code{ahash_alg}, \code{...})
  \item Implement the \code{xxx_alg} interface and call
        \code{crypto_register_xxx()} to register it to the crypto
        framework
  \item We will study a simple algorithm type: \code{cbc(aes)}
  \item Go look at other drivers or the crypto framework doc if you need
	information on other alg type interfaces
  \end{itemize}
\end{frame}

\begin{frame}[fragile]{Important \code{crypto_alg} Fields}
  \begin{block}{}
    \fontsize{6}{6}\selectfont
    \begin{minted}{c}
struct skcipher_alg xxx_cbc_aes_alg = {
	...
	.base = {
		/* Name used by the framework to find who is implementing what. */
		.cra_name = "cbc(aes)",

		/* Driver name. Can be used to request a specific implementation of an algorithm. */
		.cra_driver_name = "xxx-cbc-aes",

		/* Priority is used when implementation auto-selection takes place:
		 * if there are several implementers, the one with the highest priority is chosen.
		 * By convention: HW engine > ASM/arch-optimized > plain C
		 */
		.cra_priority = 300,

		/* CRYPTO_ALG_TYPE_XX: describes the type algorithm implemented here
		 * CRYPTO_ALG_ASYNC: the engine is operating in an asynchronous manner
		 * CRYPTO_ALG_KERN_DRIVER_ONLY: engine is not directly accessible to userspace
		 */
		.cra_flags = CRYPTO_ALG_TYPE_SKCIPHER | CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC,

		/* Size of the data blocks this algo operates on. */
		.cra_blocksize = AES_BLOCK_SIZE,

		/* Size of the context attached to an algorithm instance. */
		.cra_ctxsize = sizeof(struct xxx_aes_ctx),

		/* constructor/destructor methods called every time an alg instance is created/destroyed. */
		.cra_init = xxx_skcipher_cra_init,
		.cra_exit = xxx_skcipher_cra_exit,
	},
};
    \end{minted}
  \end{block}
\end{frame}

\begin{frame}[fragile]{\code{skcipher_alg} Fields}
  \begin{columns}
    \column{0.4\textwidth}
    \begin{block}{}
      \fontsize{6}{6}\selectfont
      \begin{minted}{c}
struct skcipher_alg mv_cesa_cbc_aes_alg = {
	/* Set key implementation. */
	.setkey = xxx_aes_setkey,

	/* Encrypt/decrypt implementation. */
	.encrypt = xxx_cbc_aes_encrypt,
	.decrypt = xxx_cbc_aes_decrypt,

	/* Symmetric key size. */
	.min_keysize = AES_MIN_KEY_SIZE,
	.max_keysize = AES_MAX_KEY_SIZE,
	/* IV size */
	.ivsize = AES_BLOCK_SIZE,
	.base = {
	....
	},
};
      \end{minted}
    \end{block}
    \column{0.6\textwidth}
    \begin{block}{}
      \fontsize{6}{6}\selectfont
      \begin{minted}{c}
static int xxx_encrypt(struct skcipher_request *req)
{
	struct my_ctx *ctx = crypto_tfm_ctx(req->base.tfm);

	/* Prepare and queue the request here. Return 0 if the request has
	 * been executed, -EINPROGRESS if it's been queued, -EBUSY if it's
	 * been backlogged or a different error code for other kind of
	 * errors.
	 */

	return ret;
}

static int xxx_decrypt(struct skcipher_request *req)
{
	struct my_ctx *ctx = crypto_tfm_ctx(req->base.tfm);

	/* Similar to xxx_encrypt() except this time we prepare and queue
	 * a decrypt operation.
	 */

	return ret;
}

static int xxx_setkey(struct crypto_skcipher *cipher, const u8 *key,
		      unsigned int len)
{
	struct my_ctx *ctx = crypto_tfm_ctx(req->base.tfm);

	/* Expand key and assing store the result in the ctx. */
	return ret;
}
      \end{minted}
    \end{block}
  \end{columns}
\end{frame}

\subsection{Feedback on my experience with the crypto framework}

\begin{frame}{The framework is a complex beast}
  \begin{itemize}
  \item Good aspects:
    \begin{itemize}
    \item Can easily be extended to support new algorithms (even non
          crypto related ones)
    \item It comes with an extensive testsuite to detect bad
          implementation or regressions
    \end{itemize}
  \item Bad aspects:
    \begin{itemize}
    \item The crypto framework is so open that you sometime have
          several ways to expose the same thing (example:
          \code{crypto_alg+ablkcipher} or \code{skcipher})
    \item The object model is not consistent (how to inherit from a base
          interface is not enforced even in the core)
    \item Hard to tell what the good practices are (old/existing
          drivers are usually not converted to the new way of doing
          things)
    \item Important details can be discovered the hard way (example:
          completion callback should be called with \code{softirqs}
          disabled)
    \end{itemize}
  \item Who am I to complain about these problems, the NAND framework
        is probably worse in this regard...
  \item Getting these things addressed requires a non-negligible effort
        and some help from drivers contributors
  \end{itemize}
\end{frame}

\begin{frame}{Limiting the number of interrupts: NAPI for crypto?}
  \begin{itemize}
  \item Crypto engines may generate a lot of interrupts (potentially
	one per crypto request, maybe more if the engine has a limited
        FIFO size)
  \item Crypto can be used by the net stack which may decide to switch
        in polling mode under heavy net load
  \item The lack of NAPI-awareness at the crypto level defeats NAPI
        mode: you'll end up with a bunch of crypto interrupts which
        will prevent your system from running smoothly while under
        heavy net+crypto load
  \item CESA driver approach to limit the number of interrupts: try to
        queue requests at the DMA level and use a threaded IRQ in
        \code{IRQF_ONESHOT} mode to do some polling instead of
        re-activating interrupts right away
  \item Not a perfect solution: bigger latency than the \code{irq+softirq}
        approach
  \item {\bf Question}: should we add a NAPI-like interface?
  \end{itemize}
\end{frame}

\begin{frame}{Load balancing: random thoughts}
  \begin{itemize}
  \item Current priority-based algorithm selection shows its limits:
    \begin{itemize}
    \item Some systems come with several instances of the same engine
          but only one of them can be used/exposed, unless the driver
          implements its own load balancing logic
    \item We can't distribute the load over heterogeneous engines in
          the system: the engine with the highest priority gets all
          the requests
    \end{itemize}
  \item Should we introduce a generic load-balancing mechanism?
  \item Here is a list of things to address if we do:
    \begin{itemize}
    \item Introduce the concept of crypto engine, because some engines
	  expose several crypto algs, but can't process things in
	  parallel
    \item Calculate a per-request load based on the request type, the
          request length and the engine it is queued to (or something
          simpler \code{load = length}?)
    \item Keep track of the total load of each engine registered to the
	  system in order to decide where the next request will go
    \item Algorithm contexts/states are driver dependent: we need an
          intermediate/driver-agnostic representation to allow moving
          the context/state from one engine to another
    \end{itemize}
  \end{itemize}
\end{frame}

\begin{frame}{Load balancing: random thoughts}
  \begin{center}
  \includegraphics[scale=0.22]{load-balancing-ideas.pdf}
  \end{center}
\end{frame}

\begin{frame}
  \begin{center}
  \Huge
  Questions? Suggestions? Comments?\\
  \vspace{1.5cm}
  \huge
  Boris Brezillon\\
  \large
  \vspace{0.5cm}
  \code{boris.brezillon@bootlin.com}
  \vspace{0.5cm}
  \newline Slides under CC-BY-SA 3.0\\
  \scriptsize
  \url{http://bootlin.com/pub/conferences/2017/elce/brezillon-crypto-framework/}
  \end{center}
\end{frame}

\end{document}
