From 48dad61069435113fc307421110d1d53290a8136 Mon Sep 17 00:00:00 2001 From: --get-all Date: Tue, 27 Sep 2022 00:53:44 +0000 Subject: [PATCH 01/15] Added lab on new module system --- labs/NewModuleSystem/NewModuleSystem.md | 615 +++++++++++++ .../NewModuleSystem/NewModuleSystemAnswers.md | 814 ++++++++++++++++++ labs/SimonSpeck/SimonSpeck.md | 4 +- 3 files changed, 1430 insertions(+), 3 deletions(-) create mode 100644 labs/NewModuleSystem/NewModuleSystem.md create mode 100644 labs/NewModuleSystem/NewModuleSystemAnswers.md diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md new file mode 100644 index 00000000..3df6f890 --- /dev/null +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -0,0 +1,615 @@ +# Introduction + +## Prerequisites + +Before working through this lab, you'll need + * Cryptol to be installed, + * this module to load successfully, and + * an editor for completing the exercises in associated files. + +You'll also need experience with + * loading modules and evaluating functions in the interpreter, + * parameterized modules + * ... + +## Skills You'll Learn + +By the end of this lab you will have gained experience with Cryptol's +new module system, using it to implement a complex module with +multiple submodules and interfaces. + +## Load This Module + +This lab is a +[literate](https://en.wikipedia.org/wiki/Literate_programming) Cryptol +document --- that is, it can be loaded directly into the Cryptol +interpreter. Load this module from within the Cryptol interpreter +running in the `cryptol-course` directory with: + +```Xcryptol-session +Loading module Cryptol +Cryptol> :m labs::NewModuleSystem::NewModuleSystem +Loading module Cryptol +Loading module labs::NewModuleSystem::NewModuleSystem +``` + +We start by defining a new module for this lab: + +```cryptol +module labs::NewModuleSystem::NewModuleSystem where +``` + +You do not need to enter the above into the interpreter; the previous +`:m ...` command loaded this literate Cryptol file automatically. +In general, you should run `Xcryptol-session` commands in the +interpreter and leave `cryptol` code alone to be parsed by `:m ...`. + +# Background + +Before proceeding, recall a prior lab describing +[parameterized modules](../SimonSpeck/SimonSpeck.md) +for the SIMON and SPECK algorithms. That lab required us to split +each parameter setting into its own module: + +> But because this lab introduces further concepts around the +> definition and use of modules, this lab will not be entirely +> self-contained in this file. + +Another more subtle limitation is that it is tricky to specify +multiple layers of parameterized modules, so specifications often +resort to repeating parameters. For SIMON, block size is repeated +for each corresponding key size and number of rounds, but this is +minor because block size is a simple integer. But for more complex +specifications like +[SHA-2](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Keyless/Hash/SHA2), +complex parameters like `K` (a sequence of words of bitlength 32 or +64) are repeated for different digest lengths at each word size, +potentially introducing easy-to-miss inconsistencies. + +# New Module System + +These limitations motivated customers to ask Galois to +[update the module system](https://github.com/GaloisInc/cryptol/issues/815) +to support layered specifications, cipher modes, common interfaces, +and other such complexities. The long-awaited update will soon be (or +have been) [merged](https://github.com/GaloisInc/cryptol/pull/1363) +and +[documented](https://github.com/GaloisInc/cryptol/blob/master/docs/RefMan/Modules.rst). + +## `submodule`: Nested Modules + +The new module system supports `submodule`s that can be nested into +other (`sub`)`module`s: + +```cryptol + submodule Hash where + submodule XXHash where + TODO = undefined + + submodule SHA where + TODO = undefined + + submodule SHA2 where + TODO = undefined +``` + +This removes a previous limitation -- multiple parameterizations no +longer have to be specified in different modules. + +## Interfaces, Parameterization, Abstraction, and Instantiation + +In the new module system, an _interface_ specifies _parameters_ that an +_abstract_ (sub)module can _instantiate_ to produce an _implementation_. +Let's see this in a simple contrived example: + +```cryptol + submodule Contrivance where + interface submodule I_n where + type n : # + type constraint (2 >= n, n >= 1) + + submodule A_n where + import interface submodule I_n + + type w = 32*n + type W = [w] + + z : W + z = zero + + submodule P_1 where + type n = 1 + + submodule P_2 where + type n = 2 + + submodule Z_1 = submodule A_n { submodule P_1 } + submodule Z_2 = submodule A_n { submodule P_2 } + + import submodule Z_1 as Z32 + import submodule Z_2 as Z64 + + property zero_32 = Z32::z == zero`{[32]} + property zero_64 = Z64::z == zero`{[64]} +``` + +The above snippet introduces a `submodule` `Contrivance`. Within, it +defines an `interface` with a numeric parameter `n` bound to the +interval `[1,2]`. An abstract submodule `A_n` requests a +parameterization satisfying `I_n`, and produces an implementation +defining `z`, a `zero` value of `type W = [w]`, where `type w = 32*n`. +Parameterizations `P_1` and `P_2` specify `n` values of `1` and `2`, +respectively, each satisfying `I_n`. Parameterizations `P_1` and `P_2` +instantiate `A_n` to define `Z_1` and `Z_2`, respectively, with `z` as +a corresponding `32`- or `64`-bit `zero`, as expressed by properties +`zero_32` and `zero_64`, respectively; each property requires the +instantiated submodules to be imported with aliases (here, `Z32` and +`Z64`) to distinguish their respective `z` definitions: + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :prove Contrivance::zero_32 +:prove Contrivance::zero_32 + Q.E.D. +(Total Elapsed Time: 0.035s, using "Z3") +labs::NewModuleSystem::NewModuleSystem> :prove Contrivance::zero_64 +:prove Contrivance::zero_64 + Q.E.D. +(Total Elapsed Time: 0.031s, using "Z3") +``` + +All of the above were defined within the `Contrivance` submodule, +hence the leading `Contrivance::` in the property names. + +## Scope + +Only the top-level definitions `zero_32` and `zero_64` are in scope +after loading the top-level module from this point. Nested submodules +are not exported; to make `z` from each submodule available as well, +they must be reassigned explicitly: + +```cryptol + z32 = Z32::z + z64 = Z64::z +``` + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :h Contrivance + +Module Contrivance exports: + + + zero_32 : Bit + zero_64 : Bit + z32 : W + z64 : W +labs::NewModuleSystem::NewModuleSystem> Contrivance::z32 +0x00000000 +labs::NewModuleSystem::NewModuleSystem> Contrivance::z64 +0x0000000000000000 +labs::NewModuleSystem::NewModuleSystem> Contrivance::zero_32 +True +labs::NewModuleSystem::NewModuleSystem> Contrivance::zero_64 +True +``` + +# Practicum + +Having introduced the new module system, let's apply it to SIMON and +compare the former and new module systems. + +## Simon says what? + +Recall that SIMON is parameterized on four constrained numeric (`#`) +`type`s: + * `n` (word size) + * `m` (words per key) + * `T` (rounds in key schedule) + * `j` (index of z-sequence) + +**EXERCISE**: Define an `interface submodule` comprising these types +and their constraints. +(**Hint**: Just copy them from the previous lab.) + +```cryptol + submodule SIMON where + /** interface specifying parameters for SIMON block cipher */ + interface submodule I_SIMON where + /** word size */ + type n : # + // TODO: Add constraint + + /** number of words in key */ + type m : # + // TODO: Add constraint + + /** number of rounds in key schedule */ + type T : # + // TODO: Add constraint + + /** index of z-sequence used (i.e. use sequence z_j) */ + type j : # + // TODO: Add constraint +``` + +Recall that, given these parameters, SIMON exports: + * derived `type`s: + * `type blockSize = 2 * n` + * `type keySize = m * n` + * block cipher functions: + * `encrypt : [keySize] -> [blockSize] -> [blockSize]` + * `decrypt : [keySize] -> [blockSize] -> [blockSize]` + * properties: + * `EncryptDecryptIdentity : [keySize] -> [blockSize] -> Bool` + * `DecryptEncryptIdentity : [keySize] -> [blockSize] -> Bool` + +These exported definitions rely on numerous `private` ones. + +Eventually, we will need to (re)implement `encrypt` and `decrypt` +functions in an abstract submodule that imports `I_SIMON`. Testing the +implementations will require parameterizations satisfying `I_n` to +instantiate the abstract submodule. So let's start there: + +**EXERCISE**: Define submodules with parameters satisfying `I_n` for +each approved `n` (word size) and `m` (words per key), or just a few +if you're so inclined. (`T` and `j` depend on `n` and `m`.) +(**Hint**: For each `simon_2n_mn.cry`, copy its `parameter` block body +into a `submodule P_SIMON_2n_mn` (or `P_SIMON_n_m` if you prefer). + +```cryptol + submodule P_SIMON_32_64 where + type n = 0 + type m = 0 + type T = 0 + type j = 0 + + // TODO: Add remaining SIMON parameterizations +``` + +**EXERCISE**: Define an abstract `submodule` `A_SIMON` that requests a +parameterization satisfying `I_SIMON` and exports an implementation +with the above definitions, and instantiate this with each of the +above parameterizations into concrete submodules. `:check` your work. +(**Hint**: Just copy the previous definitions from `Simon.cry` from +the previous lab, and instatiate them using lines of the form +`submodule SIMON_2n_mn = submodule A_SIMON { submodule P_SIMON_2n_mn }` (or +likewise if you used another naming scheme earlier). + +```cryptol + /** + * abstraction that produces a SIMON block cipher implementation + * from parameters satisfying the `I_SIMON` interface + */ + submodule A_SIMON where + import interface submodule I_SIMON + TODO = undefined // Replace with abstract SIMON implementation + + submodule SIMON_32_64 = submodule A_SIMON { submodule P_SIMON_32_64 } + // TODO: Add remaining instantiations +``` + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :check +property Contrivance::zero_32 Using exhaustive testing. +Passed 1 tests. +Q.E.D. +property Contrivance::zero_64 Using exhaustive testing. +Passed 1 tests. +Q.E.D. +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^48 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^48 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^120 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^160 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^240 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^256 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^320 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^384 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^120 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^160 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^240 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^256 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^320 values) +property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^384 values) +``` + +## Simon asks why? + +Interface, parameterizations, abstractions, and instantiations -- all +to reimplement what parameterized modules are already able to do with +`parameter` blocks and parameterized `import`s. Sure, the new system +allows us to define the whole family of SIMON ciphers, alongside our +earlier contrivance no less, but is this worth all the trouble? If the +answer is immediately obvious at this point, feel free to skip the +rest of this lab, secure in your understanding of nested modules, +interfaces, and the opportunities and conundrums they bring. +Otherwise, let's show off a concept that was difficult to pull off +with the old system: cipher modes. + +Recall that our SIMON spec defined `encrypt` and `decrypt` functions +over `key` and `block` sizes. Furthermore, it defined a property that +`decrypt` inverts `encrypt`. These are all rather common, and having +to define the inversion property for AES, Blowfish, RC5, and any other +[block cipher](https://en.wikipedia.org/wiki/Block_cipher) +that comes along gets very tedious very fast. + +Now suppose we wish to run each of these ciphers in various +[modes](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation): +[Electronic Codebook (ECB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB) +(only to demonstrate why that's a really bad idea), +[Cipher Block Chaining (CBC)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC), +[Cipher Feedback (CFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB), +... If we had to define each mode, for each block cipher, again, this +would make Cryptol authors wish gone into yodeling or sports +broadcasting. So until this point, except in a few cases where +[an effort was made](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Symmetric/Cipher/Block/Modes), +this has mostly been avoided. Nested modules, for all their verbosity, +offer another approach: + * Define a block cipher interface + * Define abstract cipher modes that request block cipher parameters + * Instantiate cipher modes with parameterized block ciphers + +# Block Ciphers + +Working from +[NIST SP 800-38A](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), +a _block cipher_ is a function that maps `b`-bit plaintext blocks +to `b`-bit ciphertext blocks, parameterized by a key `K` of arbitrary +type. NIST SP 800-38A refers to block encryption and decryption as +forward and inverse ciphers to disambiguate them from corresponding +mode operations. Let's rename these to `encipher` and `decipher`, +respectively, in keeping with Cryptol naming conventions. + +```cryptol + import labs::Transposition::CommonProperties (inverts) + + interface submodule I_BlockCipher where + type k : * + type b : # + type constraint (fin b) + + encipher : k -> [b] -> [b] + decipher : k -> [b] -> [b] + + submodule A_BlockCipher_Props where + import interface submodule I_BlockCipher + + decipher_inverts_encipher : k -> [b] -> Bool + property decipher_inverts_encipher K = + inverts (decipher K) (encipher K) +``` + +Common block cipher _modes of operation_ defined in NIST 800-38A +include: + * Electronic Codebook (ECB) + * Cipher Block Chaining (CBC) + * Cipher Feedback (CFB) + * Output Feedback (OFB) + +Let's start with the easy ones. + +```cryptol + submodule A_ECB where + import interface submodule I_BlockCipher + + encrypt : {t} k -> [t][b] -> [t][b] + encrypt K P = map (encipher K) P + + decrypt : {t} k -> [t][b] -> [t][b] + decrypt K C = map (decipher K) C + + decrypt_inverts_encrypt : {t} (fin t) => k -> [t][b] -> Bool + decrypt_inverts_encrypt K = + inverts (decrypt K) (encrypt K) + + submodule A_CBC where + import interface submodule I_BlockCipher + + encrypt : {t} k -> [b] -> [t][b] -> [t][b] + encrypt K IV P = C + where + C = [ encipher K (P_j ^ C_j') | P_j <- P | C_j' <- [IV] # C ] + + decrypt : {t} k -> [b] -> [t][b] -> [t][b] + decrypt K IV C = P + where + P = [ (decipher K C_j) ^ C_j' | C_j <- C | C_j' <- [IV] # C ] + + decrypt_inverts_encrypt : {t} (fin t) => k -> [b] -> [t][b] -> Bool + decrypt_inverts_encrypt K IV = + inverts (decrypt K IV) (encrypt K IV) +``` + +Unfortunately, because different cipher modes might take different +parameters (e.g. CBC requires an _initialization vector_ `iv`), we +cannot easily define an abstract (sub)module for cipher modes. + +**EXERCISE**: Define abstract submodules for CFB and OFB modes. + +```cryptol + submodule A_CFB where + TODO = undefined + + submodule A_OFB where + TODO = undefined +``` + +As tempting as it is to ask students to go reimplement AES, DES, etc., +let's just adapt an existing cipher to the `I_BlockCipher` interface +expected by our abstract cipher mode submodules. (This part requires +[`cryptol-specs`](https://github.com/GaloisInc/cryptol-specs) to be +cloned or downloaded and in `CRYPTOLPATH`.) + +```cryptol + import module Primitive::Symmetric::Cipher::Block::DES (DES) + + submodule P_DES where + type k = [64] + type b = 64 + + encipher = DES.encrypt + decipher = DES.decrypt + + submodule S_ECB_DES = submodule A_ECB { submodule P_DES } + import submodule S_ECB_DES as S_ECB_DES + + submodule S_CBC_DES = submodule A_CBC { submodule P_DES } + import submodule S_CBC_DES as S_CBC_DES +``` + +Why is ECB bad again? Let's check... + +```cryptol + ECB_DES_bad : {n} (fin n) => P_DES::k -> [P_DES::b] -> Bool + ECB_DES_bad K block = + S_ECB_DES::encrypt K (repeat`{n} block) == repeat (P_DES::encipher K block) + + CBC_DES_less_bad : {n} (fin n, n >= 2) => P_DES::k -> [P_DES::b] -> [P_DES::b] -> Bool + CBC_DES_less_bad K IV block = + S_CBC_DES::encrypt K IV (repeat`{n} block) != repeat (P_DES::encipher K block) +``` + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :prove ECB_DES_bad`{4} +Q.E.D. +(Total Elapsed Time: 1.893s, using "Yices") +labs::NewModuleSystem::NewModuleSystem> :check CBC_DES_less_bad`{4} +Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +``` + +So ECB is bad unless you never repeat yourself or say anything an +attacker would consider... + +**EXERCISE**: Feel free to experiment with other block ciphers +(including SIMON) and modes. Show that ECB is still bad with those. + +# Conclusion + +In this module, we learned how to apply the new module system to a +previously covered specification (SIMON) and to block cipher modes of +operation by defining common interfaces for abstractions to +instantiate implementations given parameters. The new module system +allowed us to combine all of these and a contrivance into a single +module for expository purposes. In the real world, it will eventually +form a solid foundation to organize a wide variety of cryptographic +algorithms and systems that combine them. + +# Solicitation + +How was your experience with this lab? Suggestions are welcome in the +form of a ticket on the course GitHub page: +https://github.com/weaversa/cryptol-course/issues + +# From here, you can go somewhere! + +|||| +|-:|:-:|-| +|| [ ^ Key Wrapping](../KeyWrapping/KeyWrapping.md) || +| [< Parameterized Modules](../SimonSpeck/SimonSpeck.md) | **New Module System** || +|| [! New Module System (Answers)](./NewModuleSystemAnswers.md) || diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md new file mode 100644 index 00000000..5e59a640 --- /dev/null +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -0,0 +1,814 @@ +# Introduction + +## Prerequisites + +Before working through this lab, you'll need + * Cryptol to be installed, + * this module to load successfully, and + * an editor for completing the exercises in associated files. + +You'll also need experience with + * loading modules and evaluating functions in the interpreter, + * parameterized modules + * ... + +## Skills You'll Learn + +By the end of this lab you will have gained experience with Cryptol's +new module system, using it to implement a complex module with +multiple submodules and interfaces. + +## Load This Module + +This lab is a +[literate](https://en.wikipedia.org/wiki/Literate_programming) Cryptol +document --- that is, it can be loaded directly into the Cryptol +interpreter. Load this module from within the Cryptol interpreter +running in the `cryptol-course` directory with: + +```Xcryptol-session +Loading module Cryptol +Cryptol> :m labs::NewModuleSystem::NewModuleSystemAnswers +Loading module Cryptol +Loading module labs::NewModuleSystem::NewModuleSystemAnswers +``` + +We start by defining a new module for this lab: + +```cryptol +module labs::NewModuleSystem::NewModuleSystemAnswers where +``` + +You do not need to enter the above into the interpreter; the previous +`:m ...` command loaded this literate Cryptol file automatically. +In general, you should run `Xcryptol-session` commands in the +interpreter and leave `cryptol` code alone to be parsed by `:m ...`. + +# Background + +Before proceeding, recall a prior lab describing +[parameterized modules](../SimonSpeck/SimonSpeck.md) +for the SIMON and Speck algorithms. That lab required us to split +each parameter setting into its own module: + +> But because this lab introduces further concepts around the +> definition and use of modules, this lab will not be entirely +> self-contained in this file. + +Another more subtle limitation is that it is tricky to specify +multiple layers of parameterized modules, so specifications often +resort to repeating parameters. For SIMON, block size is repeated +for each corresponding key size and number of rounds, but this is +minor because block size is a simple integer. But for more complex +specifications like +[SHA-2](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Keyless/Hash/SHA2), +complex parameters like `K` (a sequence of words of bitlength 32 or +64) are repeated for different digest lengths at each word size, +potentially introducing easy-to-miss inconsistencies. + +# New Module System + +These limitations motivated customers to ask Galois to +[update the module system](https://github.com/GaloisInc/cryptol/issues/815) +to support layered specifications, cipher modes, common interfaces, +and other such complexities. The long-awaited update will soon be (or +have been) [merged](https://github.com/GaloisInc/cryptol/pull/1363) +and +[documented](https://github.com/GaloisInc/cryptol/blob/master/docs/RefMan/Modules.rst). + +## `submodule`: Nested Modules + +The new module system supports `submodule`s that can be nested into +other (`sub`)`module`s: + +```cryptol + submodule Hash where + submodule XXHash where + TODO = undefined + + submodule SHA where + TODO = undefined + + submodule SHA2 where + TODO = undefined +``` + +This removes a previous limitation -- multiple parameterizations no +longer have to be specified in different modules. + +## Interfaces, Parameterization, Abstraction, and Instantiation + +In the new module system, an _interface_ specifies _parameters_ that an +_abstract_ (sub)module can _instantiate_ to produce an _implementation_. +Let's see this in a simple contrived example: + +```cryptol + submodule Contrivance where + interface submodule I_n where + type n : # + type constraint (2 >= n, n >= 1) + + submodule A_n where + import interface submodule I_n + + type w = 32*n + type W = [w] + + z : W + z = zero + + submodule P_1 where + type n = 1 + + submodule P_2 where + type n = 2 + + submodule Z_1 = submodule A_n { submodule P_1 } + submodule Z_2 = submodule A_n { submodule P_2 } + + import submodule Z_1 as Z32 + import submodule Z_2 as Z64 + + property zero_32 = Z32::z == zero`{[32]} + property zero_64 = Z64::z == zero`{[64]} +``` + +The above snippet introduces a `submodule` `Contrivance`. Within, it +defines an `interface` with a numeric parameter `n` bound to the +interval `[1,2]`. An abstract submodule `A_n` requests a +parameterization satisfying `I_n`, and produces an implementation +defining `z`, a `zero` value of `type W = [w]`, where `type w = 32*n`. +Parameterizations `P_1` and `P_2` specify `n` values of `1` and `2`, +respectively, each satisfying `I_n`. Parameterizations `P_1` and `P_2` +instantiate `A_n` to define `Z_1` and `Z_2`, respectively, with `z` as +a corresponding `32`- or `64`-bit `zero`, as expressed by properties +`zero_32` and `zero_64`, respectively; each property requires the +instantiated submodules to be imported with aliases (here, `Z32` and +`Z64`) to distinguish their respective `z` definitions: + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :prove Contrivance::zero_32 +:prove Contrivance::zero_32 + Q.E.D. +(Total Elapsed Time: 0.035s, using "Z3") +labs::NewModuleSystem::NewModuleSystemAnswers> :prove Contrivance::zero_64 +:prove Contrivance::zero_64 + Q.E.D. +(Total Elapsed Time: 0.031s, using "Z3") +``` + +All of the above were defined within the `Contrivance` submodule, +hence the leading `Contrivance::` in the property names. + +## Scope + +Only the top-level definitions `zero_32` and `zero_64` are in scope +after loading the top-level module from this point. Nested submodules +are not exported; to make `z` from each submodule available as well, +they must be reassigned explicitly: + +```cryptol + z32 = Z32::z + z64 = Z64::z +``` + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :h Contrivance + +Module Contrivance exports: + + + zero_32 : Bit + zero_64 : Bit + z32 : W + z64 : W +labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::z32 +0x00000000 +labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::z64 +0x0000000000000000 +labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::zero_32 +True +labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::zero_64 +True +``` + +# Practicum + +Having introduced the new module system, let's apply it to SIMON and +compare the former and new module systems. + +## Simon says what? + +Recall that SIMON is parameterized on four constrained numeric (`#`) +`type`s: + * `n` (word size) + * `m` (words per key) + * `T` (rounds in key schedule) + * `j` (index of z-sequence) + +**EXERCISE**: Define an `interface submodule` comprising these types +and their constraints. +(**Hint**: Just copy them from the previous lab.) + +```cryptol + submodule SIMON where + /** interface specifying parameters for SIMON block cipher */ + interface submodule I_SIMON where + /** word size */ + type n : # + type constraint (16 <= n, n <= 64) + + /** number of words in key */ + type m : # + type constraint (2 <= m, m <= 4) + + /** number of rounds in key schedule */ + type T : # + type constraint (32 <= T, T <= 72) + + /** index of z-sequence used (i.e. use sequence z_j) */ + type j : # + type constraint (0 <= j, j <= 4) +``` + +Recall that, given these parameters, SIMON exports: + * derived `type`s: + * `type blockSize = 2 * n` + * `type keySize = m * n` + * block cipher functions: + * `encrypt : [keySize] -> [blockSize] -> [blockSize]` + * `decrypt : [keySize] -> [blockSize] -> [blockSize]` + * properties: + * `EncryptDecryptIdentity : [keySize] -> [blockSize] -> Bool` + * `DecryptEncryptIdentity : [keySize] -> [blockSize] -> Bool` + +These exported definitions rely on numerous `private` ones. + +Eventually, we will need to (re)implement `encrypt` and `decrypt` +functions in an abstract submodule that imports `I_SIMON`. Testing the +implementations will require parameterizations satisfying `I_n` to +instantiate the abstract submodule. So let's start there: + +**EXERCISE**: Define submodules with parameters satisfying `I_n` for +each approved `n` (word size) and `m` (words per key), or just a few +if you're so inclined. (`T` and `j` depend on `n` and `m`.) +(**Hint**: For each `simon_2n_mn.cry`, copy its `parameter` block body +into a `submodule P_SIMON_2n_mn` (or `P_SIMON_n_m` if you prefer). + +```cryptol + submodule P_SIMON_32_64 where + type n = 16 + type m = 4 + type T = 32 + type j = 0 + + submodule P_SIMON_48_72 where + type n = 24 + type m = 3 + type T = 36 + type j = 0 + + submodule P_SIMON_48_96 where + type n = 24 + type m = 4 + type T = 36 + type j = 1 + + submodule P_SIMON_64_96 where + type n = 32 + type m = 3 + type T = 42 + type j = 2 + + submodule P_SIMON_64_128 where + type n = 32 + type m = 4 + type T = 44 + type j = 3 + + submodule P_SIMON_96_96 where + type n = 48 + type m = 2 + type T = 52 + type j = 2 + + submodule P_SIMON_96_144 where + type n = 48 + type m = 3 + type T = 54 + type j = 3 + + submodule P_SIMON_128_128 where + type n = 64 + type m = 2 + type T = 68 + type j = 2 + + submodule P_SIMON_128_192 where + type n = 64 + type m = 3 + type T = 69 + type j = 3 + + submodule P_SIMON_128_256 where + type n = 64 + type m = 4 + type T = 72 + type j = 4 +``` + +**EXERCISE**: Define an abstract `submodule` `A_SIMON` that requests a +parameterization satisfying `I_SIMON` and exports an implementation +with the above definitions, and instantiate this with each of the +above parameterizations into concrete submodules. `:check` your work. +(**Hint**: Just copy the previous definitions from `Simon.cry` from +the previous lab, and instatiate them using lines of the form +`submodule SIMON_2n_mn = submodule A_SIMON { submodule P_SIMON_2n_mn }` (or +likewise if you used another naming scheme earlier). + +```cryptol + /** + * abstraction that produces a SIMON block cipher implementation + * from parameters satisfying the `I_SIMON` interface + */ + submodule A_SIMON where + import interface submodule I_SIMON + + type blockSize = 2 * n + type keySize = m * n + + /** + * The SIMON block cipher, encryption direction. See [1] for further + * details. + * + * [1] Beaulieu R., Shors D., et. al. "The Simon and Speck Families + * of Lightweight Block Ciphers". e-print-2013-404.pdf. + */ + encrypt : [keySize] -> [blockSize] -> [blockSize] + encrypt K P = join (last (encryptList K P)) + + /** + * The SIMON block cipher, decryption direction. See [1] for further + * details. + * + * [1] Beaulieu R., Shors D., et. al. "The Simon and Speck Families + * of Lightweight Block Ciphers". e-print-2013-404.pdf. + */ + decrypt : [keySize] -> [blockSize] -> [blockSize] + decrypt K P = join (last (decryptList K P)) + + /** + * EncryptDecryptRoundIdentity k x = Rk' k (Rk k x) == x + */ + property EncryptDecryptRoundIdentity k x = + Rk' k (Rk k x) == x + + /** + * DecryptEncryptRoundIdentity k x = Rk k (Rk' k x) == x + */ + property DecryptEncryptRoundIdentity k x = + Rk k (Rk' k x) == x + + // These properties take too long to verify with Z3 + // Try proving with abc + // :s prover=abc or + // :s prover=any + property EncryptDecryptIdentity k x = + decrypt k (encrypt k x) == x + + property DecryptEncryptIdentity k x = + encrypt k (decrypt k x) == x + + private + + // We define the sequences u, v, w, and z_j following the paper + u = join(repeat`{inf} 0b1111101000100101011000011100110) + v = join(repeat`{inf} 0b1000111011111001001100001011010) + w = join(repeat`{inf} 0b1000010010110011111000110111010) + + z0 = u + z1 = v + z2 = join([ dibit ^ 0b01 | dibit <- groupBy`{2} u ]) + z3 = join([ dibit ^ 0b01 | dibit <- groupBy`{2} v ]) + z4 = join([ dibit ^ 0b01 | dibit <- groupBy`{2} w ]) + + // Z will be the sequence selected by the module parameter j + Z = [z0, z1, z2, z3, z4]@(`j:[width j]) + + // Round Function and auxiliary functions + + /** + * f is used in the round function and appears on page 8 of [1] + */ + f x = (x <<< 1) && (x <<< 8) ^ (x <<< 2) + + /** + * The SIMON round function + */ + Rk : [n] -> [2][n] -> [2][n] + Rk k [x, y] = [x', y'] + where + x' = y ^ (f x) ^ k + y' = x + + /** + * The SIMON inverse round function + */ + Rk' : [n] -> [2][n] -> [2][n] + Rk' k [x, y] = [x', y'] + where + x' = y + y' = x ^ (f y) ^ k + + /** + * "tmp" appears as the name of a variable in the SIMON key + * expansion sample implementation on page 13. The tmp function + * below captures the possible final values the expression has, + * taking into account the type parameter `m` containing the + * number of key words. + */ + tmp: [n] -> [n] -> [n] + tmp k1 k2 = r + where + r = t ^ (t >>> 1) + t = if `m == 0x4 then (t' ^ k2) else t' + t' = (k1 >>> 3) + + /** + * The SIMON Key Expansion function. + */ + KeyExpansion : [keySize] -> [T][n] + KeyExpansion K = take Ks + where + Kis : [m][n] + Kis = reverse (split K) + Ks : [inf][n] + Ks = Kis + # [ ~k0 ^ (tmp k1 k2) ^ (zext [z]) ^ (zext 0x3) + | k0 <- Ks + | k1 <- drop`{m-1} Ks + | k2 <- drop`{max m 3 - 3} Ks // gadget to typecheck "drop`{m-3}" + | z <- Z ] + + /** + * SIMON decryption with intermediate values for each round + * produced as a list. Some test vectors are available to compare + * these intermediate values. + */ + decryptList: [keySize] -> [blockSize] -> [T+1][2][n] + decryptList K C = Ps + where + Cs = split C + Ks = reverse (KeyExpansion K) + Ps = [ Cs ] # [ Rk' k xy | k <- Ks | xy <- Ps] + + /** + * SIMON encryption with intermediate values for each round + * produced as a list. Some test vectors are available to compare + * these intermediate values. + */ + encryptList: [keySize] -> [blockSize] -> [T+1][2][n] + encryptList K P = Cs + where + Ps = split P + Ks = KeyExpansion K + Cs = [ Ps ] # [ Rk k xy | k <- Ks | xy <- Cs] + + submodule SIMON_32_64 = submodule A_SIMON { submodule P_SIMON_32_64 } + submodule SIMON_48_72 = submodule A_SIMON { submodule P_SIMON_48_72 } + submodule SIMON_48_96 = submodule A_SIMON { submodule P_SIMON_48_96 } + submodule SIMON_64_96 = submodule A_SIMON { submodule P_SIMON_64_96 } + submodule SIMON_64_128 = submodule A_SIMON { submodule P_SIMON_64_128 } + submodule SIMON_96_96 = submodule A_SIMON { submodule P_SIMON_96_96 } + submodule SIMON_96_144 = submodule A_SIMON { submodule P_SIMON_96_144 } + submodule SIMON_128_128 = submodule A_SIMON { submodule P_SIMON_128_128 } + submodule SIMON_128_192 = submodule A_SIMON { submodule P_SIMON_128_192 } + submodule SIMON_128_256 = submodule A_SIMON { submodule P_SIMON_128_256 } +``` + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check +property Contrivance::zero_32 Using exhaustive testing. +Passed 1 tests. +Q.E.D. +property Contrivance::zero_64 Using exhaustive testing. +Passed 1 tests. +Q.E.D. +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^48 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::EncryptDecryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^48 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^72 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::DecryptEncryptRoundIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^120 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^160 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^240 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^256 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^320 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::EncryptDecryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^384 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^96 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^120 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^144 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^160 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^240 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^256 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^320 values) +property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::DecryptEncryptIdentity Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^384 values) +``` + +## Simon asks why? + +Interface, parameterizations, abstractions, and instantiations -- all +to reimplement what parameterized modules are already able to do with +`parameter` blocks and parameterized `import`s. Sure, the new system +allows us to define the whole family of SIMON ciphers, alongside our +earlier contrivance no less, but is this worth all the trouble? If the +answer is immediately obvious at this point, feel free to skip the +rest of this lab, secure in your understanding of nested modules, +interfaces, and the opportunities and conundrums they bring. +Otherwise, let's show off a concept that was difficult to pull off +with the old system: cipher modes. + +Recall that our SIMON spec defined `encrypt` and `decrypt` functions +over `key` and `block` sizes. Furthermore, it defined a property that +`decrypt` inverts `encrypt`. These are all rather common, and having +to define the inversion property for AES, Blowfish, RC5, and any other +[block cipher](https://en.wikipedia.org/wiki/Block_cipher) +that comes along gets very tedious very fast. + +Now suppose we wish to run each of these ciphers in various +[modes](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation): +[Electronic Codebook (ECB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB) +(only to demonstrate why that's a really bad idea), +[Cipher Block Chaining (CBC)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC), +[Cipher Feedback (CFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB), +... If we had to define each mode, for each block cipher, again, this +would make Cryptol authors wish gone into yodeling or sports +broadcasting. So until this point, except in a few cases where +[an effort was made](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Symmetric/Cipher/Block/Modes), +this has mostly been avoided. Nested modules, for all their verbosity, +offer another approach: + * Define a block cipher interface + * Define abstract cipher modes that request block cipher parameters + * Instantiate cipher modes with parameterized block ciphers + +# Block Ciphers + +Working from +[NIST SP 800-38A](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), +a _block cipher_ is a function that maps `b`-bit plaintext blocks +to `b`-bit ciphertext blocks, parameterized by a key `K` of arbitrary +type. NIST SP 800-38A refers to block encryption and decryption as +forward and inverse ciphers to disambiguate them from corresponding +mode operations. Let's rename these to `encipher` and `decipher`, +respectively, in keeping with Cryptol naming conventions. + +```cryptol + import labs::Transposition::CommonProperties (inverts) + + interface submodule I_BlockCipher where + type k : * + type b : # + type constraint (fin b) + + encipher : k -> [b] -> [b] + decipher : k -> [b] -> [b] + + submodule A_BlockCipher_Props where + import interface submodule I_BlockCipher + + decipher_inverts_encipher : k -> [b] -> Bool + property decipher_inverts_encipher K = + inverts (decipher K) (encipher K) +``` + +Common block cipher _modes of operation_ defined in NIST 800-38A +include: + * Electronic Codebook (ECB) + * Cipher Block Chaining (CBC) + * Cipher Feedback (CFB) + * Output Feedback (OFB) + +Let's start with the easy ones. + +```cryptol + submodule A_ECB where + import interface submodule I_BlockCipher + + encrypt : {t} k -> [t][b] -> [t][b] + encrypt K P = map (encipher K) P + + decrypt : {t} k -> [t][b] -> [t][b] + decrypt K C = map (decipher K) C + + decrypt_inverts_encrypt : {t} (fin t) => k -> [t][b] -> Bool + decrypt_inverts_encrypt K = + inverts (decrypt K) (encrypt K) + + submodule A_CBC where + import interface submodule I_BlockCipher + + encrypt : {t} k -> [b] -> [t][b] -> [t][b] + encrypt K IV P = C + where + C = [ encipher K (P_j ^ C_j') | P_j <- P | C_j' <- [IV] # C ] + + decrypt : {t} k -> [b] -> [t][b] -> [t][b] + decrypt K IV C = P + where + P = [ (decipher K C_j) ^ C_j' | C_j <- C | C_j' <- [IV] # C ] + + decrypt_inverts_encrypt : {t} (fin t) => k -> [b] -> [t][b] -> Bool + decrypt_inverts_encrypt K IV = + inverts (decrypt K IV) (encrypt K IV) +``` + +Unfortunately, because different cipher modes might take different +parameters (e.g. CBC requires an _initialization vector_ `iv`), we +cannot easily define an abstract (sub)module for cipher modes. + +**EXERCISE**: Define abstract submodules for CFB and OFB modes. + +```cryptol + submodule A_CFB where + TODO = undefined + + submodule A_OFB where + TODO = undefined +``` + +As tempting as it is to ask students to go reimplement AES, DES, etc., +let's just adapt an existing cipher to the `I_BlockCipher` interface +expected by our abstract cipher mode submodules. (This part requires +[`cryptol-specs`](https://github.com/GaloisInc/cryptol-specs) to be +cloned or downloaded and in `CRYPTOLPATH`.) + +```cryptol + import module Primitive::Symmetric::Cipher::Block::DES (DES) + + submodule P_DES where + type k = [64] + type b = 64 + + encipher = DES.encrypt + decipher = DES.decrypt + + submodule S_ECB_DES = submodule A_ECB { submodule P_DES } + import submodule S_ECB_DES as S_ECB_DES + + submodule S_CBC_DES = submodule A_CBC { submodule P_DES } + import submodule S_CBC_DES as S_CBC_DES +``` + +Why is ECB bad again? Let's check... + +```cryptol + ECB_DES_bad : {n} (fin n) => P_DES::k -> [P_DES::b] -> Bool + ECB_DES_bad K block = + S_ECB_DES::encrypt K (repeat`{n} block) == repeat (P_DES::encipher K block) + + CBC_DES_less_bad : {n} (fin n, n >= 2) => P_DES::k -> [P_DES::b] -> [P_DES::b] -> Bool + CBC_DES_less_bad K IV block = + S_CBC_DES::encrypt K IV (repeat`{n} block) != repeat (P_DES::encipher K block) +``` + +```Xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :prove ECB_DES_bad`{4} +Q.E.D. +(Total Elapsed Time: 1.893s, using "Yices") +labs::NewModuleSystem::NewModuleSystemAnswers> :check CBC_DES_less_bad`{4} +Using random testing. +Passed 100 tests. +Expected test coverage: 0.00% (100 of 2^^192 values) +``` + +So ECB is bad unless you never repeat yourself or say anything an +attacker would consider... + +**EXERCISE**: Feel free to experiment with other block ciphers +(including SIMON) and modes. Show that ECB is still bad with those. + +# Conclusion + +In this module, we learned how to apply the new module system to a +previously covered specification (SIMON) and to block cipher modes of +operation by defining common interfaces for abstractions to +instantiate implementations given parameters. The new module system +allowed us to combine all of these and a contrivance into a single +module for expository purposes. In the real world, it will eventually +form a solid foundation to organize a wide variety of cryptographic +algorithms and systems that combine them. + +# Solicitation + +How was your experience with this lab? Suggestions are welcome in the +form of a ticket on the course GitHub page: +https://github.com/weaversa/cryptol-course/issues + +# From here, you can go somewhere! + +|||| +|-:|:-:|-| +|| [ ^ Key Wrapping](../KeyWrapping/KeyWrapping.md) || +| [< Parameterized Modules](../SimonSpeck/SimonSpeck.md) | **New Module System** || +|| [? New Module System](./NewModuleSystem.md) || diff --git a/labs/SimonSpeck/SimonSpeck.md b/labs/SimonSpeck/SimonSpeck.md index 577b2f24..a85ab13d 100644 --- a/labs/SimonSpeck/SimonSpeck.md +++ b/labs/SimonSpeck/SimonSpeck.md @@ -178,8 +178,6 @@ Here is the header for the `Simon.cry` base module: ```example module labs::SimonSpeck::Simon::Simon where -module labs::SimonSpeck::Simon::Simon where - parameter /** word size */ @@ -408,4 +406,4 @@ https://github.com/weaversa/cryptol-course/issues |||| |-:|:-:|-| || [- Key Wrapping](../KeyWrapping/KeyWrapping.md) || -|| **Parameterized Modules** || +|| **Parameterized Modules** | [New Module System >](../NewModuleSystem/NewModuleSystem.md) | From f58ae032cf553fd515b64eabbc52e34f237c83fd Mon Sep 17 00:00:00 2001 From: --get-all Date: Tue, 27 Sep 2022 01:09:21 +0000 Subject: [PATCH 02/15] Replaced tabs with spaces --- labs/NewModuleSystem/NewModuleSystem.md | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index 3df6f890..8db3f224 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -216,19 +216,19 @@ and their constraints. interface submodule I_SIMON where /** word size */ type n : # - // TODO: Add constraint + // TODO: Add constraint /** number of words in key */ type m : # - // TODO: Add constraint + // TODO: Add constraint /** number of rounds in key schedule */ type T : # - // TODO: Add constraint + // TODO: Add constraint /** index of z-sequence used (i.e. use sequence z_j) */ type j : # - // TODO: Add constraint + // TODO: Add constraint ``` Recall that, given these parameters, SIMON exports: @@ -257,12 +257,12 @@ into a `submodule P_SIMON_2n_mn` (or `P_SIMON_n_m` if you prefer). ```cryptol submodule P_SIMON_32_64 where - type n = 0 - type m = 0 - type T = 0 - type j = 0 - - // TODO: Add remaining SIMON parameterizations + type n = 0 + type m = 0 + type T = 0 + type j = 0 + + // TODO: Add remaining SIMON parameterizations ``` **EXERCISE**: Define an abstract `submodule` `A_SIMON` that requests a @@ -280,11 +280,11 @@ likewise if you used another naming scheme earlier). * from parameters satisfying the `I_SIMON` interface */ submodule A_SIMON where - import interface submodule I_SIMON - TODO = undefined // Replace with abstract SIMON implementation + import interface submodule I_SIMON + TODO = undefined // Replace with abstract SIMON implementation submodule SIMON_32_64 = submodule A_SIMON { submodule P_SIMON_32_64 } - // TODO: Add remaining instantiations + // TODO: Add remaining instantiations ``` ```Xcryptol-session From 9e4f1cb8d47480f0b619e17d9f8f349b4787d86f Mon Sep 17 00:00:00 2001 From: --get-all Date: Sat, 18 Nov 2023 03:12:55 +0000 Subject: [PATCH 03/15] Add lab on new module system --- labs/NewModuleSystem/NewModuleSystem.md | 1193 ++++++++----- .../NewModuleSystem/NewModuleSystemAnswers.md | 1587 ++++++++++------- 2 files changed, 1637 insertions(+), 1143 deletions(-) diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index 8db3f224..3c584100 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -9,14 +9,19 @@ Before working through this lab, you'll need You'll also need experience with * loading modules and evaluating functions in the interpreter, - * parameterized modules - * ... + * the `:prove` command, + * writing functions and properties, + * sequence comprehensions, + * functions with curried parameters, and + * parameterized modules. ## Skills You'll Learn By the end of this lab you will have gained experience with Cryptol's -new module system, using it to implement a complex module with -multiple submodules and interfaces. +new module system to define common *interfaces* and *functors* to +generate related implementations and properties, enabling you to +create reusable families of cryptographic algorithms and modes, and +*nested modules* to combine these into one or more Cryptol modules. ## Load This Module @@ -30,7 +35,9 @@ running in the `cryptol-course` directory with: Loading module Cryptol Cryptol> :m labs::NewModuleSystem::NewModuleSystem Loading module Cryptol -Loading module labs::NewModuleSystem::NewModuleSystem +Loading interface module `parameter` interface of labs::SimonSpeck::Simon::Simon +Loading module labs::SimonSpeck::Simon::Simon +Loading mdoule labs::NewModuleSystem::NewModuleSystem ``` We start by defining a new module for this lab: @@ -40,565 +47,807 @@ module labs::NewModuleSystem::NewModuleSystem where ``` You do not need to enter the above into the interpreter; the previous -`:m ...` command loaded this literate Cryptol file automatically. +`:m ...` command loaded this literate Cryptol file automatically. In general, you should run `Xcryptol-session` commands in the interpreter and leave `cryptol` code alone to be parsed by `:m ...`. -# Background +# Cryptol's New Module System + +In a previous lab on parameterized modules, you learned how to specify +[*parameterized modules*](../SimonSpeck.md) and use these to generate +families of related +[Simon](https://en.wikipedia.org/wiki/Simon_(cipher)) and +[Speck](https://en.wikipedia.org/wiki/Speck_(cipher)) modules from +common parameters. Now suppose we wish to define +[block cipher modes of operation](https://en.wikipedia.org/wiki/Block_cipher_modes_of_operation) +such as +[Output Feedback (OFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB)) +or [authenticated encryption modes](https://en.wikipedia.org/wiki/Authenticated_encryption) +such as +[Galois Counter Mode (GCM)](https://en.wikipedia.org/wiki/Galois/Counter_Mode). +Using the techniques covered so far, we would have to specify similar +`parameter` blocks for each of the different modes, define similar +`property`s for them, and generally repeat ourselves a lot, which is +[WET](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself#WET) (bad). + +In this module, we will leverage Cryptol 3.0's powerful +[new module system](https://galoisinc.github.io/cryptol/master/Modules.html) +to define: + * a reusable + [`interface`](https://galoisinc.github.io/cryptol/master/Modules.html#interface-modules) + for block ciphers + * multiple block cipher *implementations* of this interface + * multiple block cipher modes as + [*functors*](https://galoisinc.github.io/cryptol/master/Modules.html#instantiating-a-parameterized-module) + to *instantiate* with any of these block cipher implementations + * functors to generate common properties and algorithm/mode-specific + test vectors from implementations of this same interface + * Cryptol batch files to check test vectors and + SAW proof scripts to verify more complex properties + +This is a tall order, but powerful new tools and features will help. +Let's get started... + +# Nested Modules (`submodule`) + +First, an administrative item: Cryptol's new module system is complex! +For consistency with other modules in our Cryptol course, we present +this as a single +[*nested module*](https://galoisinc.github.io/cryptol/master/Modules.html#nested-modules). +This is a new feature that enables us to present multiple +`submodule`s -- e.g. multiple instances of a common parameterized +module -- in a single file, which was not previously an option for +examples like the earlier +[**Parameterized Modules**](../SimonSpeck/SimonSpeck.md) lab. + +# Symmetric Block Ciphers and Modes of Operation + +Following along from [1], a *symmetric block cipher* comprises a pair +of algorithms: + * *encryption* (a "forward cipher operation") and + * *decryption* (a "reverse cipher operation") + +Each accepts... + * a **key** (of arbitrary type; usually a bit string) + * a **block** (a bit string) + +...and produces the corresponding ciphertext or plaintext. + +A **block cipher mode of operation** applies an underlying symmetric +block cipher over some number of blocks (or sub-blocks). + +# Interfaces + +Using prior techniques for parameterized modules, we might start each +mode's (sub)module with a parameter block (commented out here because +that's not how we'll finish...): + +(Here, we use the terms `encipher` and `decipher` to distinguish block +cipher operations from those for modes, in keeping with [1].) -Before proceeding, recall a prior lab describing -[parameterized modules](../SimonSpeck/SimonSpeck.md) -for the SIMON and SPECK algorithms. That lab required us to split -each parameter setting into its own module: +```cryptol +/* +parameter + /** arbitrary key type */ + type K : * + + /** block size (in bits) */ + type b : # + + /** forward cipher operator */ + encipher : K -> [b] -> [b] + + /** reverse cipher operator */ + decipher : K -> [b] -> [b] +*/ +``` + +But this approach already repeats itself, and we'd have to start each +(sub)module with the `parameter`s in this block (along with any others +unique to the mode). It would be better to reuse the common block +cipher parameters, and we can do so by defining an **interface** with +reusable **type aliases**: + +```cryptol +/** common parameters for symmetric block ciphers */ +interface submodule I_BlockCipher where + /** arbitrary key type */ + type Key : * + + /** block size (in bits) */ + type b : # + + /** type alias for a block, a bit string of width `b` */ + type Block = [b] + + /** common type for `encipher` and `decipher` */ + type Op = Key -> Block -> Block + + /** forward cipher operator */ + encipher : Op + + /** reverse cipher operator */ + decipher : Op +``` + +# Functors + +Each mode's (sub)module can then `import` this `interface` to serve the +same purpose as previously for `parameter` blocks: to indicate that the +module needs these parameters: + +```cryptol +/* (Not quite ready to define these modes yet... + +submodule ECB where + import interface submodule I_BlockCipher + // Now `type K`, `type b`, `encipher` and `decipher` are in scope + // But unfortunately, `type Block` and `type Op` are not... +*/ +``` -> But because this lab introduces further concepts around the -> definition and use of modules, this lab will not be entirely -> self-contained in this file. +This is an example of a +[**functor**](https://galoisinc.github.io/cryptol/master/Modules.html#importing-an-interface-module) +-- the modern, more versatile update to parameterized modules. Later, +we will see that such functors can accept multiple interfaces, and +define additional type constraints on their implementations. -Another more subtle limitation is that it is tricky to specify -multiple layers of parameterized modules, so specifications often -resort to repeating parameters. For SIMON, block size is repeated -for each corresponding key size and number of rounds, but this is -minor because block size is a simple integer. But for more complex -specifications like -[SHA-2](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Keyless/Hash/SHA2), -complex parameters like `K` (a sequence of words of bitlength 32 or -64) are repeated for different digest lengths at each word size, -potentially introducing easy-to-miss inconsistencies. +# Instantiation -# New Module System +As noted above, interfaces do not (yet) export their type aliases, but +it would be nice for users to have access to these. We'll quickly do +this with a functor that imports the same interface and exports the +(copied) type aliases. This also demonstrates an **interface alias** +to distinguish the interface's aliases (which would otherwise conflict +despite not being accessible) from the functor's: -These limitations motivated customers to ask Galois to -[update the module system](https://github.com/GaloisInc/cryptol/issues/815) -to support layered specifications, cipher modes, common interfaces, -and other such complexities. The long-awaited update will soon be (or -have been) [merged](https://github.com/GaloisInc/cryptol/pull/1363) -and -[documented](https://github.com/GaloisInc/cryptol/blob/master/docs/RefMan/Modules.rst). +```cryptol +submodule F_BlockCipher_Aliases where + // aliased interface import to avoid conflict + import interface submodule I_BlockCipher as I -## `submodule`: Nested Modules + /** type alias for a block, a bit string of width `b` */ + type Block = [I::b] + + /** common type for `encipher` and `decipher` */ + type Op = I::Key -> Block -> Block +``` -The new module system supports `submodule`s that can be nested into -other (`sub`)`module`s: +Then a `mode` can... ```cryptol - submodule Hash where - submodule XXHash where - TODO = undefined +/* + import interface submodule I_BlockCipher as I + import submodule F_BlockCipher_Aliases { interface I } (Block, Op) +*/ +``` + +...and reuse these aliases in its own definitions. - submodule SHA where - TODO = undefined +Of course, parameterizable block cipher modes need block ciphers! We +recently implemeented the Simon and Speck block ciphers, which are +themselves parameterized. We just need to adapt these to our fancy +new interface... - submodule SHA2 where - TODO = undefined +## Explicit Instantiation + +We could do so explicitly... + +```cryptol +/** Simon 32/64 implementation of `labs::SimonSpeck::Simon::Simon`'s parameters */ +submodule P_Simon_32_64' where + type n = 16 + type m = 4 + type T = 32 + type j = 0 + +/** Simon 32/64 instance */ +submodule Simon_32_64' = labs::SimonSpeck::Simon::Simon { submodule P_Simon_32_64' } + +/** explicit implementation of `submodule I_BlockCipher` with Simon 32/64 */ +submodule P_Simon_32_64_BlockCipher' where + import submodule Simon_32_64' as S + type Key = [S::keySize] + type b = S::blockSize + encipher = S::encrypt + decipher = S::decrypt ``` -This removes a previous limitation -- multiple parameterizations no -longer have to be specified in different modules. +This approach makes sense if we expect (us or our users) to refer to +these submodules later. -## Interfaces, Parameterization, Abstraction, and Instantiation +`labs::SimonSpeck::Simon::Simon` can be instantiated because its `parameter` +block is treated by the new module system as an +[**anonymous interface**](https://galoisinc.github.io/cryptol/master/Modules.html#anonymous-interface-modules). -In the new module system, an _interface_ specifies _parameters_ that an -_abstract_ (sub)module can _instantiate_ to produce an _implementation_. -Let's see this in a simple contrived example: +## Anonymous Instantiation + +If we don't intend to reuse these underlying implementations, we can also +implement our interface with an +[**anonymous instantiation**](https://galoisinc.github.io/cryptol/master/Modules.html#anonymous-instantiation-arguments): ```cryptol - submodule Contrivance where - interface submodule I_n where - type n : # - type constraint (2 >= n, n >= 1) +/** anonymous implementation of `submodule I_BlockCipher` with Simon 32/64 */ +submodule Simon_32_64_BlockCipher'' where + // ...by importing an anonymous instance of `labs::SimonSpeck::Simon::Simon` into a namespace `S` + import labs::SimonSpeck::Simon::Simon as S where + type n = 16 + type m = 4 + type T = 32 + type j = 0 + + type Key = [S::keySize] + type b = S::blockSize + encrypt = S::encrypt + decrypt = S::decrypt +``` - submodule A_n where - import interface submodule I_n +**Exercise**: Implement (define submodules that define types and values +for) `I_BlockCipher` for other Simon and Speck parameter sets, from +solutions (yours or ours) for +[`labs::SimonSpeck::SimonSpeck`](../SimonSpeck/SimonSpeck.md). Later, +we will define multiple block cipher modes for each of these block +ciphers and parameter sets... - type w = 32*n - type W = [w] +**Hint**: This will be less tedious if you define an `interface` and +functor with which to "adapt" prior solutions as implementations of +`I_BlockCipher`. Instances would look something like: - z : W - z = zero +`submodule P_Speck_64_128_BlockCipher = submodule F_Speck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_64_128 }` - submodule P_1 where - type n = 1 +Start with explicitly named submodules. These can always be +"anonymized" later if it is obvious they won't be reused. - submodule P_2 where - type n = 2 +```cryptol +/** interface to collect relevant definitions from Simon instance */ +interface submodule I_Simon_Inst where + type keySize : # + type blockSize : # - submodule Z_1 = submodule A_n { submodule P_1 } - submodule Z_2 = submodule A_n { submodule P_2 } - - import submodule Z_1 as Z32 - import submodule Z_2 as Z64 - - property zero_32 = Z32::z == zero`{[32]} - property zero_64 = Z64::z == zero`{[64]} -``` - -The above snippet introduces a `submodule` `Contrivance`. Within, it -defines an `interface` with a numeric parameter `n` bound to the -interval `[1,2]`. An abstract submodule `A_n` requests a -parameterization satisfying `I_n`, and produces an implementation -defining `z`, a `zero` value of `type W = [w]`, where `type w = 32*n`. -Parameterizations `P_1` and `P_2` specify `n` values of `1` and `2`, -respectively, each satisfying `I_n`. Parameterizations `P_1` and `P_2` -instantiate `A_n` to define `Z_1` and `Z_2`, respectively, with `z` as -a corresponding `32`- or `64`-bit `zero`, as expressed by properties -`zero_32` and `zero_64`, respectively; each property requires the -instantiated submodules to be imported with aliases (here, `Z32` and -`Z64`) to distinguish their respective `z` definitions: + type Key = [keySize] + type Block = [blockSize] + type Op = Key -> Block -> Block -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :prove Contrivance::zero_32 -:prove Contrivance::zero_32 - Q.E.D. -(Total Elapsed Time: 0.035s, using "Z3") -labs::NewModuleSystem::NewModuleSystem> :prove Contrivance::zero_64 -:prove Contrivance::zero_64 - Q.E.D. -(Total Elapsed Time: 0.031s, using "Z3") -``` + encrypt : Op + decrypt : Op -All of the above were defined within the `Contrivance` submodule, -hence the leading `Contrivance::` in the property names. +// generalization of `Simon_32_64_BlockCipher` above +/** generate `I_BlockCipher` implementation from Simon instance */ +submodule F_SimonSpeck_BlockCipher where + import interface submodule I_Simon_Inst as S -## Scope + // export definitions for `I_BlockCipher` parameters + // type Key = [S::keySize] + // type b = // Define and uncomment. + + encipher = S::encrypt + decipher = undefined // Define and uncomment. + +/** `I_BlockCipher` implementation for Simon 32/64 */ +submodule P_Simon_32_64_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_32_64 } + +// /** `I_BlockCipher` implementation for Simon 48/72 */ +// submodule P_Simon_48_72_BlockCipher = // Define and uncomment. + +// Repeat for remaining Simon and Speck parameter sets. +``` -Only the top-level definitions `zero_32` and `zero_64` are in scope -after loading the top-level module from this point. Nested submodules -are not exported; to make `z` from each submodule available as well, -they must be reassigned explicitly: +## Adapting other block ciphers + +In the last exercise, you adapted your own prior work to fit out new +`I_BlockCipher` interface. We can also adapt other simple block +ciphers, such as DES in the +[Cryptographic Proofs](../CryptoProofs/CryptoProofs.md) lab. ```cryptol - z32 = Z32::z - z64 = Z64::z +import specs::Primitive::Symmetric::Cipher::Block::DES (DES) ``` -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :h Contrivance +**Exercise**: Adapt `DES` with a submodule that provides all parameters +required by `interface submodule I_BlockCipher`: -Module Contrivance exports: +```cryptol +submodule P_DES_BlockCipher where + // type Key = ? // Define and uncomment. + type b = 64 - - zero_32 : Bit - zero_64 : Bit - z32 : W - z64 : W -labs::NewModuleSystem::NewModuleSystem> Contrivance::z32 -0x00000000 -labs::NewModuleSystem::NewModuleSystem> Contrivance::z64 -0x0000000000000000 -labs::NewModuleSystem::NewModuleSystem> Contrivance::zero_32 -True -labs::NewModuleSystem::NewModuleSystem> Contrivance::zero_64 -True -``` - -# Practicum - -Having introduced the new module system, let's apply it to SIMON and -compare the former and new module systems. - -## Simon says what? - -Recall that SIMON is parameterized on four constrained numeric (`#`) -`type`s: - * `n` (word size) - * `m` (words per key) - * `T` (rounds in key schedule) - * `j` (index of z-sequence) - -**EXERCISE**: Define an `interface submodule` comprising these types -and their constraints. -(**Hint**: Just copy them from the previous lab.) + encipher = DES.encrypt + decipher = undefined // Replace with your definition. +``` + +# Block Cipher Modes of Operation: ECB, CBC, ... + +Having implemented various block ciphers, we can now apply them toward +**block cipher modes of operation** such as: + - [Electronic Codebook (ECB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) + - [Cipher Block Chaining (CBC)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_block_chaining_(CBC)) + - [Cipher Feedback (CFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB)) + - ... + +Each cipher mode repeatedly applies a block cipher to multiple blocks +in a message -- often with an **initialization vector** and +intermediate transformations, and possibly after **padding** the +message to evenly divide it into blocks -- to produce a ciphertext +message with the same number of blocks. (Authenticated encryption +generates a **signature** alongside the ciphertext.) + +## Electronic Codebook (ECB) + +ECB is a simple block cipher mode that just enciphers each block, with +no intermediate transformations to **diffuse** the message, a weakness +that disqualifies it for use in security-critical settings. But just +as a pedagogical exercise, we can define ECB in Cryptol... + +**Exercise**: In the specification of ECB mode below, define `decrypt` +so that using the same shared key on an encrypted ciphertext message +produces the original plaintext. Do not include an argument for the +message in your definition. ```cryptol - submodule SIMON where - /** interface specifying parameters for SIMON block cipher */ - interface submodule I_SIMON where - /** word size */ - type n : # - // TODO: Add constraint - - /** number of words in key */ - type m : # - // TODO: Add constraint - - /** number of rounds in key schedule */ - type T : # - // TODO: Add constraint - - /** index of z-sequence used (i.e. use sequence z_j) */ - type j : # - // TODO: Add constraint -``` - -Recall that, given these parameters, SIMON exports: - * derived `type`s: - * `type blockSize = 2 * n` - * `type keySize = m * n` - * block cipher functions: - * `encrypt : [keySize] -> [blockSize] -> [blockSize]` - * `decrypt : [keySize] -> [blockSize] -> [blockSize]` - * properties: - * `EncryptDecryptIdentity : [keySize] -> [blockSize] -> Bool` - * `DecryptEncryptIdentity : [keySize] -> [blockSize] -> Bool` - -These exported definitions rely on numerous `private` ones. - -Eventually, we will need to (re)implement `encrypt` and `decrypt` -functions in an abstract submodule that imports `I_SIMON`. Testing the -implementations will require parameterizations satisfying `I_n` to -instantiate the abstract submodule. So let's start there: - -**EXERCISE**: Define submodules with parameters satisfying `I_n` for -each approved `n` (word size) and `m` (words per key), or just a few -if you're so inclined. (`T` and `j` depend on `n` and `m`.) -(**Hint**: For each `simon_2n_mn.cry`, copy its `parameter` block body -into a `submodule P_SIMON_2n_mn` (or `P_SIMON_n_m` if you prefer). +/** Disclaimer: Weak block cipher mode; do not use in production. */ +submodule ECB where + // request a block cipher + import interface submodule I_BlockCipher + + // derive the type aliases for that block cipher + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } (Block) + + /** Encrypt a multi-block message using ECB mode */ + encrypt : {n} Key -> [n]Block -> [n]Block + encrypt key = map (encipher key) + + /** Decrypt a multi-block message using ECB mode */ + decrypt : {n} Key -> [n]Block -> [n]Block + decrypt key = undefined // Replace with your definition. +``` + +Again, please do not ever use this weak cipher mode in the real world. + +**Exercise**: Define submodules for each Simon and Speck parameter set +in ECB mode. ```cryptol - submodule P_SIMON_32_64 where - type n = 0 - type m = 0 - type T = 0 - type j = 0 - - // TODO: Add remaining SIMON parameterizations -``` - -**EXERCISE**: Define an abstract `submodule` `A_SIMON` that requests a -parameterization satisfying `I_SIMON` and exports an implementation -with the above definitions, and instantiate this with each of the -above parameterizations into concrete submodules. `:check` your work. -(**Hint**: Just copy the previous definitions from `Simon.cry` from -the previous lab, and instatiate them using lines of the form -`submodule SIMON_2n_mn = submodule A_SIMON { submodule P_SIMON_2n_mn }` (or -likewise if you used another naming scheme earlier). +/** Simon 32/64 in ECB mode */ +submodule ECB_Simon_32_64 = submodule ECB { submodule P_Simon_32_64_BlockCipher } + +/** Simon 48/72 in ECB mode */ +// submodule ECB_Simon_48_72 = ... // Replace with your definition and uncomment. + +// Repeat for each Simon and Speck parameter set. +``` + +**Exercise**: Likewise, define a submodule that implements DES (a weak +block cipher) in ECB (a weak cipher mode). Add a docstring with a +prominent warning not to use it in production. ```cryptol - /** - * abstraction that produces a SIMON block cipher implementation - * from parameters satisfying the `I_SIMON` interface - */ - submodule A_SIMON where - import interface submodule I_SIMON - TODO = undefined // Replace with abstract SIMON implementation +// /** Add a docstring */ +// submodule ECB_DES = submodule ... { submodule P_DES_BlockCipher } // Replace with your definition and uncomment. +``` + +Your solution should pass the following test vectors [3]: + +```cryptol +submodule ECB_DES_Test where + import submodule ECB_DES as ECB_DES + + e (key, pt, ct) = ECB_DES::encrypt key pt == ct + d (key, pt, ct) = ECB_DES::decrypt key ct == pt - submodule SIMON_32_64 = submodule A_SIMON { submodule P_SIMON_32_64 } - // TODO: Add remaining instantiations + // from FIPS 81 (withdrawn, of course) + v_fips_81 = (0x0123456789abcdef, [0x4e6f772069732074, 0x68652074696d6520, 0x666f7220616c6c20], [0x3fa40e8a984d4815, 0x6a271787ab8883f9, 0x893d51ec4b563b53]) + property e_fips_81 = e v_fips_81 + property d_fips_81 = d v_fips_81 + + // another test vector based on `https://opensource.apple.com/source/OpenSSL/OpenSSL-23/openssl/test/evptests.txt.auto.html` + v35 = (0x1111111111111111, [0x1111111111111111, 0x0123456789ABCDEF], [0xF40379AB9E0EC533, 0x8A5AE1F81AB8F2DD]) + property e35 = e v35 + property d35 = d v35 ``` -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :check -property Contrivance::zero_32 Using exhaustive testing. +```xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :check ECB_DES_Test::e_fips_81 +Using exhaustive testing. Passed 1 tests. Q.E.D. -property Contrivance::zero_64 Using exhaustive testing. +labs::NewModuleSystem::NewModuleSystem> :check ECB_DES_Test::d_fips_81 +Using exhaustive testing. Passed 1 tests. Q.E.D. -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^48 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^48 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^120 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^160 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^240 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^256 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^320 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^384 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_32_64::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_72::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^120 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_48_96::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_96::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^160 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_64_128::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_96::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_96_144::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^240 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_128::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^256 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_192::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^320 values) -property labs::NewModuleSystem::NewModuleSystem::SIMON::SIMON_128_256::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^384 values) -``` - -## Simon asks why? - -Interface, parameterizations, abstractions, and instantiations -- all -to reimplement what parameterized modules are already able to do with -`parameter` blocks and parameterized `import`s. Sure, the new system -allows us to define the whole family of SIMON ciphers, alongside our -earlier contrivance no less, but is this worth all the trouble? If the -answer is immediately obvious at this point, feel free to skip the -rest of this lab, secure in your understanding of nested modules, -interfaces, and the opportunities and conundrums they bring. -Otherwise, let's show off a concept that was difficult to pull off -with the old system: cipher modes. - -Recall that our SIMON spec defined `encrypt` and `decrypt` functions -over `key` and `block` sizes. Furthermore, it defined a property that -`decrypt` inverts `encrypt`. These are all rather common, and having -to define the inversion property for AES, Blowfish, RC5, and any other -[block cipher](https://en.wikipedia.org/wiki/Block_cipher) -that comes along gets very tedious very fast. - -Now suppose we wish to run each of these ciphers in various -[modes](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation): -[Electronic Codebook (ECB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB) -(only to demonstrate why that's a really bad idea), -[Cipher Block Chaining (CBC)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC), -[Cipher Feedback (CFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB), -... If we had to define each mode, for each block cipher, again, this -would make Cryptol authors wish gone into yodeling or sports -broadcasting. So until this point, except in a few cases where -[an effort was made](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Symmetric/Cipher/Block/Modes), -this has mostly been avoided. Nested modules, for all their verbosity, -offer another approach: - * Define a block cipher interface - * Define abstract cipher modes that request block cipher parameters - * Instantiate cipher modes with parameterized block ciphers - -# Block Ciphers - -Working from -[NIST SP 800-38A](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), -a _block cipher_ is a function that maps `b`-bit plaintext blocks -to `b`-bit ciphertext blocks, parameterized by a key `K` of arbitrary -type. NIST SP 800-38A refers to block encryption and decryption as -forward and inverse ciphers to disambiguate them from corresponding -mode operations. Let's rename these to `encipher` and `decipher`, -respectively, in keeping with Cryptol naming conventions. +labs::NewModuleSystem::NewModuleSystem> :exhaust ECB_DES_Test::e35 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :exhaust ECB_DES_Test::d35 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :exhaust ECB_DES_Test::e53 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :exhaust ECB_DES_Test::d53 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + +## Cipher Block Chaining (CBC) + +To better diffuse message blocks, CBC introduces an *initialization +vector* and applies the binary exclusive-or operator (`^` in Cryptol) +to each block of plaintext with the previous ciphertext (the +initialization vector for the first block) before applying the block +cipher with a given key. That is: + +`c0 = encipher key (p0 ^ iv)` +`c1 = encipher key (p1 ^ c0)` +`c2 = encipher key (p2 ^ c1)` +`...` +`cn = encipher key (pn ^ ...)` + +**Exercise**: Define a functor to generate a CBC mode for a given block +cipher. ```cryptol - import labs::Transposition::CommonProperties (inverts) +submodule CBC where + // request a block cipher + import interface submodule I_BlockCipher + + // derive the type aliases for that block cipher + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } (Block) - interface submodule I_BlockCipher where - type k : * - type b : # - type constraint (fin b) + /** Encrypt a multi-block message using CBC mode */ + encrypt : {n} Key -> Block -> [n]Block -> [n]Block + encrypt key iv pt = undefined // Replace with your definition. - encipher : k -> [b] -> [b] - decipher : k -> [b] -> [b] + /** Decrypt a multi-block message using CBC mode */ + decrypt : {n} Key -> Block -> [n]Block -> [n]Block + decrypt key iv ct = undefined // Replace with your definition. +``` - submodule A_BlockCipher_Props where - import interface submodule I_BlockCipher +**Hint**: The above definition can be directly translated to a Cryptol +[generating function](https://galoisinc.github.io/cryptol/master/BasicTypes.html#sequences). +(See `generate` and the examples following it.) So for `encrypt`, you +can say `encrypt key iv pt = ct where ct@i = ...` and then fill in the +rest. Remember to account for the initialization vector and the first +ciphertext block `ct@0`. + +**Note**: Though we suggest generator expressions here, it is also +possible to use various higher-order functions such as `foldl` and +`zipWith`. For now, this is mostly a matter of style. However, when +it comes time to verify that our submodules meet important properties +(most notably, that using the same key and initialization vector +to decrypt ciphertext recovers the original plaintext), this choice +will greatly affect future verification efforts... + +**Hint**: We have not specified above how to derive plaintext blocks +from ciphertext blocks. If you get stuck defining `decrypt`, using the +above equalities, you can work backward to solve for each plaintext +block. You will need simple properties about `(^)` and a very +important property of (useful) block ciphers, defined as +`block_recovery` below. - decipher_inverts_encipher : k -> [b] -> Bool - property decipher_inverts_encipher K = - inverts (decipher K) (encipher K) +```cryptol +// property block_recovery key pt = decipher key (encipher key pt) == pt +// property xor_inv_r x y = (x ^ y) ^ y == x +// property xor_inv_l x y = x ^ (x ^ y) == y ``` -Common block cipher _modes of operation_ defined in NIST 800-38A -include: - * Electronic Codebook (ECB) - * Cipher Block Chaining (CBC) - * Cipher Feedback (CFB) - * Output Feedback (OFB) +Working backward from our generator expression for `encrypt`... + +`ct@i = encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1)))` +(Apply `decipher key` to both sides.) +`decipher key (ct@i) = decipher key (encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1))))` +(Apply `block_recovery` on right.) +`decipher key (ct@i) = pt@i ^ ct'` +(Apply `(^) ct'` to both sides, where `ct'` is the `if`-expression.) +`decipher key (ct@i) ^ ct' = pt@i ^ ct' ^ ct' +(Apply `xor_inv_r` on right.) +`decipher key (ct@i) ^ ct' = pt@i` +(Flip sides for assignment to plaintext.) +`pt@i = decipher key (ct@i) ^ ct'` +(Substitute the `if`-expression back in for `ct'`.) +`pt@i = decipher key (ct@i) ^ (if i == 0 then iv else ct@(i-1)))` -Let's start with the easy ones. +**Note**: Could you define `decrypt` without an initialization vector? + +As with `ECB`, we can now define a `CBC` mode for various Simon and +Speck configurations, or any other simple block cipher that implements +our interface. + +**Exercise**: Define submodules for each Simon and Speck parameter set +in CBC mode. ```cryptol - submodule A_ECB where - import interface submodule I_BlockCipher +// /** Simon 32/64 in CBC mode */ +// submodule CBC_Simon_32_64 = submodule ... { submodule ... } // Replace with your definition and uncomment. - encrypt : {t} k -> [t][b] -> [t][b] - encrypt K P = map (encipher K) P +// Repeat for other Simon and Speck parameter sets. +``` - decrypt : {t} k -> [t][b] -> [t][b] - decrypt K C = map (decipher K) C +**Exercise**: Likewise, define a submodule that implements DES (a weak +block cipher) in CBC (a cipher mode). Add a docstring with a prominent +warning not to use it in production. - decrypt_inverts_encrypt : {t} (fin t) => k -> [t][b] -> Bool - decrypt_inverts_encrypt K = - inverts (decrypt K) (encrypt K) +```cryptol +// /** Add a docstring. */ +// submodule CBC_DES = ... // Replace with your definition and uncomment. +``` - submodule A_CBC where - import interface submodule I_BlockCipher +Your solution should pass the following test vectors [3]: - encrypt : {t} k -> [b] -> [t][b] -> [t][b] - encrypt K IV P = C - where - C = [ encipher K (P_j ^ C_j') | P_j <- P | C_j' <- [IV] # C ] +```cryptol +submodule CBC_DES_Test where + import submodule CBC_DES as CBC_DES - decrypt : {t} k -> [b] -> [t][b] -> [t][b] - decrypt K IV C = P - where - P = [ (decipher K C_j) ^ C_j' | C_j <- C | C_j' <- [IV] # C ] + e (key, iv, pt, ct) = CBC_DES::encrypt key iv pt == ct + d (key, iv, pt, ct) = CBC_DES::decrypt key iv ct == pt + + // from FIPS 81 (withdrawn, of course) + v_fips_81 = (0x0123456789abcdef, 0x1234567890abcdef, [0x4e6f772069732074, 0x68652074696d6520, 0x666f7220616c6c20], [0xe5c7cdde872bf27c, 0x43e934008c389c0f, 0x683788499a7c05f6]) + + property e_fips_81 = e v_fips_81 + property d_fips_81 = d v_fips_81 +``` - decrypt_inverts_encrypt : {t} (fin t) => k -> [b] -> [t][b] -> Bool - decrypt_inverts_encrypt K IV = - inverts (decrypt K IV) (encrypt K IV) +```xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :check CBC_DES_Test::e_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check CBC_DES_Test::d_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. ``` -Unfortunately, because different cipher modes might take different -parameters (e.g. CBC requires an _initialization vector_ `iv`), we -cannot easily define an abstract (sub)module for cipher modes. +## Cipher Feedback (CFB) + +CFB can take any of various forms, including a full-block mode or one +that segments block into *subblocks*. In the full-block configuration, +CFB is similar to CBC except that the XOR and cipher operations are +performed in the other order, i.e.: + +`c0 = (encipher key iv) ^ pt0` +`c1 = (encipher key c0) ^ pt1` +`c2 = (encipher key c1) ^ pt2` +`...` -**EXERCISE**: Define abstract submodules for CFB and OFB modes. +**Exercise**: Define CFB full-block mode for a given block cipher. ```cryptol - submodule A_CFB where - TODO = undefined +submodule CFB where + // request a block cipher + import interface submodule I_BlockCipher - submodule A_OFB where - TODO = undefined + // derive the type aliases for that block cipher + import submodule F_BlockCipher_Aliases { I = interface I_BlockCipher } (Block) + + /** Encrypt a multi-block message using CFB mode */ + encrypt : {n} fin n => Key -> Block -> [n]Block -> [n]Block + encrypt key iv pt = undefined // Replace with your definition. + + /** Decrypt a multi-block message using CFB mode */ + decrypt : {n} fin n => Key -> Block -> [n]Block -> [n]Block + decrypt key iv ct = undefined // Replace with your definition. +``` + +**Hint**: It is not necessary for a functor to use all the fields +in its interface(s). Asking for unused fields is perhaps questionable, +but being able to reuse the same interface might turn out to justify +an ignored field... + +**Hint**: The different order of operations has a profound impact on +CFB `decrypt` that might seem counterintuitive at first. Again, if +you get stuck defining `decrypt`, work out from the above definition +how to solve for plaintext blocks in terms of ciphertext. That is, +given `ct_i = (encipher key iv) ^ pt_i`, solve for `pt_i`. + +**Exercise**: Define a submodule that implements DES (a weak block +cipher) in full-block CFB mode (a cipher mode). Add a docstring with +a prominent warning not to use it in production. + +**Solution**: + +```cryptol +// /** ... */ +// submodule CFB_DES = ... // Replace with your definition and uncomment. ``` -As tempting as it is to ask students to go reimplement AES, DES, etc., -let's just adapt an existing cipher to the `I_BlockCipher` interface -expected by our abstract cipher mode submodules. (This part requires -[`cryptol-specs`](https://github.com/GaloisInc/cryptol-specs) to be -cloned or downloaded and in `CRYPTOLPATH`.) +Your solution should pass the following test vectors [3]: ```cryptol - import module Primitive::Symmetric::Cipher::Block::DES (DES) +submodule CFB_DES_Test where + import submodule CFB_DES as CFB_DES + + e (key, iv, pt, ct) = CFB_DES::encrypt key iv pt == ct + d (key, iv, pt, ct) = CFB_DES::decrypt key iv ct == pt - submodule P_DES where - type k = [64] - type b = 64 - - encipher = DES.encrypt - decipher = DES.decrypt + // from FIPS 81 (withdrawn, of course) + v_fips_81 = (0x0123456789abcdef, 0x1234567890abcdef, [0x4e6f772069732074, 0x68652074696d6520, 0x666f7220616c6c20], [0xf3096249c7f46e51, 0xa69e839b1a92f784, 0x03467133898ea622]) + + property e_fips_81 = e v_fips_81 + property d_fips_81 = d v_fips_81 +``` + +```xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :check CFB_DES_Test::e_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check CFB_DES_Test::d_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + +## Counter (CTR) Mode (Multiple Interfaces and Interface Constraints) + +Counter mode produces a stream cipher by applying a block cipher to +successive values of a "counter" that produces a long sequence of +non-repeating values -- most commonly a simple incremental counter. - submodule S_ECB_DES = submodule A_ECB { submodule P_DES } - import submodule S_ECB_DES as S_ECB_DES +This counter is concatenated with a *nonce*, which acts much as the +initialization vector from other modes into blocks, which are +enciphered with the key and then XOR'ed (or another invertible +operation) with each plaintext block: - submodule S_CBC_DES = submodule A_CBC { submodule P_DES } - import submodule S_CBC_DES as S_CBC_DES +`c0 = encipher key (nonce # 0) ^ p0` +`c1 = encipher key (nonce # 1) ^ p1` +`c2 = encipher key (nonce # 2) ^ p2` + +Our CTR specification will need to define and import an interface for +the additional nonce-specific parameters, and constrain these such +that a nonce and counter can be concatenated into a `Block`. Cryptol's +new module system can specify an +[`interface constraint`](https://galoisinc.github.io/cryptol/master/Modules.html#interface-constraints) +for parameters provided for one or more interfaces, e.g. + +```cryptol +// import interface submodule I +// import interface submodule J +// interface constraint I::n == J::n ``` -Why is ECB bad again? Let's check... +**Exercise**: Define CTR mode for a given block cipher and CTR-specific +parameters. ```cryptol - ECB_DES_bad : {n} (fin n) => P_DES::k -> [P_DES::b] -> Bool - ECB_DES_bad K block = - S_ECB_DES::encrypt K (repeat`{n} block) == repeat (P_DES::encipher K block) +interface submodule I_CTR where + // nonce width + type w: # + + // counter width + type c: # - CBC_DES_less_bad : {n} (fin n, n >= 2) => P_DES::k -> [P_DES::b] -> [P_DES::b] -> Bool - CBC_DES_less_bad K IV block = - S_CBC_DES::encrypt K IV (repeat`{n} block) != repeat (P_DES::encipher K block) +submodule F_CTR_Aliases where + import interface submodule I_CTR as I + + // type alias for nonce + type Nonce = [I::w] + +submodule CTR where + // request a block cipher + import interface submodule I_BlockCipher + // request parameters for CTR + import interface submodule I_CTR + + // interface constraint ... // Define constraints over parameters in `I_BlockCipher` and `I_CTR`, and uncomment. + + // derive the type aliases for that block cipher, and for the nonce + import submodule F_BlockCipher_Aliases { I = interface I_BlockCipher } (Block) + import submodule F_CTR_Aliases { I = interface I_CTR } (Nonce) + + /** Encrypt a multi-block message using CFB mode */ + encrypt : {n} c >= width (max 1 n - 1) => Key -> Nonce -> [n]Block -> [n]Block + encrypt key nonce pt = undefined // Replace with your definition. + + /** Decrypt a multi-block message using CFB mode */ + decrypt : {n} c >= width (max 1 n - 1) => Key -> Nonce -> [n]Block -> [n]Block + decrypt key nonce ct = undefined // Replace with your definition. ``` -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :prove ECB_DES_bad`{4} -Q.E.D. -(Total Elapsed Time: 1.893s, using "Yices") -labs::NewModuleSystem::NewModuleSystem> :check CBC_DES_less_bad`{4} -Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) +**Hint**: Use generator expressions again. + +**Exercise**: Find test vectors for a block cipher in CTR mode, specify +it, and verify the test vectors. + +## ...and more! + +**Exercise**: Feel free to specify other basic block cipher modes +such as PCBC, OFB, segmented CFB, etc. + +**TODO**: Introduce authenticated cipher modes, a positive +authentication property, and resilience properties (tamper, spoofing). + +**TODO**: Get to the point where (sub)modules can import interfaces and +specify properties assumed about their implementations. Use these to +generate "contracts" and reusable test cases. + +# Properties and Verification (Interface Reuse) + +[2] defines the *CIA triad* of: + - *confidentiality* + - *integrity* + - *availability* + +In these terms, a cipher's purpose is to provide confidentiality (for +authorized persons to securely exchange message so that they are not +easily recoverable by unauthorized persons) and availability (messages +need to remain readable by authorized persons). + +In particular, at the expense of repeating ourselves, a cipher must +satisfy this familiar property, which you may have already used to +understand how to specify `decrypt` for block cipher modes: + +```cryptol +// property block_recovery key pt = decipher key (encipher key pt) == pt +``` + +In other words, a cipher must remain *available* to those with a shared +symmetric key. This must also hold true for cipher modes, e.g. for a +cipher mode with an initialization vector: + +```cryptol +// property mode_recovery = decrypt key iv (encrypt key iv pt) == pt ``` -So ECB is bad unless you never repeat yourself or say anything an -attacker would consider... +Another prerequisite of availability is that authorized parties agree +on how the cipher operates. Cryptol expresses algorithms as rigorous +formal specifications, but users more commonly confirm this agreement +using *test vectors*, e.g.: + +```cryptol +import submodule P_Simon_32_64_BlockCipher as S_32_64 +import submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } as S_32_64 -**EXERCISE**: Feel free to experiment with other block ciphers -(including SIMON) and modes. Show that ECB is still bad with those. +S_32_64_test_key = 0x94eeea8b1f2ada84 +S_32_64_test_pt = 0xadf10331 +S_32_64_test_ct = 0x391e7f1a + +property test_simon_32_64_enc = S_32_64::encipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct +property test_simon_32_64_dec = S_32_64::decipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct +``` + +A strong cipher must resist cryptanalytic attacks of varying +sophistication. Cryptol and SMT solvers are not designed to perform +rigorous cryptanalysis, but can run some quick sanity checks. + +For example, it should not be easy to (ab)use SMT solvers to quickly +recover a cryptographic key from known or chosen plaintext: + +```xcryptol-session +// Try to recover `test_key` from known test plaintext/ciphertext +// labs::ModuleSystem> :sat \key -> S_32_64::encipher key test_pt == test_ct +``` + +**TODO**: Introduce exercises to check `mode_recovery` for various +block cipher modes, and show how to use SAW to formally verify these +using a block cipher's `block_recovery` property when applicable. # Conclusion -In this module, we learned how to apply the new module system to a -previously covered specification (SIMON) and to block cipher modes of -operation by defining common interfaces for abstractions to -instantiate implementations given parameters. The new module system -allowed us to combine all of these and a contrivance into a single -module for expository purposes. In the real world, it will eventually -form a solid foundation to organize a wide variety of cryptographic -algorithms and systems that combine them. +In this module, you learned how to define and reuse interfaces and +functors as the cryptographic building blocks (pun intended) of a +complex nested module with multiple ciphers, modes, and properties. + +# References + +[1] NIST SP 800-38A. + "Recommendation for Block Cipher Modes of Operation: Tools and Techniques". + December 2001. + https://csrc.nist.gov/pubs/sp/800/38/a/final + +[2] NIST SP 800-12 Rev. 1. + "An Introduction to Information Security". + June 2017. + https://csrc.nist.gov/pubs/sp/800/12/r1/final + +[3] NIST FIPS 81 (withdrawn). + "DES MODES OF OPERATION" + 2 December 1980. + https://csrc.nist.gov/pubs/fips/81/final # Solicitation @@ -610,6 +859,6 @@ https://github.com/weaversa/cryptol-course/issues |||| |-:|:-:|-| -|| [ ^ Key Wrapping](../KeyWrapping/KeyWrapping.md) || +|| [- Key Wrapping](../KeyWrapping/KeyWrapping.md) || | [< Parameterized Modules](../SimonSpeck/SimonSpeck.md) | **New Module System** || || [! New Module System (Answers)](./NewModuleSystemAnswers.md) || diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md index 5e59a640..2c59edee 100644 --- a/labs/NewModuleSystem/NewModuleSystemAnswers.md +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -9,14 +9,19 @@ Before working through this lab, you'll need You'll also need experience with * loading modules and evaluating functions in the interpreter, - * parameterized modules - * ... + * the `:prove` command, + * writing functions and properties, + * sequence comprehensions, + * functions with curried parameters, and + * parameterized modules. ## Skills You'll Learn By the end of this lab you will have gained experience with Cryptol's -new module system, using it to implement a complex module with -multiple submodules and interfaces. +new module system to define common *interfaces* and *functors* to +generate related implementations and properties, enabling you to +create reusable families of cryptographic algorithms and modes, and +*nested modules* to combine these into one or more Cryptol modules. ## Load This Module @@ -30,7 +35,9 @@ running in the `cryptol-course` directory with: Loading module Cryptol Cryptol> :m labs::NewModuleSystem::NewModuleSystemAnswers Loading module Cryptol -Loading module labs::NewModuleSystem::NewModuleSystemAnswers +Loading interface module `parameter` interface of labs::SimonSpeck::Simon::Simon +Loading module labs::SimonSpeck::Simon::Simon +Loading mdoule labs::NewModuleSystem::NewModuleSystemAnswers ``` We start by defining a new module for this lab: @@ -40,764 +47,1002 @@ module labs::NewModuleSystem::NewModuleSystemAnswers where ``` You do not need to enter the above into the interpreter; the previous -`:m ...` command loaded this literate Cryptol file automatically. +`:m ...` command loaded this literate Cryptol file automatically. In general, you should run `Xcryptol-session` commands in the interpreter and leave `cryptol` code alone to be parsed by `:m ...`. -# Background +# Cryptol's New Module System + +In a previous lab on parameterized modules, you learned how to specify +[*parameterized modules*](../SimonSpeck.md) and use these to generate +families of related +[Simon](https://en.wikipedia.org/wiki/Simon_(cipher)) and +[Speck](https://en.wikipedia.org/wiki/Speck_(cipher)) modules from +common parameters. Now suppose we wish to define +[block cipher modes of operation](https://en.wikipedia.org/wiki/Block_cipher_modes_of_operation) +such as +[Output Feedback (OFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB)) +or [authenticated encryption modes](https://en.wikipedia.org/wiki/Authenticated_encryption) +such as +[Galois Counter Mode (GCM)](https://en.wikipedia.org/wiki/Galois/Counter_Mode). +Using the techniques covered so far, we would have to specify similar +`parameter` blocks for each of the different modes, define similar +`property`s for them, and generally repeat ourselves a lot, which is +[WET](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself#WET) (bad). + +In this module, we will leverage Cryptol 3.0's powerful +[new module system](https://galoisinc.github.io/cryptol/master/Modules.html) +to define: + * a reusable + [`interface`](https://galoisinc.github.io/cryptol/master/Modules.html#interface-modules) + for block ciphers + * multiple block cipher *implementations* of this interface + * multiple block cipher modes as + [*functors*](https://galoisinc.github.io/cryptol/master/Modules.html#instantiating-a-parameterized-module) + to *instantiate* with any of these block cipher implementations + * functors to generate common properties and algorithm/mode-specific + test vectors from implementations of this same interface + * Cryptol batch files to check test vectors and + SAW proof scripts to verify more complex properties + +This is a tall order, but powerful new tools and features will help. +Let's get started... + +# Nested Modules (`submodule`) + +First, an administrative item: Cryptol's new module system is complex! +For consistency with other modules in our Cryptol course, we present +this as a single +[*nested module*](https://galoisinc.github.io/cryptol/master/Modules.html#nested-modules). +This is a new feature that enables us to present multiple +`submodule`s -- e.g. multiple instances of a common parameterized +module -- in a single file, which was not previously an option for +examples like the earlier +[**Parameterized Modules**](../SimonSpeck/SimonSpeck.md) lab. + +# Symmetric Block Ciphers and Modes of Operation + +Following along from [1], a *symmetric block cipher* comprises a pair +of algorithms: + * *encryption* (a "forward cipher operation") and + * *decryption* (a "reverse cipher operation") + +Each accepts... + * a **key** (of arbitrary type; usually a bit string) + * a **block** (a bit string) + +...and produces the corresponding ciphertext or plaintext. + +A **block cipher mode of operation** applies an underlying symmetric +block cipher over some number of blocks (or sub-blocks). + +# Interfaces + +Using prior techniques for parameterized modules, we might start each +mode's (sub)module with a parameter block (commented out here because +that's not how we'll finish...): + +(Here, we use the terms `encipher` and `decipher` to distinguish block +cipher operations from those for modes, in keeping with [1].) -Before proceeding, recall a prior lab describing -[parameterized modules](../SimonSpeck/SimonSpeck.md) -for the SIMON and Speck algorithms. That lab required us to split -each parameter setting into its own module: +```cryptol +/* +parameter + /** arbitrary key type */ + type K : * + + /** block size (in bits) */ + type b : # + + /** forward cipher operator */ + encipher : K -> [b] -> [b] + + /** reverse cipher operator */ + decipher : K -> [b] -> [b] +*/ +``` -> But because this lab introduces further concepts around the -> definition and use of modules, this lab will not be entirely -> self-contained in this file. +But this approach already repeats itself, and we'd have to start each +(sub)module with the `parameter`s in this block (along with any others +unique to the mode). It would be better to reuse the common block +cipher parameters, and we can do so by defining an **interface** with +reusable **type aliases**: -Another more subtle limitation is that it is tricky to specify -multiple layers of parameterized modules, so specifications often -resort to repeating parameters. For SIMON, block size is repeated -for each corresponding key size and number of rounds, but this is -minor because block size is a simple integer. But for more complex -specifications like -[SHA-2](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Keyless/Hash/SHA2), -complex parameters like `K` (a sequence of words of bitlength 32 or -64) are repeated for different digest lengths at each word size, -potentially introducing easy-to-miss inconsistencies. +```cryptol +/** common parameters for symmetric block ciphers */ +interface submodule I_BlockCipher where + /** arbitrary key type */ + type Key : * + + /** block size (in bits) */ + type b : # + + /** type alias for a block, a bit string of width `b` */ + type Block = [b] + + /** common type for `encipher` and `decipher` */ + type Op = Key -> Block -> Block + + /** forward cipher operator */ + encipher : Op + + /** reverse cipher operator */ + decipher : Op +``` -# New Module System +# Functors -These limitations motivated customers to ask Galois to -[update the module system](https://github.com/GaloisInc/cryptol/issues/815) -to support layered specifications, cipher modes, common interfaces, -and other such complexities. The long-awaited update will soon be (or -have been) [merged](https://github.com/GaloisInc/cryptol/pull/1363) -and -[documented](https://github.com/GaloisInc/cryptol/blob/master/docs/RefMan/Modules.rst). +Each mode's (sub)module can then `import` this `interface` to serve the +same purpose as previously for `parameter` blocks: to indicate that the +module needs these parameters: -## `submodule`: Nested Modules +```cryptol +/* (Not quite ready to define these modes yet... -The new module system supports `submodule`s that can be nested into -other (`sub`)`module`s: +submodule ECB where + import interface submodule I_BlockCipher + // Now `type K`, `type b`, `encipher` and `decipher` are in scope + // But unfortunately, `type Block` and `type Op` are not... +*/ +``` + +This is an example of a +[**functor**](https://galoisinc.github.io/cryptol/master/Modules.html#importing-an-interface-module) +-- the modern, more versatile update to parameterized modules. Later, +we will see that such functors can accept multiple interfaces, and +define additional type constraints on their implementations. + +# Instantiation + +As noted above, interfaces do not (yet) export their type aliases, but +it would be nice for users to have access to these. We'll quickly do +this with a functor that imports the same interface and exports the +(copied) type aliases. This also demonstrates an **interface alias** +to distinguish the interface's aliases (which would otherwise conflict +despite not being accessible) from the functor's: ```cryptol - submodule Hash where - submodule XXHash where - TODO = undefined +submodule F_BlockCipher_Aliases where + // aliased interface import to avoid conflict + import interface submodule I_BlockCipher as I + + /** type alias for a block, a bit string of width `b` */ + type Block = [I::b] + + /** common type for `encipher` and `decipher` */ + type Op = I::Key -> Block -> Block +``` + +Then a `mode` can... + +```cryptol +/* + import interface submodule I_BlockCipher as I + import submodule F_BlockCipher_Aliases { interface I } (Block, Op) +*/ +``` + +...and reuse these aliases in its own definitions. + +Of course, parameterizable block cipher modes need block ciphers! We +recently implemeented the Simon and Speck block ciphers, which are +themselves parameterized. We just need to adapt these to our fancy +new interface... + +## Explicit Instantiation - submodule SHA where - TODO = undefined +We could do so explicitly... - submodule SHA2 where - TODO = undefined +```cryptol +/** Simon 32/64 implementation of `labs::SimonSpeck::Simon::Simon`'s parameters */ +submodule P_Simon_32_64' where + type n = 16 + type m = 4 + type T = 32 + type j = 0 + +/** Simon 32/64 instance */ +submodule Simon_32_64' = labs::SimonSpeck::Simon::Simon { submodule P_Simon_32_64' } + +/** explicit implementation of `submodule I_BlockCipher` with Simon 32/64 */ +submodule P_Simon_32_64_BlockCipher' where + import submodule Simon_32_64' as S + type Key = [S::keySize] + type b = S::blockSize + encipher = S::encrypt + decipher = S::decrypt ``` -This removes a previous limitation -- multiple parameterizations no -longer have to be specified in different modules. +This approach makes sense if we expect (us or our users) to refer to +these submodules later. + +`labs::SimonSpeck::Simon::Simon` can be instantiated because its `parameter` +block is treated by the new module system as an +[**anonymous interface**](https://galoisinc.github.io/cryptol/master/Modules.html#anonymous-interface-modules). -## Interfaces, Parameterization, Abstraction, and Instantiation +## Anonymous Instantiation -In the new module system, an _interface_ specifies _parameters_ that an -_abstract_ (sub)module can _instantiate_ to produce an _implementation_. -Let's see this in a simple contrived example: +If we don't intend to reuse these underlying implementations, we can also +implement our interface with an +[**anonymous instantiation**](https://galoisinc.github.io/cryptol/master/Modules.html#anonymous-instantiation-arguments): ```cryptol - submodule Contrivance where - interface submodule I_n where - type n : # - type constraint (2 >= n, n >= 1) +/** anonymous implementation of `submodule I_BlockCipher` with Simon 32/64 */ +submodule Simon_32_64_BlockCipher'' where + // ...by importing an anonymous instance of `labs::SimonSpeck::Simon::Simon` into a namespace `S` + import labs::SimonSpeck::Simon::Simon as S where + type n = 16 + type m = 4 + type T = 32 + type j = 0 + + type Key = [S::keySize] + type b = S::blockSize + encrypt = S::encrypt + decrypt = S::decrypt +``` - submodule A_n where - import interface submodule I_n +**Exercise**: Implement (define submodules that define types and values +for) `I_BlockCipher` for other Simon and Speck parameter sets, from +solutions (yours or ours) for +[`labs::SimonSpeck::SimonSpeck`](../SimonSpeck/SimonSpeck.md). Later, +we will define multiple block cipher modes for each of these block +ciphers and parameter sets... - type w = 32*n - type W = [w] +**Hint**: This will be less tedious if you define an `interface` and +functor with which to "adapt" prior solutions as implementations of +`I_BlockCipher`. Instances would look something like: - z : W - z = zero +`submodule P_Speck_64_128_BlockCipher = submodule F_Speck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_64_128 }` - submodule P_1 where - type n = 1 +Start with explicitly named submodules. These can always be +"anonymized" later if it is obvious they won't be reused. - submodule P_2 where - type n = 2 +**Solution**: - submodule Z_1 = submodule A_n { submodule P_1 } - submodule Z_2 = submodule A_n { submodule P_2 } +```cryptol +/** interface to collect relevant definitions from Simon instance */ +interface submodule I_Simon_Inst where + type keySize : # + type blockSize : # - import submodule Z_1 as Z32 - import submodule Z_2 as Z64 + type Key = [keySize] + type Block = [blockSize] + type Op = Key -> Block -> Block - property zero_32 = Z32::z == zero`{[32]} - property zero_64 = Z64::z == zero`{[64]} -``` - -The above snippet introduces a `submodule` `Contrivance`. Within, it -defines an `interface` with a numeric parameter `n` bound to the -interval `[1,2]`. An abstract submodule `A_n` requests a -parameterization satisfying `I_n`, and produces an implementation -defining `z`, a `zero` value of `type W = [w]`, where `type w = 32*n`. -Parameterizations `P_1` and `P_2` specify `n` values of `1` and `2`, -respectively, each satisfying `I_n`. Parameterizations `P_1` and `P_2` -instantiate `A_n` to define `Z_1` and `Z_2`, respectively, with `z` as -a corresponding `32`- or `64`-bit `zero`, as expressed by properties -`zero_32` and `zero_64`, respectively; each property requires the -instantiated submodules to be imported with aliases (here, `Z32` and -`Z64`) to distinguish their respective `z` definitions: + encrypt : Op + decrypt : Op -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :prove Contrivance::zero_32 -:prove Contrivance::zero_32 - Q.E.D. -(Total Elapsed Time: 0.035s, using "Z3") -labs::NewModuleSystem::NewModuleSystemAnswers> :prove Contrivance::zero_64 -:prove Contrivance::zero_64 - Q.E.D. -(Total Elapsed Time: 0.031s, using "Z3") -``` +// generalization of `Simon_32_64_BlockCipher` above +/** generate `I_BlockCipher` implementation from Simon instance */ +submodule F_SimonSpeck_BlockCipher where + import interface submodule I_Simon_Inst as S + + // export definitions for `I_BlockCipher` parameters + type Key = [S::keySize] + type b = S::blockSize + + encipher = S::encrypt + decipher = S::decrypt + +/** `I_BlockCipher` implementation for Simon 32/64 */ +submodule P_Simon_32_64_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_32_64 } + +/** `I_BlockCipher` implementation for Simon 48/72 */ +submodule P_Simon_48_72_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_48_72 } + +/** `I_BlockCipher` implementation for Simon 48/96 */ +submodule P_Simon_48_96_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_48_96 } + +/** `I_BlockCipher` implementation for Simon 64/96 */ +submodule P_Simon_64_96_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_64_96 } + +/** `I_BlockCipher` implementation for Simon 64/128 */ +submodule P_Simon_64_128_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_64_128 } + +/** `I_BlockCipher` implementation for Simon 96/96 */ +submodule P_Simon_96_96_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_96_96 } -All of the above were defined within the `Contrivance` submodule, -hence the leading `Contrivance::` in the property names. +/** `I_BlockCipher` implementation for Simon 96/144 */ +submodule P_Simon_96_144_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_96_144 } -## Scope +/** `I_BlockCipher` implementation for Simon 128/128 */ +submodule P_Simon_128_128_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_128_128 } + +/** `I_BlockCipher` implementation for Simon 128/192 */ +submodule P_Simon_128_192_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_128_192 } + +/** `I_BlockCipher` implementation for Simon 128/256 */ +submodule P_Simon_128_256_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_128_256 } + +/** `I_BlockCipher` implementation for Speck 32/64 */ +submodule P_Speck_32_64_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_32_64 } + +/** `I_BlockCipher` implementation for Speck 48/72 */ +submodule P_Speck_48_72_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_48_72 } + +/** `I_BlockCipher` implementation for Speck 48/96 */ +submodule P_Speck_48_96_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_48_96 } + +/** `I_BlockCipher` implementation for Speck 64/96 */ +submodule P_Speck_64_96_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_64_96 } + +/** `I_BlockCipher` implementation for Speck 64/128 */ +submodule P_Speck_64_128_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_64_128 } + +/** `I_BlockCipher` implementation for Speck 96/96 */ +submodule P_Speck_96_96_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_96_96 } + +/** `I_BlockCipher` implementation for Speck 96/144 */ +submodule P_Speck_96_144_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_96_144 } + +/** `I_BlockCipher` implementation for Speck 128/128 */ +submodule P_Speck_128_128_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_128_128 } + +/** `I_BlockCipher` implementation for Speck 128/192 */ +submodule P_Speck_128_192_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_128_192 } + +/** `I_BlockCipher` implementation for Speck 128/256 */ +submodule P_Speck_128_256_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_128_256 } +``` -Only the top-level definitions `zero_32` and `zero_64` are in scope -after loading the top-level module from this point. Nested submodules -are not exported; to make `z` from each submodule available as well, -they must be reassigned explicitly: +## Adapting other block ciphers + +In the last exercise, you adapted your own prior work to fit out new +`I_BlockCipher` interface. We can also adapt other simple block +ciphers, such as DES in the +[Cryptographic Proofs](../CryptoProofs/CryptoProofsAnswers.md) lab. ```cryptol - z32 = Z32::z - z64 = Z64::z +import specs::Primitive::Symmetric::Cipher::Block::DES (DES) ``` -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :h Contrivance +**Exercise**: Adapt `DES` with a submodule that provides all parameters +required by `interface submodule I_BlockCipher`: -Module Contrivance exports: +**Solution**: + +```cryptol +submodule P_DES_BlockCipher where + type Key = [64] + type b = 64 - - zero_32 : Bit - zero_64 : Bit - z32 : W - z64 : W -labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::z32 -0x00000000 -labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::z64 -0x0000000000000000 -labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::zero_32 -True -labs::NewModuleSystem::NewModuleSystemAnswers> Contrivance::zero_64 -True -``` - -# Practicum - -Having introduced the new module system, let's apply it to SIMON and -compare the former and new module systems. - -## Simon says what? - -Recall that SIMON is parameterized on four constrained numeric (`#`) -`type`s: - * `n` (word size) - * `m` (words per key) - * `T` (rounds in key schedule) - * `j` (index of z-sequence) - -**EXERCISE**: Define an `interface submodule` comprising these types -and their constraints. -(**Hint**: Just copy them from the previous lab.) + encipher = DES.encrypt + decipher = DES.decrypt +``` + +# Block Cipher Modes of Operation: ECB, CBC, ... + +Having implemented various block ciphers, we can now apply them toward +**block cipher modes of operation** such as: + - [Electronic Codebook (ECB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) + - [Cipher Block Chaining (CBC)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_block_chaining_(CBC)) + - [Cipher Feedback (CFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_(CFB)) + - ... + +Each cipher mode repeatedly applies a block cipher to multiple blocks +in a message -- often with an **initialization vector** and +intermediate transformations, and possibly after **padding** the +message to evenly divide it into blocks -- to produce a ciphertext +message with the same number of blocks. (Authenticated encryption +generates a **signature** alongside the ciphertext.) + +## Electronic Codebook (ECB) + +ECB is a simple block cipher mode that just enciphers each block, with +no intermediate transformations to **diffuse** the message, a weakness +that disqualifies it for use in security-critical settings. But just +as a pedagogical exercise, we can define ECB in Cryptol... + +**Exercise**: In the specification of ECB mode below, define `decrypt` +so that using the same shared key on an encrypted ciphertext message +produces the original plaintext. Do not include an argument for the +message in your definition. + +**Solution**: ```cryptol - submodule SIMON where - /** interface specifying parameters for SIMON block cipher */ - interface submodule I_SIMON where - /** word size */ - type n : # - type constraint (16 <= n, n <= 64) - - /** number of words in key */ - type m : # - type constraint (2 <= m, m <= 4) - - /** number of rounds in key schedule */ - type T : # - type constraint (32 <= T, T <= 72) - - /** index of z-sequence used (i.e. use sequence z_j) */ - type j : # - type constraint (0 <= j, j <= 4) -``` - -Recall that, given these parameters, SIMON exports: - * derived `type`s: - * `type blockSize = 2 * n` - * `type keySize = m * n` - * block cipher functions: - * `encrypt : [keySize] -> [blockSize] -> [blockSize]` - * `decrypt : [keySize] -> [blockSize] -> [blockSize]` - * properties: - * `EncryptDecryptIdentity : [keySize] -> [blockSize] -> Bool` - * `DecryptEncryptIdentity : [keySize] -> [blockSize] -> Bool` - -These exported definitions rely on numerous `private` ones. - -Eventually, we will need to (re)implement `encrypt` and `decrypt` -functions in an abstract submodule that imports `I_SIMON`. Testing the -implementations will require parameterizations satisfying `I_n` to -instantiate the abstract submodule. So let's start there: - -**EXERCISE**: Define submodules with parameters satisfying `I_n` for -each approved `n` (word size) and `m` (words per key), or just a few -if you're so inclined. (`T` and `j` depend on `n` and `m`.) -(**Hint**: For each `simon_2n_mn.cry`, copy its `parameter` block body -into a `submodule P_SIMON_2n_mn` (or `P_SIMON_n_m` if you prefer). +/** Disclaimer: Weak block cipher mode; do not use in production. */ +submodule ECB where + // request a block cipher + import interface submodule I_BlockCipher + + // derive the type aliases for that block cipher + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } (Block) + + /** Encrypt a multi-block message using ECB mode */ + encrypt : {n} Key -> [n]Block -> [n]Block + encrypt key = map (encipher key) + + /** Decrypt a multi-block message using ECB mode */ + decrypt : {n} Key -> [n]Block -> [n]Block + decrypt key = map (decipher key) // Replaced with our definition. +``` + +Again, please do not ever use this weak cipher mode in the real world. + +**Exercise**: Define submodules for each Simon and Speck parameter set +in ECB mode. + +**Solution**: ```cryptol - submodule P_SIMON_32_64 where - type n = 16 - type m = 4 - type T = 32 - type j = 0 - - submodule P_SIMON_48_72 where - type n = 24 - type m = 3 - type T = 36 - type j = 0 - - submodule P_SIMON_48_96 where - type n = 24 - type m = 4 - type T = 36 - type j = 1 - - submodule P_SIMON_64_96 where - type n = 32 - type m = 3 - type T = 42 - type j = 2 - - submodule P_SIMON_64_128 where - type n = 32 - type m = 4 - type T = 44 - type j = 3 - - submodule P_SIMON_96_96 where - type n = 48 - type m = 2 - type T = 52 - type j = 2 - - submodule P_SIMON_96_144 where - type n = 48 - type m = 3 - type T = 54 - type j = 3 - - submodule P_SIMON_128_128 where - type n = 64 - type m = 2 - type T = 68 - type j = 2 - - submodule P_SIMON_128_192 where - type n = 64 - type m = 3 - type T = 69 - type j = 3 - - submodule P_SIMON_128_256 where - type n = 64 - type m = 4 - type T = 72 - type j = 4 -``` - -**EXERCISE**: Define an abstract `submodule` `A_SIMON` that requests a -parameterization satisfying `I_SIMON` and exports an implementation -with the above definitions, and instantiate this with each of the -above parameterizations into concrete submodules. `:check` your work. -(**Hint**: Just copy the previous definitions from `Simon.cry` from -the previous lab, and instatiate them using lines of the form -`submodule SIMON_2n_mn = submodule A_SIMON { submodule P_SIMON_2n_mn }` (or -likewise if you used another naming scheme earlier). +/** Simon 32/64 in ECB mode */ +submodule ECB_Simon_32_64 = submodule ECB { submodule P_Simon_32_64_BlockCipher } + +/** Simon 48/72 in ECB mode */ +submodule ECB_Simon_48_72 = submodule ECB { submodule P_Simon_48_72_BlockCipher } + +/** Simon 48/96 in ECB mode */ +submodule ECB_Simon_48_96 = submodule ECB { submodule P_Simon_48_96_BlockCipher } + +/** Simon 64/96 in ECB mode */ +submodule ECB_Simon_64_96 = submodule ECB { submodule P_Simon_64_96_BlockCipher } + +/** Simon 64/128 in ECB mode */ +submodule ECB_Simon_64_128 = submodule ECB { submodule P_Simon_64_128_BlockCipher } + +/** Simon 96/96 in ECB mode */ +submodule ECB_Simon_96_96 = submodule ECB { submodule P_Simon_96_96_BlockCipher } + +/** Simon 96/144 in ECB mode */ +submodule ECB_Simon_96_144 = submodule ECB { submodule P_Simon_96_144_BlockCipher } + +/** Simon 128/128 in ECB mode */ +submodule ECB_Simon_128_128 = submodule ECB { submodule P_Simon_128_128_BlockCipher } + +/** Simon 128/192 in ECB mode */ +submodule ECB_Simon_128_192 = submodule ECB { submodule P_Simon_128_192_BlockCipher } + +/** Simon 128/256 in ECB mode */ +submodule ECB_Simon_128_256 = submodule ECB { submodule P_Simon_128_256_BlockCipher } + +/** Speck 32/64 in ECB mode */ +submodule ECB_Speck_32_64 = submodule ECB { submodule P_Speck_32_64_BlockCipher } + +/** Speck 48/72 in ECB mode */ +submodule ECB_Speck_48_72 = submodule ECB { submodule P_Speck_48_72_BlockCipher } + +/** Speck 48/96 in ECB mode */ +submodule ECB_Speck_48_96 = submodule ECB { submodule P_Speck_48_96_BlockCipher } + +/** Speck 64/96 in ECB mode */ +submodule ECB_Speck_64_96 = submodule ECB { submodule P_Speck_64_96_BlockCipher } + +/** Speck 64/128 in ECB mode */ +submodule ECB_Speck_64_128 = submodule ECB { submodule P_Speck_64_128_BlockCipher } + +/** Speck 96/96 in ECB mode */ +submodule ECB_Speck_96_96 = submodule ECB { submodule P_Speck_96_96_BlockCipher } + +/** Speck 96/144 in ECB mode */ +submodule ECB_Speck_96_144 = submodule ECB { submodule P_Speck_96_144_BlockCipher } + +/** Speck 128/128 in ECB mode */ +submodule ECB_Speck_128_128 = submodule ECB { submodule P_Speck_128_128_BlockCipher } + +/** Speck 128/192 in ECB mode */ +submodule ECB_Speck_128_192 = submodule ECB { submodule P_Speck_128_192_BlockCipher } + +/** Speck 128/256 in ECB mode */ +submodule ECB_Speck_128_256 = submodule ECB { submodule P_Speck_128_256_BlockCipher } +``` + +**Exercise**: Likewise, define a submodule that implements DES (a weak +block cipher) in ECB (a weak cipher mode). Add a docstring with a +prominent warning not to use it in production. + +**Solution**: ```cryptol - /** - * abstraction that produces a SIMON block cipher implementation - * from parameters satisfying the `I_SIMON` interface - */ - submodule A_SIMON where - import interface submodule I_SIMON - - type blockSize = 2 * n - type keySize = m * n - - /** - * The SIMON block cipher, encryption direction. See [1] for further - * details. - * - * [1] Beaulieu R., Shors D., et. al. "The Simon and Speck Families - * of Lightweight Block Ciphers". e-print-2013-404.pdf. - */ - encrypt : [keySize] -> [blockSize] -> [blockSize] - encrypt K P = join (last (encryptList K P)) - - /** - * The SIMON block cipher, decryption direction. See [1] for further - * details. - * - * [1] Beaulieu R., Shors D., et. al. "The Simon and Speck Families - * of Lightweight Block Ciphers". e-print-2013-404.pdf. - */ - decrypt : [keySize] -> [blockSize] -> [blockSize] - decrypt K P = join (last (decryptList K P)) - - /** - * EncryptDecryptRoundIdentity k x = Rk' k (Rk k x) == x - */ - property EncryptDecryptRoundIdentity k x = - Rk' k (Rk k x) == x - - /** - * DecryptEncryptRoundIdentity k x = Rk k (Rk' k x) == x - */ - property DecryptEncryptRoundIdentity k x = - Rk k (Rk' k x) == x - - // These properties take too long to verify with Z3 - // Try proving with abc - // :s prover=abc or - // :s prover=any - property EncryptDecryptIdentity k x = - decrypt k (encrypt k x) == x +/** DEPRECATED!!! JUST...NO!!!!! */ +submodule ECB_DES = submodule ECB { submodule P_DES_BlockCipher } +``` + +Your solution should pass the following test vectors [3]: + +```cryptol +submodule ECB_DES_Test where + import submodule ECB_DES as ECB_DES + + e (key, pt, ct) = ECB_DES::encrypt key pt == ct + d (key, pt, ct) = ECB_DES::decrypt key ct == pt + + // from FIPS 81 (withdrawn, of course) + v_fips_81 = (0x0123456789abcdef, [0x4e6f772069732074, 0x68652074696d6520, 0x666f7220616c6c20], [0x3fa40e8a984d4815, 0x6a271787ab8883f9, 0x893d51ec4b563b53]) + property e_fips_81 = e v_fips_81 + property d_fips_81 = d v_fips_81 - property DecryptEncryptIdentity k x = - encrypt k (decrypt k x) == x - - private - - // We define the sequences u, v, w, and z_j following the paper - u = join(repeat`{inf} 0b1111101000100101011000011100110) - v = join(repeat`{inf} 0b1000111011111001001100001011010) - w = join(repeat`{inf} 0b1000010010110011111000110111010) - - z0 = u - z1 = v - z2 = join([ dibit ^ 0b01 | dibit <- groupBy`{2} u ]) - z3 = join([ dibit ^ 0b01 | dibit <- groupBy`{2} v ]) - z4 = join([ dibit ^ 0b01 | dibit <- groupBy`{2} w ]) - - // Z will be the sequence selected by the module parameter j - Z = [z0, z1, z2, z3, z4]@(`j:[width j]) - - // Round Function and auxiliary functions - - /** - * f is used in the round function and appears on page 8 of [1] - */ - f x = (x <<< 1) && (x <<< 8) ^ (x <<< 2) - - /** - * The SIMON round function - */ - Rk : [n] -> [2][n] -> [2][n] - Rk k [x, y] = [x', y'] - where - x' = y ^ (f x) ^ k - y' = x - - /** - * The SIMON inverse round function - */ - Rk' : [n] -> [2][n] -> [2][n] - Rk' k [x, y] = [x', y'] - where - x' = y - y' = x ^ (f y) ^ k - - /** - * "tmp" appears as the name of a variable in the SIMON key - * expansion sample implementation on page 13. The tmp function - * below captures the possible final values the expression has, - * taking into account the type parameter `m` containing the - * number of key words. - */ - tmp: [n] -> [n] -> [n] - tmp k1 k2 = r - where - r = t ^ (t >>> 1) - t = if `m == 0x4 then (t' ^ k2) else t' - t' = (k1 >>> 3) - - /** - * The SIMON Key Expansion function. - */ - KeyExpansion : [keySize] -> [T][n] - KeyExpansion K = take Ks - where - Kis : [m][n] - Kis = reverse (split K) - Ks : [inf][n] - Ks = Kis - # [ ~k0 ^ (tmp k1 k2) ^ (zext [z]) ^ (zext 0x3) - | k0 <- Ks - | k1 <- drop`{m-1} Ks - | k2 <- drop`{max m 3 - 3} Ks // gadget to typecheck "drop`{m-3}" - | z <- Z ] - - /** - * SIMON decryption with intermediate values for each round - * produced as a list. Some test vectors are available to compare - * these intermediate values. - */ - decryptList: [keySize] -> [blockSize] -> [T+1][2][n] - decryptList K C = Ps - where - Cs = split C - Ks = reverse (KeyExpansion K) - Ps = [ Cs ] # [ Rk' k xy | k <- Ks | xy <- Ps] - - /** - * SIMON encryption with intermediate values for each round - * produced as a list. Some test vectors are available to compare - * these intermediate values. - */ - encryptList: [keySize] -> [blockSize] -> [T+1][2][n] - encryptList K P = Cs - where - Ps = split P - Ks = KeyExpansion K - Cs = [ Ps ] # [ Rk k xy | k <- Ks | xy <- Cs] - - submodule SIMON_32_64 = submodule A_SIMON { submodule P_SIMON_32_64 } - submodule SIMON_48_72 = submodule A_SIMON { submodule P_SIMON_48_72 } - submodule SIMON_48_96 = submodule A_SIMON { submodule P_SIMON_48_96 } - submodule SIMON_64_96 = submodule A_SIMON { submodule P_SIMON_64_96 } - submodule SIMON_64_128 = submodule A_SIMON { submodule P_SIMON_64_128 } - submodule SIMON_96_96 = submodule A_SIMON { submodule P_SIMON_96_96 } - submodule SIMON_96_144 = submodule A_SIMON { submodule P_SIMON_96_144 } - submodule SIMON_128_128 = submodule A_SIMON { submodule P_SIMON_128_128 } - submodule SIMON_128_192 = submodule A_SIMON { submodule P_SIMON_128_192 } - submodule SIMON_128_256 = submodule A_SIMON { submodule P_SIMON_128_256 } + // another test vector based on `https://opensource.apple.com/source/OpenSSL/OpenSSL-23/openssl/test/evptests.txt.auto.html` + v35 = (0x1111111111111111, [0x1111111111111111, 0x0123456789ABCDEF], [0xF40379AB9E0EC533, 0x8A5AE1F81AB8F2DD]) + property e35 = e v35 + property d35 = d v35 ``` -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check -property Contrivance::zero_32 Using exhaustive testing. +```xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check ECB_DES_Test::e_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check ECB_DES_Test::d_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust ECB_DES_Test::e35 +Using exhaustive testing. Passed 1 tests. Q.E.D. -property Contrivance::zero_64 Using exhaustive testing. +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust ECB_DES_Test::d35 +Using exhaustive testing. Passed 1 tests. Q.E.D. -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^48 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::EncryptDecryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^48 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^72 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::DecryptEncryptRoundIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^120 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^160 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^240 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^256 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^320 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::EncryptDecryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^384 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_32_64::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^96 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_72::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^120 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_48_96::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^144 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_96::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^160 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_64_128::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_96::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_96_144::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^240 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_128::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^256 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_192::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^320 values) -property labs::NewModuleSystem::NewModuleSystemAnswers::SIMON::SIMON_128_256::DecryptEncryptIdentity Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^384 values) -``` - -## Simon asks why? - -Interface, parameterizations, abstractions, and instantiations -- all -to reimplement what parameterized modules are already able to do with -`parameter` blocks and parameterized `import`s. Sure, the new system -allows us to define the whole family of SIMON ciphers, alongside our -earlier contrivance no less, but is this worth all the trouble? If the -answer is immediately obvious at this point, feel free to skip the -rest of this lab, secure in your understanding of nested modules, -interfaces, and the opportunities and conundrums they bring. -Otherwise, let's show off a concept that was difficult to pull off -with the old system: cipher modes. - -Recall that our SIMON spec defined `encrypt` and `decrypt` functions -over `key` and `block` sizes. Furthermore, it defined a property that -`decrypt` inverts `encrypt`. These are all rather common, and having -to define the inversion property for AES, Blowfish, RC5, and any other -[block cipher](https://en.wikipedia.org/wiki/Block_cipher) -that comes along gets very tedious very fast. - -Now suppose we wish to run each of these ciphers in various -[modes](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation): -[Electronic Codebook (ECB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#ECB) -(only to demonstrate why that's a really bad idea), -[Cipher Block Chaining (CBC)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC), -[Cipher Feedback (CFB)](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CFB), -... If we had to define each mode, for each block cipher, again, this -would make Cryptol authors wish gone into yodeling or sports -broadcasting. So until this point, except in a few cases where -[an effort was made](https://github.com/GaloisInc/cryptol-specs/tree/master/Primitive/Symmetric/Cipher/Block/Modes), -this has mostly been avoided. Nested modules, for all their verbosity, -offer another approach: - * Define a block cipher interface - * Define abstract cipher modes that request block cipher parameters - * Instantiate cipher modes with parameterized block ciphers - -# Block Ciphers - -Working from -[NIST SP 800-38A](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf), -a _block cipher_ is a function that maps `b`-bit plaintext blocks -to `b`-bit ciphertext blocks, parameterized by a key `K` of arbitrary -type. NIST SP 800-38A refers to block encryption and decryption as -forward and inverse ciphers to disambiguate them from corresponding -mode operations. Let's rename these to `encipher` and `decipher`, -respectively, in keeping with Cryptol naming conventions. +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust ECB_DES_Test::e53 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust ECB_DES_Test::d53 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` -```cryptol - import labs::Transposition::CommonProperties (inverts) +## Cipher Block Chaining (CBC) + +To better diffuse message blocks, CBC introduces an *initialization +vector* and applies the binary exclusive-or operator (`^` in Cryptol) +to each block of plaintext with the previous ciphertext (the +initialization vector for the first block) before applying the block +cipher with a given key. That is: - interface submodule I_BlockCipher where - type k : * - type b : # - type constraint (fin b) +`c0 = encipher key (p0 ^ iv)` +`c1 = encipher key (p1 ^ c0)` +`c2 = encipher key (p2 ^ c1)` +`...` +`cn = encipher key (pn ^ ...)` - encipher : k -> [b] -> [b] - decipher : k -> [b] -> [b] +**Exercise**: Define a functor to generate a CBC mode for a given block +cipher. - submodule A_BlockCipher_Props where - import interface submodule I_BlockCipher +**Solution**: - decipher_inverts_encipher : k -> [b] -> Bool - property decipher_inverts_encipher K = - inverts (decipher K) (encipher K) +```cryptol +submodule CBC where + // request a block cipher + import interface submodule I_BlockCipher + + // derive the type aliases for that block cipher + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } (Block) + + /** Encrypt a multi-block message using CBC mode */ + encrypt : {n} Key -> Block -> [n]Block -> [n]Block + encrypt key iv pt = ct // Replaced with our solution. + where + ct@i = encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1))) + + /** Decrypt a multi-block message using CBC mode */ + decrypt : {n} Key -> Block -> [n]Block -> [n]Block + decrypt key iv ct = pt // Replaced with our solution. + where + pt@i = decipher key (ct@i) ^ (if i == 0 then iv else ct@(i-1)) ``` -Common block cipher _modes of operation_ defined in NIST 800-38A -include: - * Electronic Codebook (ECB) - * Cipher Block Chaining (CBC) - * Cipher Feedback (CFB) - * Output Feedback (OFB) +**Hint**: The above definition can be directly translated to a Cryptol +[generating function](https://galoisinc.github.io/cryptol/master/BasicTypes.html#sequences). +(See `generate` and the examples following it.) So for `encrypt`, you +can say `encrypt key iv pt = ct where ct@i = ...` and then fill in the +rest. Remember to account for the initialization vector and the first +ciphertext block `ct@0`. + +**Note**: Though we suggest generator expressions here, it is also +possible to use various higher-order functions such as `foldl` and +`zipWith`. For now, this is mostly a matter of style. However, when +it comes time to verify that our submodules meet important properties +(most notably, that using the same key and initialization vector +to decrypt ciphertext recovers the original plaintext), this choice +will greatly affect future verification efforts... + +**Hint**: We have not specified above how to derive plaintext blocks +from ciphertext blocks. If you get stuck defining `decrypt`, using the +above equalities, you can work backward to solve for each plaintext +block. You will need simple properties about `(^)` and a very +important property of (useful) block ciphers, defined as +`block_recovery` below. + +```cryptol +// property block_recovery key pt = decipher key (encipher key pt) == pt +// property xor_inv_r x y = (x ^ y) ^ y == x +// property xor_inv_l x y = x ^ (x ^ y) == y +``` + +Working backward from our generator expression for `encrypt`... + +`ct@i = encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1)))` +(Apply `decipher key` to both sides.) +`decipher key (ct@i) = decipher key (encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1))))` +(Apply `block_recovery` on right.) +`decipher key (ct@i) = pt@i ^ ct'` +(Apply `(^) ct'` to both sides, where `ct'` is the `if`-expression.) +`decipher key (ct@i) ^ ct' = pt@i ^ ct' ^ ct' +(Apply `xor_inv_r` on right.) +`decipher key (ct@i) ^ ct' = pt@i` +(Flip sides for assignment to plaintext.) +`pt@i = decipher key (ct@i) ^ ct'` +(Substitute the `if`-expression back in for `ct'`.) +`pt@i = decipher key (ct@i) ^ (if i == 0 then iv else ct@(i-1)))` + +**Note**: Could you define `decrypt` without an initialization vector? -Let's start with the easy ones. +As with `ECB`, we can now define a `CBC` mode for various Simon and +Speck configurations, or any other simple block cipher that implements +our interface. + +**Exercise**: Define submodules for each Simon and Speck parameter set +in CBC mode. + +**Solution**: ```cryptol - submodule A_ECB where - import interface submodule I_BlockCipher +/** Simon 32/64 in CBC mode */ +submodule CBC_Simon_32_64 = submodule CBC { submodule P_Simon_32_64_BlockCipher } + +/** Simon 48/72 in CBC mode */ +submodule CBC_Simon_48_72 = submodule CBC { submodule P_Simon_48_72_BlockCipher } + +/** Simon 48/96 in CBC mode */ +submodule CBC_Simon_48_96 = submodule CBC { submodule P_Simon_48_96_BlockCipher } + +/** Simon 64/96 in CBC mode */ +submodule CBC_Simon_64_96 = submodule CBC { submodule P_Simon_64_96_BlockCipher } + +/** Simon 64/128 in CBC mode */ +submodule CBC_Simon_64_128 = submodule CBC { submodule P_Simon_64_128_BlockCipher } + +/** Simon 96/96 in CBC mode */ +submodule CBC_Simon_96_96 = submodule CBC { submodule P_Simon_96_96_BlockCipher } + +/** Simon 96/144 in CBC mode */ +submodule CBC_Simon_96_144 = submodule CBC { submodule P_Simon_96_144_BlockCipher } + +/** Simon 128/128 in CBC mode */ +submodule CBC_Simon_128_128 = submodule CBC { submodule P_Simon_128_128_BlockCipher } + +/** Simon 128/192 in CBC mode */ +submodule CBC_Simon_128_192 = submodule CBC { submodule P_Simon_128_192_BlockCipher } + +/** Simon 128/256 in CBC mode */ +submodule CBC_Simon_128_256 = submodule CBC { submodule P_Simon_128_256_BlockCipher } + +/** Speck 32/64 in CBC mode */ +submodule CBC_Speck_32_64 = submodule CBC { submodule P_Speck_32_64_BlockCipher } + +/** Speck 48/72 in CBC mode */ +submodule CBC_Speck_48_72 = submodule CBC { submodule P_Speck_48_72_BlockCipher } - encrypt : {t} k -> [t][b] -> [t][b] - encrypt K P = map (encipher K) P +/** Speck 48/96 in CBC mode */ +submodule CBC_Speck_48_96 = submodule CBC { submodule P_Speck_48_96_BlockCipher } - decrypt : {t} k -> [t][b] -> [t][b] - decrypt K C = map (decipher K) C +/** Speck 64/96 in CBC mode */ +submodule CBC_Speck_64_96 = submodule CBC { submodule P_Speck_64_96_BlockCipher } - decrypt_inverts_encrypt : {t} (fin t) => k -> [t][b] -> Bool - decrypt_inverts_encrypt K = - inverts (decrypt K) (encrypt K) +/** Speck 64/128 in CBC mode */ +submodule CBC_Speck_64_128 = submodule CBC { submodule P_Speck_64_128_BlockCipher } - submodule A_CBC where - import interface submodule I_BlockCipher +/** Speck 96/96 in CBC mode */ +submodule CBC_Speck_96_96 = submodule CBC { submodule P_Speck_96_96_BlockCipher } - encrypt : {t} k -> [b] -> [t][b] -> [t][b] - encrypt K IV P = C - where - C = [ encipher K (P_j ^ C_j') | P_j <- P | C_j' <- [IV] # C ] +/** Speck 96/144 in CBC mode */ +submodule CBC_Speck_96_144 = submodule CBC { submodule P_Speck_96_144_BlockCipher } - decrypt : {t} k -> [b] -> [t][b] -> [t][b] - decrypt K IV C = P - where - P = [ (decipher K C_j) ^ C_j' | C_j <- C | C_j' <- [IV] # C ] +/** Speck 128/128 in CBC mode */ +submodule CBC_Speck_128_128 = submodule CBC { submodule P_Speck_128_128_BlockCipher } - decrypt_inverts_encrypt : {t} (fin t) => k -> [b] -> [t][b] -> Bool - decrypt_inverts_encrypt K IV = - inverts (decrypt K IV) (encrypt K IV) +/** Speck 128/192 in CBC mode */ +submodule CBC_Speck_128_192 = submodule CBC { submodule P_Speck_128_192_BlockCipher } + +/** Speck 128/256 in CBC mode */ +submodule CBC_Speck_128_256 = submodule CBC { submodule P_Speck_128_256_BlockCipher } ``` -Unfortunately, because different cipher modes might take different -parameters (e.g. CBC requires an _initialization vector_ `iv`), we -cannot easily define an abstract (sub)module for cipher modes. +**Exercise**: Likewise, define a submodule that implements DES (a weak +block cipher) in CBC (a cipher mode). Add a docstring with a prominent +warning not to use it in production. -**EXERCISE**: Define abstract submodules for CFB and OFB modes. +**Solution**: ```cryptol - submodule A_CFB where - TODO = undefined - - submodule A_OFB where - TODO = undefined +/** STILL DEPRECATED!!! */ +submodule CBC_DES = submodule CBC { submodule P_DES_BlockCipher } ``` -As tempting as it is to ask students to go reimplement AES, DES, etc., -let's just adapt an existing cipher to the `I_BlockCipher` interface -expected by our abstract cipher mode submodules. (This part requires -[`cryptol-specs`](https://github.com/GaloisInc/cryptol-specs) to be -cloned or downloaded and in `CRYPTOLPATH`.) +Your solution should pass the following test vectors [3]: ```cryptol - import module Primitive::Symmetric::Cipher::Block::DES (DES) +submodule CBC_DES_Test where + import submodule CBC_DES as CBC_DES + + e (key, iv, pt, ct) = CBC_DES::encrypt key iv pt == ct + d (key, iv, pt, ct) = CBC_DES::decrypt key iv ct == pt + + // from FIPS 81 (withdrawn, of course) + v_fips_81 = (0x0123456789abcdef, 0x1234567890abcdef, [0x4e6f772069732074, 0x68652074696d6520, 0x666f7220616c6c20], [0xe5c7cdde872bf27c, 0x43e934008c389c0f, 0x683788499a7c05f6]) - submodule P_DES where - type k = [64] - type b = 64 - - encipher = DES.encrypt - decipher = DES.decrypt + property e_fips_81 = e v_fips_81 + property d_fips_81 = d v_fips_81 +``` + +```xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check CBC_DES_Test::e_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check CBC_DES_Test::d_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + +## Cipher Feedback (CFB) + +CFB can take any of various forms, including a full-block mode or one +that segments block into *subblocks*. In the full-block configuration, +CFB is similar to CBC except that the XOR and cipher operations are +performed in the other order, i.e.: - submodule S_ECB_DES = submodule A_ECB { submodule P_DES } - import submodule S_ECB_DES as S_ECB_DES +`c0 = (encipher key iv) ^ pt0` +`c1 = (encipher key c0) ^ pt1` +`c2 = (encipher key c1) ^ pt2` +`...` - submodule S_CBC_DES = submodule A_CBC { submodule P_DES } - import submodule S_CBC_DES as S_CBC_DES +**Exercise**: Define CFB full-block mode for a given block cipher. + +**Solution**: + +```cryptol +submodule CFB where + // request a block cipher + import interface submodule I_BlockCipher + + // derive the type aliases for that block cipher + import submodule F_BlockCipher_Aliases { I = interface I_BlockCipher } (Block) + + /** Encrypt a multi-block message using CFB mode */ + encrypt : {n} fin n => Key -> Block -> [n]Block -> [n]Block + encrypt key iv pt = ct // Replaced with our definition. + where + ct@i = (encipher key (if i == 0 then iv else ct@(i-1))) ^ pt@i + + /** Decrypt a multi-block message using CFB mode */ + decrypt : {n} fin n => Key -> Block -> [n]Block -> [n]Block + decrypt key iv ct = pt // Replaced with our definition. + where + pt@i = (encipher key (if i == 0 then iv else ct@(i-1))) ^ ct@i + + /* **Note**: These are the same `step` functions, just with + * different parameter names... + */ ``` -Why is ECB bad again? Let's check... +**Hint**: It is not necessary for a functor to use all the fields +in its interface(s). Asking for unused fields is perhaps questionable, +but being able to reuse the same interface might turn out to justify +an ignored field... + +**Hint**: The different order of operations has a profound impact on +CFB `decrypt` that might seem counterintuitive at first. Again, if +you get stuck defining `decrypt`, work out from the above definition +how to solve for plaintext blocks in terms of ciphertext. That is, +given `ct_i = (encipher key iv) ^ pt_i`, solve for `pt_i`. + +**Exercise**: Define a submodule that implements DES (a weak block +cipher) in full-block CFB mode (a cipher mode). Add a docstring with +a prominent warning not to use it in production. + +**Solution**: ```cryptol - ECB_DES_bad : {n} (fin n) => P_DES::k -> [P_DES::b] -> Bool - ECB_DES_bad K block = - S_ECB_DES::encrypt K (repeat`{n} block) == repeat (P_DES::encipher K block) +/** NO!!! NO, NO, NO!!! */ +submodule CFB_DES = submodule CFB { submodule P_DES_BlockCipher } +``` - CBC_DES_less_bad : {n} (fin n, n >= 2) => P_DES::k -> [P_DES::b] -> [P_DES::b] -> Bool - CBC_DES_less_bad K IV block = - S_CBC_DES::encrypt K IV (repeat`{n} block) != repeat (P_DES::encipher K block) +Your solution should pass the following test vectors [3]: + +```cryptol +submodule CFB_DES_Test where + import submodule CFB_DES as CFB_DES + + e (key, iv, pt, ct) = CFB_DES::encrypt key iv pt == ct + d (key, iv, pt, ct) = CFB_DES::decrypt key iv ct == pt + + // from FIPS 81 (withdrawn, of course) + v_fips_81 = (0x0123456789abcdef, 0x1234567890abcdef, [0x4e6f772069732074, 0x68652074696d6520, 0x666f7220616c6c20], [0xf3096249c7f46e51, 0xa69e839b1a92f784, 0x03467133898ea622]) + + property e_fips_81 = e v_fips_81 + property d_fips_81 = d v_fips_81 ``` -```Xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :prove ECB_DES_bad`{4} +```xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check CFB_DES_Test::e_fips_81 +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check CFB_DES_Test::d_fips_81 +Using exhaustive testing. +Passed 1 tests. Q.E.D. -(Total Elapsed Time: 1.893s, using "Yices") -labs::NewModuleSystem::NewModuleSystemAnswers> :check CBC_DES_less_bad`{4} -Using random testing. -Passed 100 tests. -Expected test coverage: 0.00% (100 of 2^^192 values) ``` -So ECB is bad unless you never repeat yourself or say anything an -attacker would consider... +## Counter (CTR) Mode (Multiple Interfaces and Interface Constraints) + +Counter mode produces a stream cipher by applying a block cipher to +successive values of a "counter" that produces a long sequence of +non-repeating values -- most commonly a simple incremental counter. + +This counter is concatenated with a *nonce*, which acts much as the +initialization vector from other modes into blocks, which are +enciphered with the key and then XOR'ed (or another invertible +operation) with each plaintext block: + +`c0 = encipher key (nonce # 0) ^ p0` +`c1 = encipher key (nonce # 1) ^ p1` +`c2 = encipher key (nonce # 2) ^ p2` + +Our CTR specification will need to define and import an interface for +the additional nonce-specific parameters, and constrain these such +that a nonce and counter can be concatenated into a `Block`. Cryptol's +new module system can specify an +[`interface constraint`](https://galoisinc.github.io/cryptol/master/Modules.html#interface-constraints) +for parameters provided for one or more interfaces, e.g. + +```cryptol +// import interface submodule I +// import interface submodule J +// interface constraint I::n == J::n +``` + +**Exercise**: Define CTR mode for a given block cipher and CTR-specific +parameters. + +**Solution**: + +```cryptol +interface submodule I_CTR where + // nonce width + type w: # + + // counter width + type c: # + +submodule F_CTR_Aliases where + import interface submodule I_CTR as I + + // type alias for nonce + type Nonce = [I::w] + +submodule CTR where + // request a block cipher + import interface submodule I_BlockCipher + // request parameters for CTR + import interface submodule I_CTR + + interface constraint (fin b, b == w + c) // Replaced with our constraint. + + // derive the type aliases for that block cipher, and for the nonce + import submodule F_BlockCipher_Aliases { I = interface I_BlockCipher } (Block) + import submodule F_CTR_Aliases { I = interface I_CTR } (Nonce) + + /** Encrypt a multi-block message using CFB mode */ + encrypt : {n} c >= width (max 1 n - 1) => Key -> Nonce -> [n]Block -> [n]Block + encrypt key nonce pt = ct // Replaced with our definition. + where + ct@i = encipher key (nonce # i) ^ pt@i + + /** Decrypt a multi-block message using CFB mode */ + decrypt : {n} c >= width (max 1 n - 1) => Key -> Nonce -> [n]Block -> [n]Block + decrypt key nonce ct = pt // Replaced with our definition. + where + pt@i = encipher key (nonce # i) ^ ct@i +``` + +**Hint**: Use generator expressions again. + +**Exercise**: Find test vectors for a block cipher in CTR mode, specify +it, and verify the test vectors. + +## ...and more! + +**Exercise**: Feel free to specify other basic block cipher modes +such as PCBC, OFB, segmented CFB, etc. + +**TODO**: Introduce authenticated cipher modes, a positive +authentication property, and resilience properties (tamper, spoofing). -**EXERCISE**: Feel free to experiment with other block ciphers -(including SIMON) and modes. Show that ECB is still bad with those. +**TODO**: Get to the point where (sub)modules can import interfaces and +specify properties assumed about their implementations. Use these to +generate "contracts" and reusable test cases. + +# Properties and Verification (Interface Reuse) + +[2] defines the *CIA triad* of: + - *confidentiality* + - *integrity* + - *availability* + +In these terms, a cipher's purpose is to provide confidentiality (for +authorized persons to securely exchange message so that they are not +easily recoverable by unauthorized persons) and availability (messages +need to remain readable by authorized persons). + +In particular, at the expense of repeating ourselves, a cipher must +satisfy this familiar property, which you may have already used to +understand how to specify `decrypt` for block cipher modes: + +```cryptol +// property block_recovery key pt = decipher key (encipher key pt) == pt +``` + +In other words, a cipher must remain *available* to those with a shared +symmetric key. This must also hold true for cipher modes, e.g. for a +cipher mode with an initialization vector: + +```cryptol +// property mode_recovery = decrypt key iv (encrypt key iv pt) == pt +``` + +Another prerequisite of availability is that authorized parties agree +on how the cipher operates. Cryptol expresses algorithms as rigorous +formal specifications, but users more commonly confirm this agreement +using *test vectors*, e.g.: + +```cryptol +import submodule P_Simon_32_64_BlockCipher as S_32_64 +import submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } as S_32_64 + +S_32_64_test_key = 0x94eeea8b1f2ada84 +S_32_64_test_pt = 0xadf10331 +S_32_64_test_ct = 0x391e7f1a + +property test_simon_32_64_enc = S_32_64::encipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct +property test_simon_32_64_dec = S_32_64::decipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct +``` + +A strong cipher must resist cryptanalytic attacks of varying +sophistication. Cryptol and SMT solvers are not designed to perform +rigorous cryptanalysis, but can run some quick sanity checks. + +For example, it should not be easy to (ab)use SMT solvers to quickly +recover a cryptographic key from known or chosen plaintext: + +```xcryptol-session +// Try to recover `test_key` from known test plaintext/ciphertext +// labs::ModuleSystem> :sat \key -> S_32_64::encipher key test_pt == test_ct +``` + +**TODO**: Introduce exercises to check `mode_recovery` for various +block cipher modes, and show how to use SAW to formally verify these +using a block cipher's `block_recovery` property when applicable. # Conclusion -In this module, we learned how to apply the new module system to a -previously covered specification (SIMON) and to block cipher modes of -operation by defining common interfaces for abstractions to -instantiate implementations given parameters. The new module system -allowed us to combine all of these and a contrivance into a single -module for expository purposes. In the real world, it will eventually -form a solid foundation to organize a wide variety of cryptographic -algorithms and systems that combine them. +In this module, you learned how to define and reuse interfaces and +functors as the cryptographic building blocks (pun intended) of a +complex nested module with multiple ciphers, modes, and properties. + +# References + +[1] NIST SP 800-38A. + "Recommendation for Block Cipher Modes of Operation: Tools and Techniques". + December 2001. + https://csrc.nist.gov/pubs/sp/800/38/a/final + +[2] NIST SP 800-12 Rev. 1. + "An Introduction to Information Security". + June 2017. + https://csrc.nist.gov/pubs/sp/800/12/r1/final + +[3] NIST FIPS 81 (withdrawn). + "DES MODES OF OPERATION" + 2 December 1980. + https://csrc.nist.gov/pubs/fips/81/final # Solicitation @@ -809,6 +1054,6 @@ https://github.com/weaversa/cryptol-course/issues |||| |-:|:-:|-| -|| [ ^ Key Wrapping](../KeyWrapping/KeyWrapping.md) || -| [< Parameterized Modules](../SimonSpeck/SimonSpeck.md) | **New Module System** || +|| [- Key Wrapping](../KeyWrapping/KeyWrapping.md) || +| [< Parameterized Modules](../SimonSpeck/SimonSpeck.md) | **New Module System (Answers)** || || [? New Module System](./NewModuleSystem.md) || From eb582acccc7d32a9d6d7401f6da9faa67e46c5c2 Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 8 Dec 2023 00:53:11 +0000 Subject: [PATCH 04/15] Add AES exercises and test vectors --- labs/NewModuleSystem/NewModuleSystem.md | 359 ++++++++++-- .../NewModuleSystem/NewModuleSystemAnswers.md | 531 +++++++++++++++++- labs/SimonSpeck/SimonSpeck.md | 2 + labs/SimonSpeck/SpeckAnswers/Speck.cry | 38 +- 4 files changed, 851 insertions(+), 79 deletions(-) diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index 3c584100..c31af20e 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -9,8 +9,8 @@ Before working through this lab, you'll need You'll also need experience with * loading modules and evaluating functions in the interpreter, - * the `:prove` command, * writing functions and properties, + * testing properties by using the `:prove` and `:check` commands, * sequence comprehensions, * functions with curried parameters, and * parameterized modules. @@ -293,7 +293,7 @@ ciphers and parameter sets... functor with which to "adapt" prior solutions as implementations of `I_BlockCipher`. Instances would look something like: -`submodule P_Speck_64_128_BlockCipher = submodule F_Speck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_64_128 }` +`submodule P_Simon_64_128_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_64_128 }` Start with explicitly named submodules. These can always be "anonymized" later if it is obvious they won't be reused. @@ -334,10 +334,11 @@ submodule P_Simon_32_64_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs: ## Adapting other block ciphers -In the last exercise, you adapted your own prior work to fit out new +In the last exercise, you adapted your own prior work to fit our new `I_BlockCipher` interface. We can also adapt other simple block ciphers, such as DES in the -[Cryptographic Proofs](../CryptoProofs/CryptoProofs.md) lab. +[Cryptographic Proofs](../CryptoProofs/CryptoProofs.md) lab or +AES in the [Key Wrapping](../KeyWrapping/KeyWrapping.md) lab. ```cryptol import specs::Primitive::Symmetric::Cipher::Block::DES (DES) @@ -355,6 +356,39 @@ submodule P_DES_BlockCipher where decipher = undefined // Replace with your definition. ``` +**Exercise**: Specify an interface and a functor to generate adaptors +for each `AES` key size that provide all parameters required by +`interface submodule I_BlockCipher`: + +```cryptol +// Define and uncomment +/* +interface submodule I_AES_BlockCipher where + // ... + +submodule F_P_AES_BlockCipher where + import interface submodule I_AES_BlockCipher as I + + // ... + +submodule P_AES_128_BlockCipher = submodule F_P_AES_BlockCipher where + // ... + +submodule P_AES_192_BlockCipher = submodule F_P_AES_BlockCipher where + // ... + +submodule P_AES_256_BlockCipher = submodule F_P_AES_BlockCipher where + // ... +*/ +``` + +**Hint**: The interface could take the same key size parameter as +`specs::Primitive::Symmetric::Cipher::Block::AES::Algorithm`, and the +functor could define the corresponding key type, the same block size +for each key size, and `encipher` and `decipher`, using functions from +`specs::Primitive::Symmetric::Cipher::Block::AES_parameterized`. + + # Block Cipher Modes of Operation: ECB, CBC, ... Having implemented various block ciphers, we can now apply them toward @@ -410,7 +444,7 @@ in ECB mode. /** Simon 32/64 in ECB mode */ submodule ECB_Simon_32_64 = submodule ECB { submodule P_Simon_32_64_BlockCipher } -/** Simon 48/72 in ECB mode */ +// /** Simon 48/72 in ECB mode */ // submodule ECB_Simon_48_72 = ... // Replace with your definition and uncomment. // Repeat for each Simon and Speck parameter set. @@ -428,6 +462,7 @@ prominent warning not to use it in production. Your solution should pass the following test vectors [3]: ```cryptol +/** ECB/DES Tests */ submodule ECB_DES_Test where import submodule ECB_DES as ECB_DES @@ -472,6 +507,137 @@ Passed 1 tests. Q.E.D. ``` +**Exercise**: Now define submodules that implement AES (a stronger +block cipher) in ECB (a weak cipher mode). Add docstrings with +prominent warnings not to use them in production. + +Your solution should pass the following test vectors [4]: + +```cryptol +// Let's use the new module system to generate groups of related properties... + +submodule F_KAT_ECB where + import interface submodule I_BlockCipher + interface constraint fin b + + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } + + interface submodule I_KAT where + type n : # + type constraint fin n + + KEY: Key + PLAINTEXT : [n]Block + CIPHERTEXT : [n]Block + + submodule F_KAT where + import interface submodule I_KAT + + import submodule ECB { interface I_BlockCipher } + + property test_encrypt = encrypt KEY PLAINTEXT == CIPHERTEXT + property test_decrypt = decrypt KEY CIPHERTEXT == PLAINTEXT + +submodule P_KAT_ECB_AES_128_ENCRYPT_9 where + type n = 10 + KEY = 0xebea9c6a82213a00ac1d22faea22116f + PLAINTEXT = split 0x451f45663b44fd005f3c288ae57b383883f02d9ad3dc1715f9e3d6948564257b9b06d7dd51935fee580a96bbdfefb918b4e6b1daac809847465578cb8b5356ed38556f801ff7c11ecba9cdd263039c15d05900fc228e1caf302d261d7fb56cee663595b96f192a78ff4455393a5fe8162170a066fdaeac35019469f22b3470686bced2f007a1a2e43e01b4562caaa502ed541b8205874ec1ffb1c8b255766942 + CIPHERTEXT = split 0x01043053f832ef9b911ed387ba577451e30d51d4b6b11f319d4cd539d067b7f4f9b4f41f7f3d4e920c57cbe2b5e1885aa66203ae493e93a1df63793a9563c176bc6775dd09cc9161e278a01beb8fd8a19200326bd95abc5f716768e34f90b50523d30fdabb103a3bc020afbbb0cb3bd2ad512a6fea79f8d64cef347458dec48be89451cb0b807d73593f273d9fc521b789a77524404f43e00f20b3b77b938b1a + +submodule P_KAT_ECB_AES_128_DECRYPT_9 where + type n = 10 + KEY = 0x44f0ee626d0446e0a3924cfb078944bb + CIPHERTEXT = split 0x931b2f5f3a5820d53a6beaaa6431083a3488f4eb03b0f5b57ef838e1579623103bd6e6800377538b2e51ef708f3c4956432e8a8ee6a34e190642b26ad8bdae6c2af9a6c7996f3b6004d2671e41f1c9f40ee03d1c4a52b0a0654a331f15f34dce4acb96bd6507815ca4347a3de11a311b7de5351c9787c4538158e28974ffa83d8296dfe9cd09cd87f7bf4f54d97d28d4788799163408323943b3e72f5eab66c1 + PLAINTEXT = split 0x9c29eecb2de04254fafb896a994102d1da30ddb49d82728eb23dbd029901e9b75b3d0aee03f7a05f6c852d8fada0b5c28e8c9aed334fad11829df3dfadc5c2e471eb41af9e48a8a465e03d5ebdb0216915081f3b5a0ebb2308dfc2d28e5a8ba3f32adae4c3575921bc657b63d46ba5a618880ee9ad8af3fba5643a5026facd7d667ce599327f936cdda7e1bb742a33a019990b76be648a6ec725daed540ed9e7 + +submodule P_KAT_ECB_AES_192_ENCRYPT_9 where + type n = 10 + KEY = 0x4f41fa4d4a25100b586551828373bcca5540c68e9bf84562 + PLAINTEXT = split 0x7c727bd3e7048e7a8995b7b1169ae4b5a55e854bb4f7a9576d7863ab2868731d307322dcca606e047343676f6af4d9cf6ebf2bf9c95d87848d233c931e7a60eff08fb959924cde1eec8699ebc57890e3887024ef47c89a550018788d1faa3250452e06f148af25f07bc613cd2f0e501a79d738d4361f28f34dbee24034e03367b6b8d34df3738ca3a86b9ebcb09e639bcb5e2f519f4a7a86fc7c41556404a95d + CIPHERTEXT = split 0x922812ad5feacdf11fe7fdae96300149419e31cff54061b3c5ed27fdb8b50c9c0932b522a6c04e482499b011ef3c3e9dc56a1a61cfeb78b34032d26dbdc3cac51a3279bc934b9bce2d9c19bf858235613ba784e48e292d22c6b5a28e1d1bb860524fb7b5f9b3d9a5f4da66e340585bd2496fe6d6942db8d05d716fec03b17d19abb58b33332e24beaec7995d69525364fe139aa1fd62054668c58f23f1f94cfd + +submodule P_KAT_ECB_AES_192_DECRYPT_9 where + type n = 10 + KEY = 0x9cc24ea1f1959d9a972e7182ef3b4e22a97a87d0da7ff64b + CIPHERTEXT = split 0x952f4546a8bf7166964917ece01bda3c6857e427cef5da0ff90b0e4bf44cf7ccfccfdf01d713dcf9673f01c87eaed52bf4aa046ff778558ea396dc9cd240716136386148a5c76378b3ffcd40864407b8e60b40a594e0619eddae3f6d6e3b15b86af231e1bae5ed2aa512e11da0e5572b67ffff934c36e585cfdd9f877045cb19c183b994bf74645862ffa726739aadcb9e10aaffc881c88ca3aa65b37f667bcb + PLAINTEXT = split 0xb8bb5ce53a15aa6dfdf2cb61bc8e3617d1d0fefe9ba5d175550470e32397f6f3b3e65b43bded2b21e5c181d3c4c4c526c41ceab044289508458048b63352dfc379de373fd19a2c900c43524b75949e677cceda866f7f2bcc4844ef2e5dac5b804b4045e657c8156d1dcdb43cbf2f5e00a4f9255e3be2439436c4d0449a8d2c4c1a56bece98ea0fd68abaf12398039994aebffc692b9000e580479b4f4b28b5fe + +submodule P_KAT_ECB_AES_256_ENCRYPT_9 where + type n = 10 + KEY = 0x44a2b5a7453e49f38261904f21ac797641d1bcd8ddedd293f319449fe63b2948 + PLAINTEXT = split 0xc91b8a7b9c511784b6a37f73b290516bb9ef1e8df68d89bf49169eac4039650c4307b6260e9c4e93650223440252f5c7d31c26c56209cbd095bf035b9705880a1628832daf9da587a6e77353dbbce189f963235df160c008a753e8ccea1e0732aa469a97659c42e6e31c16a723153e39958abe5b8ad88ff2e89af40622ca0b0d6729a26c1ae04d3b8367b548c4a6335f0e5a9ec914bb6113c05cd0112552bc21 + CIPHERTEXT = split 0x05d51af0e2b61e2c06cb1e843fee3172825e63b5d1ce8183b7e1db6268db5aa726521f46e948028aa443af9ebd8b7c6baf958067ab0d4a8ac530ecbb68cdfc3eb93034a428eb7e8f6a3813cea6189068dfecfa268b7ecd5987f8cb2732c6882bbec8f716bac254d72269230aec5dc7f5a6b866fd305242552d400f5b0404f19cbfe7291fab690ecfe6018c4309fc639d1b65fcb65e643edb0ad1f09cfe9cee4a + +submodule P_KAT_ECB_AES_256_DECRYPT_9 where + type n = 10 + KEY = 0xc4a71e055a7254dda360693fe1be49f10faa6731c36dbaa6590b05974e185c5b + CIPHERTEXT = split 0x2c487fa96f4090c56aa1b5be81918a934c9492878fb0cd686dcf8d17d86485454c51237bbd09205dcef1552f430dd098b9d827a694730c133a0222c77f540f9d5fc2d36af359583c9e3b49df884228a64de79b67f66207c8281360b99b214042ce61367ff97960e944453cd63679bb44708897d29bc5e70f9fc8f1f715143fbb00f7f5c1b7b161ec26d8d41d36fab0fa8a85c3ee6ce4d37007eb7a89d6753590 + PLAINTEXT = split 0x31fd5a307e279b2f34581e2c432379df8eccbaf79532938916711cd377540b9045373e47f2214b8f876040af733f6c9d8f03a7c58f8714d2fbb4c14af59c75b483adc718946ee907a18286cc4efd206789064b6f1b195f0d0d234468e4f00e6f1cad5cd3b9c0a643b3c0dd09280ff2e2a5929183409384dd72dc94e39687ea2b623d5d776700bd8b36e6130ffde966f134c4b1f35f29c5cc4a03297e1ccc9539 + +import submodule F_KAT_ECB { submodule P_AES_128_BlockCipher } as F_KAT_ECB_AES_128 (F_KAT) +import submodule F_KAT_ECB_AES_128::F_KAT { submodule P_KAT_ECB_AES_128_ENCRYPT_9 } as KAT_ECB_AES_128_ENCRYPT_9 +import submodule F_KAT_ECB_AES_128::F_KAT { submodule P_KAT_ECB_AES_128_DECRYPT_9 } as KAT_ECB_AES_128_DECRYPT_9 + +import submodule F_KAT_ECB { submodule P_AES_192_BlockCipher } as F_KAT_ECB_AES_192 (F_KAT) +import submodule F_KAT_ECB_AES_192::F_KAT { submodule P_KAT_ECB_AES_192_ENCRYPT_9 } as KAT_ECB_AES_192_ENCRYPT_9 +import submodule F_KAT_ECB_AES_192::F_KAT { submodule P_KAT_ECB_AES_192_DECRYPT_9 } as KAT_ECB_AES_192_DECRYPT_9 + +import submodule F_KAT_ECB { submodule P_AES_256_BlockCipher } as F_KAT_ECB_AES_256 (F_KAT) +import submodule F_KAT_ECB_AES_256::F_KAT { submodule P_KAT_ECB_AES_256_ENCRYPT_9 } as KAT_ECB_AES_256_ENCRYPT_9 +import submodule F_KAT_ECB_AES_256::F_KAT { submodule P_KAT_ECB_AES_256_DECRYPT_9 } as KAT_ECB_AES_256_DECRYPT_9 +``` + +```xcryptol-session +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + ## Cipher Block Chaining (CBC) To better diffuse message blocks, CBC introduces an *initialization @@ -602,6 +768,141 @@ Passed 1 tests. Q.E.D. ``` +**Exercise**: Now define submodules that implement AES in CBC mode for +each AES key size. + +Your solution should pass the following test vectors: + +```cryptol +submodule F_KAT_CBC where + import interface submodule I_BlockCipher + interface constraint fin b + + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } + + interface submodule I_KAT where + type n : # + type constraint fin n + + KEY: Key + IV: Block + PLAINTEXT : [n]Block + CIPHERTEXT : [n]Block + + submodule F_KAT where + import interface submodule I_KAT + + import submodule CBC { interface I_BlockCipher } + + property test_encrypt = encrypt KEY IV PLAINTEXT == CIPHERTEXT + property test_decrypt = decrypt KEY IV CIPHERTEXT == PLAINTEXT + +submodule P_KAT_CBC_AES_128_ENCRYPT_9 where + type n = 10 + KEY = 0x2c14413751c31e2730570ba3361c786b + IV = 0x1dbbeb2f19abb448af849796244a19d7 + PLAINTEXT = split 0x40d930f9a05334d9816fe204999c3f82a03f6a0457a8c475c94553d1d116693adc618049f0a769a2eed6a6cb14c0143ec5cccdbc8dec4ce560cfd206225709326d4de7948e54d603d01b12d7fed752fb23f1aa4494fbb00130e9ded4e77e37c079042d828040c325b1a5efd15fc842e44014ca4374bf38f3c3fc3ee327733b0c8aee1abcd055772f18dc04603f7b2c1ea69ff662361f2be0a171bbdcea1e5d3f + CIPHERTEXT = split 0x6be8a12800455a320538853e0cba31bd2d80ea0c85164a4c5c261ae485417d93effe2ebc0d0a0b51d6ea18633d210cf63c0c4ddbc27607f2e81ed9113191ef86d56f3b99be6c415a4150299fb846ce7160b40b63baf1179d19275a2e83698376d28b92548c68e06e6d994e2c1501ed297014e702cdefee2f656447706009614d801de1caaf73f8b7fa56cf1ba94b631933bbe577624380850f117435a0355b2b + +submodule P_KAT_CBC_AES_128_DECRYPT_9 where + type n = 10 + KEY = 0x97a1025529b9925e25bbe78770ca2f99 + IV = 0xd4b4eab92aa9637e87d366384ed6915c + CIPHERTEXT = split 0x22cdc3306fcd4d31ccd32720cbb61bad28d855670657c48c7b88c31f4fa1f93c01b57da90be63ead67d6a325525e6ed45083e6fb70a53529d1fa0f55653b942af59d78a2660361d63a7290155ac5c43312a25b235dacbbc863faf00940c99624076dfa44068e7c554c9038176953e571751dfc0954d41d113771b06466b1c8d13e0d4cb675ed58d1a619e1540970983781dc11d2dd8525ab5745958d615defda + PLAINTEXT = split 0xe8b89150d8438bf5b17449d6ed26bd72127e10e4aa57cad85283e8359e089208e84921649f5b60ea21f7867cbc9620560c4c6238db021216db453c9943f1f1a60546173daef2557c3cdd855031b353d4bf176f28439e48785c37d38f270aa4a6faad2baabcb0c0b2d1dd5322937498ce803ba1148440a52e227ddba4872fe4d81d2d76a939d24755adb8a7b8452ceed2d179e1a5848f316f5c016300a390bfa7 + +submodule P_KAT_CBC_AES_192_ENCRYPT_9 where + type n = 10 + KEY = 0x162ad50ee64a0702aa551f571dedc16b2c1b6a1e4d4b5eee + IV = 0x24408038161a2ccae07b029bb66355c1 + PLAINTEXT = split 0xbe8abf00901363987a82cc77d0ec91697ba3857f9e4f84bd79406c138d02698f003276d0449120bef4578d78fecabe8e070e11710b3f0a2744bd52434ec70015884c181ebdfd51c604a71c52e4c0e110bc408cd462b248a80b8a8ac06bb952ac1d7faed144807f1a731b7febcaf7835762defe92eccfc7a9944e1c702cffe6bc86733ed321423121085ac02df8962bcbc1937092eebf0e90a8b20e3dd8c244ae + CIPHERTEXT = split 0xc82cf2c476dea8cb6a6e607a40d2f0391be82ea9ec84a537a6820f9afb997b76397d005424faa6a74dc4e8c7aa4a8900690f894b6d1dca80675393d2243adac762f159301e357e98b724762310cd5a7bafe1c2a030dba46fd93a9fdb89cc132ca9c17dc72031ec6822ee5a9d99dbca66c784c01b0885cbb62e29d97801927ec415a5d215158d325f9ee689437ad1b7684ad33c0d92739451ac87f39ff8c31b84 + +submodule P_KAT_CBC_AES_192_DECRYPT_9 where + type n = 10 + KEY = 0x509baf46fb9de34281dafcc3db79593bffa8426904302688 + IV = 0xd6d86e0c82dd8788f4147a26f9a71c74 + CIPHERTEXT = split 0x6928299c52b4f047926f8a541529da2d6bbaa399143ced8efb77ab47409d9a953a386c7abd6026f49831c717627c2a5e77bd2d433d4d130dacd927ea0d13a23d01a7cf39c6716dafb6ed552410ef5d27fb947be2c8782eee7829196c7edcf151c65f9a01f54f8d20f38b7da4a7e83a2f0127d59d3e2405d8674fc9f41b604f788f4715f9d3624eee57f387bfadd18a1f905e839c26b8617482347fab6d08845a + PLAINTEXT = split 0x67d2dda6da26e21307973400600725727ae81415511772f4a09ad9903bcf90cc2c0dac58ba559a0109c54a9d6117b15bb574ca473e848047e9a54ee4abde76aff9849c44109d161f46442e1610d8b015cf36a010ed8efa3207fdfc8fcc548f145c027e44c5b0ec35c9886f4b9d6513a5bc10d0ea6bbbc26f54b183bcae27fb799d8872ff748fc459d55cfa255aae29d71b076d9b44c14d5ceba9332a763d9c94 + +submodule P_KAT_CBC_AES_256_ENCRYPT_9 where + type n = 10 + KEY = 0x48be597e632c16772324c8d3fa1d9c5a9ecd010f14ec5d110d3bfec376c5532b + IV = 0xd6d581b8cf04ebd3b6eaa1b53f047ee1 + PLAINTEXT = split 0x0c63d413d3864570e70bb6618bf8a4b9585586688c32bba0a5ecc1362fada74ada32c52acfd1aa7444ba567b4e7daaecf7cc1cb29182af164ae5232b002868695635599807a9a7f07a1f137e97b1e1c9dabc89b6a5e4afa9db5855edaa575056a8f4f8242216242bb0c256310d9d329826ac353d715fa39f80cec144d6424558f9f70b98c920096e0f2c855d594885a00625880e9dfb734163cecef72cf030b8 + CIPHERTEXT = split 0xfc5873e50de8faf4c6b84ba707b0854e9db9ab2e9f7d707fbba338c6843a18fc6facebaf663d26296fb329b4d26f18494c79e09e779647f9bafa87489630d79f4301610c2300c19dbf3148b7cac8c4f4944102754f332e92b6f7c5e75bc6179eb877a078d4719009021744c14f13fd2a55a2b9c44d18000685a845a4f632c7c56a77306efa66a24d05d088dcd7c13fe24fc447275965db9e4d37fbc9304448cd + +submodule P_KAT_CBC_AES_256_DECRYPT_9 where + type n = 10 + KEY = 0x87725bd43a45608814180773f0e7ab95a3c859d83a2130e884190e44d14c6996 + IV = 0xe49651988ebbb72eb8bb80bb9abbca34 + CIPHERTEXT = split 0x5b97a9d423f4b97413f388d9a341e727bb339f8e18a3fac2f2fb85abdc8f135deb30054a1afdc9b6ed7da16c55eba6b0d4d10c74e1d9a7cf8edfaeaa684ac0bd9f9d24ba674955c79dc6be32aee1c260b558ff07e3a4d49d24162011ff254db8be078e8ad07e648e6bf5679376cb4321a5ef01afe6ad8816fcc7634669c8c4389295c9241e45fff39f3225f7745032daeebe99d4b19bcb215d1bfdb36eda2c24 + PLAINTEXT = split 0xbfe5c6354b7a3ff3e192e05775b9b75807de12e38a626b8bf0e12d5fff78e4f1775aa7d792d885162e66d88930f9c3b2cdf8654f56972504803190386270f0aa43645db187af41fcea639b1f8026ccdd0c23e0de37094a8b941ecb7602998a4b2604e69fc04219585d854600e0ad6f99a53b2504043c08b1c3e214d17cde053cbdf91daa999ed5b47c37983ba3ee254bc5c793837daaa8c85cfc12f7f54f699f + +import submodule F_KAT_CBC { submodule P_AES_128_BlockCipher } as F_KAT_CBC_AES_128 (F_KAT) +import submodule F_KAT_CBC_AES_128::F_KAT { submodule P_KAT_CBC_AES_128_ENCRYPT_9 } as KAT_CBC_AES_128_ENCRYPT_9 +import submodule F_KAT_CBC_AES_128::F_KAT { submodule P_KAT_CBC_AES_128_DECRYPT_9 } as KAT_CBC_AES_128_DECRYPT_9 + +import submodule F_KAT_CBC { submodule P_AES_192_BlockCipher } as F_KAT_CBC_AES_192 (F_KAT) +import submodule F_KAT_CBC_AES_192::F_KAT { submodule P_KAT_CBC_AES_192_ENCRYPT_9 } as KAT_CBC_AES_192_ENCRYPT_9 +import submodule F_KAT_CBC_AES_192::F_KAT { submodule P_KAT_CBC_AES_192_DECRYPT_9 } as KAT_CBC_AES_192_DECRYPT_9 + +import submodule F_KAT_CBC { submodule P_AES_256_BlockCipher } as F_KAT_CBC_AES_256 (F_KAT) +import submodule F_KAT_CBC_AES_256::F_KAT { submodule P_KAT_CBC_AES_256_ENCRYPT_9 } as KAT_CBC_AES_256_ENCRYPT_9 +import submodule F_KAT_CBC_AES_256::F_KAT { submodule P_KAT_CBC_AES_256_DECRYPT_9 } as KAT_CBC_AES_256_DECRYPT_9 +``` + +```xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + ## Cipher Feedback (CFB) CFB can take any of various forms, including a full-block mode or one @@ -646,7 +947,8 @@ given `ct_i = (encipher key iv) ^ pt_i`, solve for `pt_i`. **Exercise**: Define a submodule that implements DES (a weak block cipher) in full-block CFB mode (a cipher mode). Add a docstring with -a prominent warning not to use it in production. +a prominent warning not to use it in production. Check these against +test vectors in [3]. **Solution**: @@ -655,33 +957,6 @@ a prominent warning not to use it in production. // submodule CFB_DES = ... // Replace with your definition and uncomment. ``` -Your solution should pass the following test vectors [3]: - -```cryptol -submodule CFB_DES_Test where - import submodule CFB_DES as CFB_DES - - e (key, iv, pt, ct) = CFB_DES::encrypt key iv pt == ct - d (key, iv, pt, ct) = CFB_DES::decrypt key iv ct == pt - - // from FIPS 81 (withdrawn, of course) - v_fips_81 = (0x0123456789abcdef, 0x1234567890abcdef, [0x4e6f772069732074, 0x68652074696d6520, 0x666f7220616c6c20], [0xf3096249c7f46e51, 0xa69e839b1a92f784, 0x03467133898ea622]) - - property e_fips_81 = e v_fips_81 - property d_fips_81 = d v_fips_81 -``` - -```xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :check CFB_DES_Test::e_fips_81 -Using exhaustive testing. -Passed 1 tests. -Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check CFB_DES_Test::d_fips_81 -Using exhaustive testing. -Passed 1 tests. -Q.E.D. -``` - ## Counter (CTR) Mode (Multiple Interfaces and Interface Constraints) Counter mode produces a stream cipher by applying a block cipher to @@ -758,13 +1033,6 @@ it, and verify the test vectors. **Exercise**: Feel free to specify other basic block cipher modes such as PCBC, OFB, segmented CFB, etc. -**TODO**: Introduce authenticated cipher modes, a positive -authentication property, and resilience properties (tamper, spoofing). - -**TODO**: Get to the point where (sub)modules can import interfaces and -specify properties assumed about their implementations. Use these to -generate "contracts" and reusable test cases. - # Properties and Verification (Interface Reuse) [2] defines the *CIA triad* of: @@ -800,7 +1068,8 @@ using *test vectors*, e.g.: ```cryptol import submodule P_Simon_32_64_BlockCipher as S_32_64 -import submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } as S_32_64 +submodule S_32_64_Aliases = submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } +import submodule S_32_64_Aliases as S_32_64 S_32_64_test_key = 0x94eeea8b1f2ada84 S_32_64_test_pt = 0xadf10331 @@ -822,10 +1091,6 @@ recover a cryptographic key from known or chosen plaintext: // labs::ModuleSystem> :sat \key -> S_32_64::encipher key test_pt == test_ct ``` -**TODO**: Introduce exercises to check `mode_recovery` for various -block cipher modes, and show how to use SAW to formally verify these -using a block cipher's `block_recovery` property when applicable. - # Conclusion In this module, you learned how to define and reuse interfaces and @@ -849,6 +1114,10 @@ complex nested module with multiple ciphers, modes, and properties. 2 December 1980. https://csrc.nist.gov/pubs/fips/81/final +[4] NIST Advanced Encryption Standard Algorithm Verification System + AES Multiblock Message Test (MMT) Sample Vectors + https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmmt.zip + # Solicitation How was your experience with this lab? Suggestions are welcome in the diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md index 2c59edee..7ca8835b 100644 --- a/labs/NewModuleSystem/NewModuleSystemAnswers.md +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -9,8 +9,8 @@ Before working through this lab, you'll need You'll also need experience with * loading modules and evaluating functions in the interpreter, - * the `:prove` command, * writing functions and properties, + * testing properties by using the `:prove` and `:check` commands, * sequence comprehensions, * functions with curried parameters, and * parameterized modules. @@ -130,13 +130,13 @@ cipher operations from those for modes, in keeping with [1].) parameter /** arbitrary key type */ type K : * - + /** block size (in bits) */ type b : # - + /** forward cipher operator */ encipher : K -> [b] -> [b] - + /** reverse cipher operator */ decipher : K -> [b] -> [b] */ @@ -153,19 +153,19 @@ reusable **type aliases**: interface submodule I_BlockCipher where /** arbitrary key type */ type Key : * - + /** block size (in bits) */ type b : # - + /** type alias for a block, a bit string of width `b` */ type Block = [b] - + /** common type for `encipher` and `decipher` */ type Op = Key -> Block -> Block - + /** forward cipher operator */ encipher : Op - + /** reverse cipher operator */ decipher : Op ``` @@ -208,7 +208,7 @@ submodule F_BlockCipher_Aliases where /** type alias for a block, a bit string of width `b` */ type Block = [I::b] - + /** common type for `encipher` and `decipher` */ type Op = I::Key -> Block -> Block ``` @@ -293,7 +293,7 @@ ciphers and parameter sets... functor with which to "adapt" prior solutions as implementations of `I_BlockCipher`. Instances would look something like: -`submodule P_Speck_64_128_BlockCipher = submodule F_Speck_BlockCipher { labs::SimonSpeck::SpeckAnswers::speck_64_128 }` +`submodule P_Simon_64_128_BlockCipher = submodule F_SimonSpeck_BlockCipher { labs::SimonSpeck::Simon::simon_64_128 }` Start with explicitly named submodules. These can always be "anonymized" later if it is obvious they won't be reused. @@ -302,7 +302,7 @@ Start with explicitly named submodules. These can always be ```cryptol /** interface to collect relevant definitions from Simon instance */ -interface submodule I_Simon_Inst where +interface submodule I_SimonSpeck_Inst where type keySize : # type blockSize : # @@ -316,7 +316,7 @@ interface submodule I_Simon_Inst where // generalization of `Simon_32_64_BlockCipher` above /** generate `I_BlockCipher` implementation from Simon instance */ submodule F_SimonSpeck_BlockCipher where - import interface submodule I_Simon_Inst as S + import interface submodule I_SimonSpeck_Inst as S // export definitions for `I_BlockCipher` parameters type Key = [S::keySize] @@ -388,10 +388,11 @@ submodule P_Speck_128_256_BlockCipher = submodule F_SimonSpeck_BlockCipher { lab ## Adapting other block ciphers -In the last exercise, you adapted your own prior work to fit out new +In the last exercise, you adapted your own prior work to fit our new `I_BlockCipher` interface. We can also adapt other simple block ciphers, such as DES in the -[Cryptographic Proofs](../CryptoProofs/CryptoProofsAnswers.md) lab. +[Cryptographic Proofs](../CryptoProofs/CryptoProofsAnswers.md) lab or +AES in the [Key Wrapping](../KeyWrapping/KeyWrappingAnswers.md) lab. ```cryptol import specs::Primitive::Symmetric::Cipher::Block::DES (DES) @@ -411,6 +412,47 @@ submodule P_DES_BlockCipher where decipher = DES.decrypt ``` +**Exercise**: Specify an interface and a functor to generate adaptors +for each `AES` key size that provide all parameters required by +`interface submodule I_BlockCipher`: + +**Solution**: + +```cryptol +interface submodule I_AES_BlockCipher where + /** 0: AES128, 1: AES192, 2: AES256 */ + type Mode : # + type constraint (2 >= Mode) + +submodule F_P_AES_BlockCipher where + import interface submodule I_AES_BlockCipher as I + + import specs::Primitive::Symmetric::Cipher::Block::AES_parameterized as S + + type k = 128 + I::Mode * 64 + type Key = [k] + type b = 128 + + encipher = S::encrypt`{k=k, m=I::Mode} + decipher = S::decrypt`{k=k, m=I::Mode} + +submodule P_AES_128_BlockCipher = submodule F_P_AES_BlockCipher where + type Mode = 0 + +submodule P_AES_192_BlockCipher = submodule F_P_AES_BlockCipher where + type Mode = 1 + +submodule P_AES_256_BlockCipher = submodule F_P_AES_BlockCipher where + type Mode = 2 +``` + +**Hint**: The interface could take the same key size parameter as +`specs::Primitive::Symmetric::Cipher::Block::AES::Algorithm`, and the +functor could define the corresponding key type, the same block size +for each key size, and `encipher` and `decipher`, using functions from +`specs::Primitive::Symmetric::Cipher::Block::AES_parameterized`. + + # Block Cipher Modes of Operation: ECB, CBC, ... Having implemented various block ciphers, we can now apply them toward @@ -542,6 +584,7 @@ submodule ECB_DES = submodule ECB { submodule P_DES_BlockCipher } Your solution should pass the following test vectors [3]: ```cryptol +/** ECB/DES Tests */ submodule ECB_DES_Test where import submodule ECB_DES as ECB_DES @@ -586,6 +629,148 @@ Passed 1 tests. Q.E.D. ``` +**Exercise**: Now define submodules that implement AES (a stronger +block cipher) in ECB (a weak cipher mode). Add docstrings with +prominent warnings not to use them in production. + +**Solution**: + +```cryptol +/** AES is A-OK, but ECB is bad; do not use this for anything important */ +submodule ECB_AES_128 = submodule ECB { submodule P_AES_128_BlockCipher } +/** AES is A-OK, but ECB is bad; do not use this for anything important */ +submodule ECB_AES_192 = submodule ECB { submodule P_AES_192_BlockCipher } +/** AES is A-OK, but ECB is bad; do not use this for anything important */ +submodule ECB_AES_256 = submodule ECB { submodule P_AES_256_BlockCipher } +``` + +Your solution should pass the following test vectors [4]: + +```cryptol +// Let's use the new module system to generate groups of related properties... + +submodule F_KAT_ECB where + import interface submodule I_BlockCipher + interface constraint fin b + + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } + + interface submodule I_KAT where + type n : # + type constraint fin n + + KEY: Key + PLAINTEXT : [n]Block + CIPHERTEXT : [n]Block + + submodule F_KAT where + import interface submodule I_KAT + + import submodule ECB { interface I_BlockCipher } + + property test_encrypt = encrypt KEY PLAINTEXT == CIPHERTEXT + property test_decrypt = decrypt KEY CIPHERTEXT == PLAINTEXT + +submodule P_KAT_ECB_AES_128_ENCRYPT_9 where + type n = 10 + KEY = 0xebea9c6a82213a00ac1d22faea22116f + PLAINTEXT = split 0x451f45663b44fd005f3c288ae57b383883f02d9ad3dc1715f9e3d6948564257b9b06d7dd51935fee580a96bbdfefb918b4e6b1daac809847465578cb8b5356ed38556f801ff7c11ecba9cdd263039c15d05900fc228e1caf302d261d7fb56cee663595b96f192a78ff4455393a5fe8162170a066fdaeac35019469f22b3470686bced2f007a1a2e43e01b4562caaa502ed541b8205874ec1ffb1c8b255766942 + CIPHERTEXT = split 0x01043053f832ef9b911ed387ba577451e30d51d4b6b11f319d4cd539d067b7f4f9b4f41f7f3d4e920c57cbe2b5e1885aa66203ae493e93a1df63793a9563c176bc6775dd09cc9161e278a01beb8fd8a19200326bd95abc5f716768e34f90b50523d30fdabb103a3bc020afbbb0cb3bd2ad512a6fea79f8d64cef347458dec48be89451cb0b807d73593f273d9fc521b789a77524404f43e00f20b3b77b938b1a + +submodule P_KAT_ECB_AES_128_DECRYPT_9 where + type n = 10 + KEY = 0x44f0ee626d0446e0a3924cfb078944bb + CIPHERTEXT = split 0x931b2f5f3a5820d53a6beaaa6431083a3488f4eb03b0f5b57ef838e1579623103bd6e6800377538b2e51ef708f3c4956432e8a8ee6a34e190642b26ad8bdae6c2af9a6c7996f3b6004d2671e41f1c9f40ee03d1c4a52b0a0654a331f15f34dce4acb96bd6507815ca4347a3de11a311b7de5351c9787c4538158e28974ffa83d8296dfe9cd09cd87f7bf4f54d97d28d4788799163408323943b3e72f5eab66c1 + PLAINTEXT = split 0x9c29eecb2de04254fafb896a994102d1da30ddb49d82728eb23dbd029901e9b75b3d0aee03f7a05f6c852d8fada0b5c28e8c9aed334fad11829df3dfadc5c2e471eb41af9e48a8a465e03d5ebdb0216915081f3b5a0ebb2308dfc2d28e5a8ba3f32adae4c3575921bc657b63d46ba5a618880ee9ad8af3fba5643a5026facd7d667ce599327f936cdda7e1bb742a33a019990b76be648a6ec725daed540ed9e7 + +submodule P_KAT_ECB_AES_192_ENCRYPT_9 where + type n = 10 + KEY = 0x4f41fa4d4a25100b586551828373bcca5540c68e9bf84562 + PLAINTEXT = split 0x7c727bd3e7048e7a8995b7b1169ae4b5a55e854bb4f7a9576d7863ab2868731d307322dcca606e047343676f6af4d9cf6ebf2bf9c95d87848d233c931e7a60eff08fb959924cde1eec8699ebc57890e3887024ef47c89a550018788d1faa3250452e06f148af25f07bc613cd2f0e501a79d738d4361f28f34dbee24034e03367b6b8d34df3738ca3a86b9ebcb09e639bcb5e2f519f4a7a86fc7c41556404a95d + CIPHERTEXT = split 0x922812ad5feacdf11fe7fdae96300149419e31cff54061b3c5ed27fdb8b50c9c0932b522a6c04e482499b011ef3c3e9dc56a1a61cfeb78b34032d26dbdc3cac51a3279bc934b9bce2d9c19bf858235613ba784e48e292d22c6b5a28e1d1bb860524fb7b5f9b3d9a5f4da66e340585bd2496fe6d6942db8d05d716fec03b17d19abb58b33332e24beaec7995d69525364fe139aa1fd62054668c58f23f1f94cfd + +submodule P_KAT_ECB_AES_192_DECRYPT_9 where + type n = 10 + KEY = 0x9cc24ea1f1959d9a972e7182ef3b4e22a97a87d0da7ff64b + CIPHERTEXT = split 0x952f4546a8bf7166964917ece01bda3c6857e427cef5da0ff90b0e4bf44cf7ccfccfdf01d713dcf9673f01c87eaed52bf4aa046ff778558ea396dc9cd240716136386148a5c76378b3ffcd40864407b8e60b40a594e0619eddae3f6d6e3b15b86af231e1bae5ed2aa512e11da0e5572b67ffff934c36e585cfdd9f877045cb19c183b994bf74645862ffa726739aadcb9e10aaffc881c88ca3aa65b37f667bcb + PLAINTEXT = split 0xb8bb5ce53a15aa6dfdf2cb61bc8e3617d1d0fefe9ba5d175550470e32397f6f3b3e65b43bded2b21e5c181d3c4c4c526c41ceab044289508458048b63352dfc379de373fd19a2c900c43524b75949e677cceda866f7f2bcc4844ef2e5dac5b804b4045e657c8156d1dcdb43cbf2f5e00a4f9255e3be2439436c4d0449a8d2c4c1a56bece98ea0fd68abaf12398039994aebffc692b9000e580479b4f4b28b5fe + +submodule P_KAT_ECB_AES_256_ENCRYPT_9 where + type n = 10 + KEY = 0x44a2b5a7453e49f38261904f21ac797641d1bcd8ddedd293f319449fe63b2948 + PLAINTEXT = split 0xc91b8a7b9c511784b6a37f73b290516bb9ef1e8df68d89bf49169eac4039650c4307b6260e9c4e93650223440252f5c7d31c26c56209cbd095bf035b9705880a1628832daf9da587a6e77353dbbce189f963235df160c008a753e8ccea1e0732aa469a97659c42e6e31c16a723153e39958abe5b8ad88ff2e89af40622ca0b0d6729a26c1ae04d3b8367b548c4a6335f0e5a9ec914bb6113c05cd0112552bc21 + CIPHERTEXT = split 0x05d51af0e2b61e2c06cb1e843fee3172825e63b5d1ce8183b7e1db6268db5aa726521f46e948028aa443af9ebd8b7c6baf958067ab0d4a8ac530ecbb68cdfc3eb93034a428eb7e8f6a3813cea6189068dfecfa268b7ecd5987f8cb2732c6882bbec8f716bac254d72269230aec5dc7f5a6b866fd305242552d400f5b0404f19cbfe7291fab690ecfe6018c4309fc639d1b65fcb65e643edb0ad1f09cfe9cee4a + +submodule P_KAT_ECB_AES_256_DECRYPT_9 where + type n = 10 + KEY = 0xc4a71e055a7254dda360693fe1be49f10faa6731c36dbaa6590b05974e185c5b + CIPHERTEXT = split 0x2c487fa96f4090c56aa1b5be81918a934c9492878fb0cd686dcf8d17d86485454c51237bbd09205dcef1552f430dd098b9d827a694730c133a0222c77f540f9d5fc2d36af359583c9e3b49df884228a64de79b67f66207c8281360b99b214042ce61367ff97960e944453cd63679bb44708897d29bc5e70f9fc8f1f715143fbb00f7f5c1b7b161ec26d8d41d36fab0fa8a85c3ee6ce4d37007eb7a89d6753590 + PLAINTEXT = split 0x31fd5a307e279b2f34581e2c432379df8eccbaf79532938916711cd377540b9045373e47f2214b8f876040af733f6c9d8f03a7c58f8714d2fbb4c14af59c75b483adc718946ee907a18286cc4efd206789064b6f1b195f0d0d234468e4f00e6f1cad5cd3b9c0a643b3c0dd09280ff2e2a5929183409384dd72dc94e39687ea2b623d5d776700bd8b36e6130ffde966f134c4b1f35f29c5cc4a03297e1ccc9539 + +import submodule F_KAT_ECB { submodule P_AES_128_BlockCipher } as F_KAT_ECB_AES_128 (F_KAT) +import submodule F_KAT_ECB_AES_128::F_KAT { submodule P_KAT_ECB_AES_128_ENCRYPT_9 } as KAT_ECB_AES_128_ENCRYPT_9 +import submodule F_KAT_ECB_AES_128::F_KAT { submodule P_KAT_ECB_AES_128_DECRYPT_9 } as KAT_ECB_AES_128_DECRYPT_9 + +import submodule F_KAT_ECB { submodule P_AES_192_BlockCipher } as F_KAT_ECB_AES_192 (F_KAT) +import submodule F_KAT_ECB_AES_192::F_KAT { submodule P_KAT_ECB_AES_192_ENCRYPT_9 } as KAT_ECB_AES_192_ENCRYPT_9 +import submodule F_KAT_ECB_AES_192::F_KAT { submodule P_KAT_ECB_AES_192_DECRYPT_9 } as KAT_ECB_AES_192_DECRYPT_9 + +import submodule F_KAT_ECB { submodule P_AES_256_BlockCipher } as F_KAT_ECB_AES_256 (F_KAT) +import submodule F_KAT_ECB_AES_256::F_KAT { submodule P_KAT_ECB_AES_256_ENCRYPT_9 } as KAT_ECB_AES_256_ENCRYPT_9 +import submodule F_KAT_ECB_AES_256::F_KAT { submodule P_KAT_ECB_AES_256_DECRYPT_9 } as KAT_ECB_AES_256_DECRYPT_9 +``` + +```xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + ## Cipher Block Chaining (CBC) To better diffuse message blocks, CBC introduces an *initialization @@ -781,6 +966,152 @@ Passed 1 tests. Q.E.D. ``` +**Exercise**: Now define submodules that implement AES in CBC mode for +each AES key size. + +**Solution**: + +```cryptol +/** AES-128/CBC */ +submodule CBC_AES_128 = submodule CBC { submodule P_AES_128_BlockCipher } +/** AES-192/CBC */ +submodule CBC_AES_192 = submodule CBC { submodule P_AES_192_BlockCipher } +/** AES-256/CBC */ +submodule CBC_AES_256 = submodule CBC { submodule P_AES_256_BlockCipher } +``` + +Your solution should pass the following test vectors: + +```cryptol +submodule F_KAT_CBC where + import interface submodule I_BlockCipher + interface constraint fin b + + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } + + interface submodule I_KAT where + type n : # + type constraint fin n + + KEY: Key + IV: Block + PLAINTEXT : [n]Block + CIPHERTEXT : [n]Block + + submodule F_KAT where + import interface submodule I_KAT + + import submodule CBC { interface I_BlockCipher } + + property test_encrypt = encrypt KEY IV PLAINTEXT == CIPHERTEXT + property test_decrypt = decrypt KEY IV CIPHERTEXT == PLAINTEXT + +submodule P_KAT_CBC_AES_128_ENCRYPT_9 where + type n = 10 + KEY = 0x2c14413751c31e2730570ba3361c786b + IV = 0x1dbbeb2f19abb448af849796244a19d7 + PLAINTEXT = split 0x40d930f9a05334d9816fe204999c3f82a03f6a0457a8c475c94553d1d116693adc618049f0a769a2eed6a6cb14c0143ec5cccdbc8dec4ce560cfd206225709326d4de7948e54d603d01b12d7fed752fb23f1aa4494fbb00130e9ded4e77e37c079042d828040c325b1a5efd15fc842e44014ca4374bf38f3c3fc3ee327733b0c8aee1abcd055772f18dc04603f7b2c1ea69ff662361f2be0a171bbdcea1e5d3f + CIPHERTEXT = split 0x6be8a12800455a320538853e0cba31bd2d80ea0c85164a4c5c261ae485417d93effe2ebc0d0a0b51d6ea18633d210cf63c0c4ddbc27607f2e81ed9113191ef86d56f3b99be6c415a4150299fb846ce7160b40b63baf1179d19275a2e83698376d28b92548c68e06e6d994e2c1501ed297014e702cdefee2f656447706009614d801de1caaf73f8b7fa56cf1ba94b631933bbe577624380850f117435a0355b2b + +submodule P_KAT_CBC_AES_128_DECRYPT_9 where + type n = 10 + KEY = 0x97a1025529b9925e25bbe78770ca2f99 + IV = 0xd4b4eab92aa9637e87d366384ed6915c + CIPHERTEXT = split 0x22cdc3306fcd4d31ccd32720cbb61bad28d855670657c48c7b88c31f4fa1f93c01b57da90be63ead67d6a325525e6ed45083e6fb70a53529d1fa0f55653b942af59d78a2660361d63a7290155ac5c43312a25b235dacbbc863faf00940c99624076dfa44068e7c554c9038176953e571751dfc0954d41d113771b06466b1c8d13e0d4cb675ed58d1a619e1540970983781dc11d2dd8525ab5745958d615defda + PLAINTEXT = split 0xe8b89150d8438bf5b17449d6ed26bd72127e10e4aa57cad85283e8359e089208e84921649f5b60ea21f7867cbc9620560c4c6238db021216db453c9943f1f1a60546173daef2557c3cdd855031b353d4bf176f28439e48785c37d38f270aa4a6faad2baabcb0c0b2d1dd5322937498ce803ba1148440a52e227ddba4872fe4d81d2d76a939d24755adb8a7b8452ceed2d179e1a5848f316f5c016300a390bfa7 + +submodule P_KAT_CBC_AES_192_ENCRYPT_9 where + type n = 10 + KEY = 0x162ad50ee64a0702aa551f571dedc16b2c1b6a1e4d4b5eee + IV = 0x24408038161a2ccae07b029bb66355c1 + PLAINTEXT = split 0xbe8abf00901363987a82cc77d0ec91697ba3857f9e4f84bd79406c138d02698f003276d0449120bef4578d78fecabe8e070e11710b3f0a2744bd52434ec70015884c181ebdfd51c604a71c52e4c0e110bc408cd462b248a80b8a8ac06bb952ac1d7faed144807f1a731b7febcaf7835762defe92eccfc7a9944e1c702cffe6bc86733ed321423121085ac02df8962bcbc1937092eebf0e90a8b20e3dd8c244ae + CIPHERTEXT = split 0xc82cf2c476dea8cb6a6e607a40d2f0391be82ea9ec84a537a6820f9afb997b76397d005424faa6a74dc4e8c7aa4a8900690f894b6d1dca80675393d2243adac762f159301e357e98b724762310cd5a7bafe1c2a030dba46fd93a9fdb89cc132ca9c17dc72031ec6822ee5a9d99dbca66c784c01b0885cbb62e29d97801927ec415a5d215158d325f9ee689437ad1b7684ad33c0d92739451ac87f39ff8c31b84 + +submodule P_KAT_CBC_AES_192_DECRYPT_9 where + type n = 10 + KEY = 0x509baf46fb9de34281dafcc3db79593bffa8426904302688 + IV = 0xd6d86e0c82dd8788f4147a26f9a71c74 + CIPHERTEXT = split 0x6928299c52b4f047926f8a541529da2d6bbaa399143ced8efb77ab47409d9a953a386c7abd6026f49831c717627c2a5e77bd2d433d4d130dacd927ea0d13a23d01a7cf39c6716dafb6ed552410ef5d27fb947be2c8782eee7829196c7edcf151c65f9a01f54f8d20f38b7da4a7e83a2f0127d59d3e2405d8674fc9f41b604f788f4715f9d3624eee57f387bfadd18a1f905e839c26b8617482347fab6d08845a + PLAINTEXT = split 0x67d2dda6da26e21307973400600725727ae81415511772f4a09ad9903bcf90cc2c0dac58ba559a0109c54a9d6117b15bb574ca473e848047e9a54ee4abde76aff9849c44109d161f46442e1610d8b015cf36a010ed8efa3207fdfc8fcc548f145c027e44c5b0ec35c9886f4b9d6513a5bc10d0ea6bbbc26f54b183bcae27fb799d8872ff748fc459d55cfa255aae29d71b076d9b44c14d5ceba9332a763d9c94 + +submodule P_KAT_CBC_AES_256_ENCRYPT_9 where + type n = 10 + KEY = 0x48be597e632c16772324c8d3fa1d9c5a9ecd010f14ec5d110d3bfec376c5532b + IV = 0xd6d581b8cf04ebd3b6eaa1b53f047ee1 + PLAINTEXT = split 0x0c63d413d3864570e70bb6618bf8a4b9585586688c32bba0a5ecc1362fada74ada32c52acfd1aa7444ba567b4e7daaecf7cc1cb29182af164ae5232b002868695635599807a9a7f07a1f137e97b1e1c9dabc89b6a5e4afa9db5855edaa575056a8f4f8242216242bb0c256310d9d329826ac353d715fa39f80cec144d6424558f9f70b98c920096e0f2c855d594885a00625880e9dfb734163cecef72cf030b8 + CIPHERTEXT = split 0xfc5873e50de8faf4c6b84ba707b0854e9db9ab2e9f7d707fbba338c6843a18fc6facebaf663d26296fb329b4d26f18494c79e09e779647f9bafa87489630d79f4301610c2300c19dbf3148b7cac8c4f4944102754f332e92b6f7c5e75bc6179eb877a078d4719009021744c14f13fd2a55a2b9c44d18000685a845a4f632c7c56a77306efa66a24d05d088dcd7c13fe24fc447275965db9e4d37fbc9304448cd + +submodule P_KAT_CBC_AES_256_DECRYPT_9 where + type n = 10 + KEY = 0x87725bd43a45608814180773f0e7ab95a3c859d83a2130e884190e44d14c6996 + IV = 0xe49651988ebbb72eb8bb80bb9abbca34 + CIPHERTEXT = split 0x5b97a9d423f4b97413f388d9a341e727bb339f8e18a3fac2f2fb85abdc8f135deb30054a1afdc9b6ed7da16c55eba6b0d4d10c74e1d9a7cf8edfaeaa684ac0bd9f9d24ba674955c79dc6be32aee1c260b558ff07e3a4d49d24162011ff254db8be078e8ad07e648e6bf5679376cb4321a5ef01afe6ad8816fcc7634669c8c4389295c9241e45fff39f3225f7745032daeebe99d4b19bcb215d1bfdb36eda2c24 + PLAINTEXT = split 0xbfe5c6354b7a3ff3e192e05775b9b75807de12e38a626b8bf0e12d5fff78e4f1775aa7d792d885162e66d88930f9c3b2cdf8654f56972504803190386270f0aa43645db187af41fcea639b1f8026ccdd0c23e0de37094a8b941ecb7602998a4b2604e69fc04219585d854600e0ad6f99a53b2504043c08b1c3e214d17cde053cbdf91daa999ed5b47c37983ba3ee254bc5c793837daaa8c85cfc12f7f54f699f + +import submodule F_KAT_CBC { submodule P_AES_128_BlockCipher } as F_KAT_CBC_AES_128 (F_KAT) +import submodule F_KAT_CBC_AES_128::F_KAT { submodule P_KAT_CBC_AES_128_ENCRYPT_9 } as KAT_CBC_AES_128_ENCRYPT_9 +import submodule F_KAT_CBC_AES_128::F_KAT { submodule P_KAT_CBC_AES_128_DECRYPT_9 } as KAT_CBC_AES_128_DECRYPT_9 + +import submodule F_KAT_CBC { submodule P_AES_192_BlockCipher } as F_KAT_CBC_AES_192 (F_KAT) +import submodule F_KAT_CBC_AES_192::F_KAT { submodule P_KAT_CBC_AES_192_ENCRYPT_9 } as KAT_CBC_AES_192_ENCRYPT_9 +import submodule F_KAT_CBC_AES_192::F_KAT { submodule P_KAT_CBC_AES_192_DECRYPT_9 } as KAT_CBC_AES_192_DECRYPT_9 + +import submodule F_KAT_CBC { submodule P_AES_256_BlockCipher } as F_KAT_CBC_AES_256 (F_KAT) +import submodule F_KAT_CBC_AES_256::F_KAT { submodule P_KAT_CBC_AES_256_ENCRYPT_9 } as KAT_CBC_AES_256_ENCRYPT_9 +import submodule F_KAT_CBC_AES_256::F_KAT { submodule P_KAT_CBC_AES_256_DECRYPT_9 } as KAT_CBC_AES_256_DECRYPT_9 +``` + +```xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + ## Cipher Feedback (CFB) CFB can take any of various forms, including a full-block mode or one @@ -835,7 +1166,8 @@ given `ct_i = (encipher key iv) ^ pt_i`, solve for `pt_i`. **Exercise**: Define a submodule that implements DES (a weak block cipher) in full-block CFB mode (a cipher mode). Add a docstring with -a prominent warning not to use it in production. +a prominent warning not to use it in production. Check these against +test vectors in [3]. **Solution**: @@ -844,8 +1176,6 @@ a prominent warning not to use it in production. submodule CFB_DES = submodule CFB { submodule P_DES_BlockCipher } ``` -Your solution should pass the following test vectors [3]: - ```cryptol submodule CFB_DES_Test where import submodule CFB_DES as CFB_DES @@ -871,6 +1201,150 @@ Passed 1 tests. Q.E.D. ``` +**Exercise**: Now define submodules that implement AES in CFB mode for +each AES key size, and verify these against some test vectors in [4]. + +**Solution**: + +```cryptol +/** AES-128/CFB */ +submodule CFB_AES_128 = submodule CFB { submodule P_AES_128_BlockCipher } +/** AES-192/CFB */ +submodule CFB_AES_192 = submodule CFB { submodule P_AES_192_BlockCipher } +/** AES-256/CFB */ +submodule CFB_AES_256 = submodule CFB { submodule P_AES_256_BlockCipher } +``` + +```cryptol +submodule F_KAT_CFB where + import interface submodule I_BlockCipher + interface constraint fin b + + import submodule F_BlockCipher_Aliases { interface I_BlockCipher } + + interface submodule I_KAT where + type n : # + type constraint fin n + + KEY: Key + IV: Block + PLAINTEXT : [n]Block + CIPHERTEXT : [n]Block + + submodule F_KAT where + import interface submodule I_KAT + + import submodule CFB { interface I_BlockCipher } + + property test_encrypt = encrypt KEY IV PLAINTEXT == CIPHERTEXT + property test_decrypt = decrypt KEY IV CIPHERTEXT == PLAINTEXT + +submodule P_KAT_CFB_AES_128_ENCRYPT_9 where + type n = 10 + KEY = 0x7cb81fc4b203b0fa9bec49759bd515c2 + IV = 0x4d5e2fa3bf73f488b3e7e125f03dfbbe + PLAINTEXT = split 0x362789b376d85eb8181d4eeea52d42e873ce7741c11a2f820383a7457b15489b09fb21ac4445959dc9e851b7d40682c50d7044bda46a5da39fae2bab73b3db9ed22edc7ec5da936dfa7451cb5f0a829ff0762738cc2686148f1e1f00dc3fe38139c9a173201fc1f052ca34736fc1ab3dc4e707f864d6119b7adb6c8ddd41c80de5d357d17e9c85ed7af1e4f72cb2656932ccce469202680109eef89a9f42f10a + CIPHERTEXT = split 0xac23259f68d4f82604cabd2e4237821c8b6c0aad0dfb1120b6b057223c994d62b5c6f63a25edbb797cd299f81ccb86d50134ad26107865142004c2d9d52fe3f91acf7b9b8111c8b4e14b05b173730e7b812036029846f1c1c6ffb30f6abcfc3e1ea631480e0d0bda106bb87319fdae09a11b89e8dde625d53a19c65ae58fbe3f4bcbc3c99af05cb0a7cc4b793d8cdb1cfa3173ede595c8c561f92c3fe3638b8d + +submodule P_KAT_CFB_AES_128_DECRYPT_9 where + type n = 10 + KEY = 0xaef49da33f538ee66e178d4b6121055d + IV = 0x842566e68b61ff7bf001f2642da62f64 + CIPHERTEXT = split 0x6625811419bdee71535f597f7c228bafd890fd69b805a699ed58116a82bdb251abea7a4ef879a96fce8ee49518b9877a3a1e3cf346d3cd73738936d1cb6fff4b2353c8ca500a26689813ad2f67774e2343f3e4830259094d3b342e00faabeba5b8a893108a390c649836ddd5d12489b2dd591ca25361032e2da1207f793a1e69513002a90ccc036bb63e9c10be87df2def960cd7a1b1621e311735d7aee4419f + PLAINTEXT = split 0x415991f65e1a95040cef9960556f61e617827c30c74bf353cdd86173dbe4cc983a2ee6bc8ca6cfb71121e7b0d0178f2e13445c710dcc176b781201971171f7489f18faf110f39accd1cf08c85a958d7698b116f1c0d75812ac9b0b39aee7f7159ccad8fdae9b99f2d695eacf12c6469d5b51a34de26eac73613dcb2f77122cb1f8dd5162786a12052dc7b6dea6acc4989dcc7eafd9374f6c29697c74749ef16d + +submodule P_KAT_CFB_AES_192_ENCRYPT_9 where + type n = 10 + KEY = 0xaffe25a7b28fe7427aa69a89cb87bc0fb68c940d63d319b3 + IV = 0x89e612f77ef55ee86935d90a8c7466c2 + PLAINTEXT = split 0xb5719702560b8b214c73b9a2ea7a43707b10e0b79152d1019ac4179fb4dae34ac3be4e0ac04d4a575462d87ea5587c4770caeed5589d13cd7d412bbb51334cb1a7c70f310d24894c5c907d0c8deecf10ce843c76d50249fe75c0796b6f48c32d8a14433ee699304a8d840e124b432512c0c73161b3885bdaa9ca6879b61f3107942e53faf2b227970ec6f559865f64966c1a3560983831aa42e660abd0c27c88 + CIPHERTEXT = split 0x5bc958b594f0e4928cee7c019ee1884bab9b6956f40c47f24c1b8ef587d68b175dbc36226b7d95e573702f5b0dc969a8c59b82816762847275c95234e3c74fba50841202c27264131ab03773b4e28ea7c68ea946efe2e2d9a89643d98c5dddd075098a930c741b535ba96ea0a08ad0cf68d11e8e98e26d0a79d3a5ef65dd137cc6c82a4e8edb1a63e9bc6e8705cef59b4b4c391924ffd33ace99808cc163272d + +submodule P_KAT_CFB_AES_192_DECRYPT_9 where + type n = 10 + KEY = 0xdcc8702142b29e1529f23c5c3766464f6be0d2fbb16e4682 + IV = 0xe0818769d77f2315924cf81a3691e275 + CIPHERTEXT = split 0x1a22b1bf5775d43c66f1a73084eeefdb3ed24cebd9e2bef2f05867165fb5930b6058f53ef4503353856fa6d2c99f5b1de9795da6e314365e2d1bb3719b23e830823b744e1ec406503183203fedf41ba014e16ca65e3425a51b0abfca1908160ac8f2b5589c79541bb3559fdfb894394a0732015211e994ae024a138aa20d267f79a640c23719259c530eaa1af128bc050993a414c6dc89612c06371afeda1f79 + PLAINTEXT = split 0xcb550111bf0a03eaf4f49af214fcc05a32972d7b4dede3e9812a27ef80d680188119ead562313e400fe0a0fffbb88c55a42bd681d5c93a8a61ba909058e62d99fa109cfd49935b150862a8aab2c301b9b0a9157c838491cd737af438cb66b1f20420200dbc56aa66552ce4be04cace4ace5bfbc617e3b27f40ec6dbc85a42b410dcb7ea0b78d472297d9b98875d636b8ef08c254ec9bd05bfda01bb38e8beb6a + +submodule P_KAT_CFB_AES_256_ENCRYPT_9 where + type n = 10 + KEY = 0x04c4499a7a10a319992258726191f8087af1182ebd48f43bf6c158dce8a5c0af + IV = 0x7fc268d6599fcdef7457cdbde5b9c5b6 + PLAINTEXT = split 0x04a66003969bbe72eedb638fb188bea1564d96df873994e2cacd02596053d2ff6d72e2ca5a007e65781727e1e66b92a8b4daf4b248457dfb019f22341b43a0b8d38bbec3cb9ed2deaf89869e1992dc8eb2c8706a9bdffe8e0b3b23ad394b68c67c187c54b01ec1c6dd5a0a2e13a4a43d983f936f46af1d832eca8d2e81123110b142a5e7d327530348b13bfa9c4af09cae66e329115ce4c2b374e61ebc41037e + CIPHERTEXT = split 0x5235639833e022fa28ab90d2820abec07d2c3185a9c6e17618b8b68fb221bb5db242ad18c535d7694d1306ec26afdd459a53bb460485249c4f5fa73d41a0d6cc56988949f7ad82259fe3a3226f0bd8d479f40a2bd36400075981206f50f268c921338e91613b3a3c0cee1c0ff6c4e1d30c4ca0f9a311e9586d9ed29dcc876fd09eb6405023ebc180c5635d4c7987286080847d3a74e9deeafaeec0697ca8db75 + +submodule P_KAT_CFB_AES_256_DECRYPT_9 where + type n = 10 + KEY = 0x4d3e4cec63edafbb4d600007e95124f554b352ada4966a60da4c898912cada73 + IV = 0x05aef5ccf46298e0feb58d77122b58d9 + CIPHERTEXT = split 0xfedc2708ce2e2471ab8e66c69a3451a94380da0e5e9998fa68541d899a5786361b7e5157757d6fe746c79a8838afe9c832cda2a4d0a44f4b1181142450a63f1176a821f66d161d75d85bfefc01e68d021288648d891dfdf8e66e0ef3a65619cb75243eeca04155a4c9133929de2066de4c77c7d26f4cc9894de2b40085ae3beb82b95241f4463ffa81b5f418b7a79ce44663747a6c78dc87b0a4ae52d3f5cef9 + PLAINTEXT = split 0x95d6b83e1c10c721e0f0c35907b3a4e35c2794a6a8234874440be7a795dc8e2f7ec5cf739d0bb13b1fc51cf5d4d27d2ed4b93c11893c7b9a649b22cbbc96a8cd5847d135c43d1a11855811b82cffbd2287e6c55f45d124d47d549218c1ea0049281dd539a60cdbb80549db3af3b9f8d4ca127efcb5cde7ecc98e008f1edeff6980f172652806ff9395af7a62f88abaaf8974ebef1a02d78e4bd52149fa1ee183 + +import submodule F_KAT_CFB { submodule P_AES_128_BlockCipher } as F_KAT_CFB_AES_128 (F_KAT) +import submodule F_KAT_CFB_AES_128::F_KAT { submodule P_KAT_CFB_AES_128_ENCRYPT_9 } as KAT_CFB_AES_128_ENCRYPT_9 +import submodule F_KAT_CFB_AES_128::F_KAT { submodule P_KAT_CFB_AES_128_DECRYPT_9 } as KAT_CFB_AES_128_DECRYPT_9 + +import submodule F_KAT_CFB { submodule P_AES_192_BlockCipher } as F_KAT_CFB_AES_192 (F_KAT) +import submodule F_KAT_CFB_AES_192::F_KAT { submodule P_KAT_CFB_AES_192_ENCRYPT_9 } as KAT_CFB_AES_192_ENCRYPT_9 +import submodule F_KAT_CFB_AES_192::F_KAT { submodule P_KAT_CFB_AES_192_DECRYPT_9 } as KAT_CFB_AES_192_DECRYPT_9 + +import submodule F_KAT_CFB { submodule P_AES_256_BlockCipher } as F_KAT_CFB_AES_256 (F_KAT) +import submodule F_KAT_CFB_AES_256::F_KAT { submodule P_KAT_CFB_AES_256_ENCRYPT_9 } as KAT_CFB_AES_256_ENCRYPT_9 +import submodule F_KAT_CFB_AES_256::F_KAT { submodule P_KAT_CFB_AES_256_DECRYPT_9 } as KAT_CFB_AES_256_DECRYPT_9 +``` + +```xcryptol-session +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_ENCRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_ENCRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_DECRYPT_9::test_encrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_DECRYPT_9::test_decrypt +Using exhaustive testing. +Passed 1 tests. +Q.E.D. +``` + ## Counter (CTR) Mode (Multiple Interfaces and Interface Constraints) Counter mode produces a stream cipher by applying a block cipher to @@ -953,13 +1427,6 @@ it, and verify the test vectors. **Exercise**: Feel free to specify other basic block cipher modes such as PCBC, OFB, segmented CFB, etc. -**TODO**: Introduce authenticated cipher modes, a positive -authentication property, and resilience properties (tamper, spoofing). - -**TODO**: Get to the point where (sub)modules can import interfaces and -specify properties assumed about their implementations. Use these to -generate "contracts" and reusable test cases. - # Properties and Verification (Interface Reuse) [2] defines the *CIA triad* of: @@ -995,7 +1462,8 @@ using *test vectors*, e.g.: ```cryptol import submodule P_Simon_32_64_BlockCipher as S_32_64 -import submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } as S_32_64 +submodule S_32_64_Aliases = submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } +import submodule S_32_64_Aliases as S_32_64 S_32_64_test_key = 0x94eeea8b1f2ada84 S_32_64_test_pt = 0xadf10331 @@ -1014,13 +1482,10 @@ recover a cryptographic key from known or chosen plaintext: ```xcryptol-session // Try to recover `test_key` from known test plaintext/ciphertext -// labs::ModuleSystem> :sat \key -> S_32_64::encipher key test_pt == test_ct +// labs::ModuleSystem> :sat \key -> S_32_64::encipher key S_32_64_test_pt == S_32_64_test_ct +// ... ``` -**TODO**: Introduce exercises to check `mode_recovery` for various -block cipher modes, and show how to use SAW to formally verify these -using a block cipher's `block_recovery` property when applicable. - # Conclusion In this module, you learned how to define and reuse interfaces and @@ -1044,6 +1509,10 @@ complex nested module with multiple ciphers, modes, and properties. 2 December 1980. https://csrc.nist.gov/pubs/fips/81/final +[4] NIST Advanced Encryption Standard Algorithm Verification System + AES Multiblock Message Test (MMT) Sample Vectors + https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmmt.zip + # Solicitation How was your experience with this lab? Suggestions are welcome in the diff --git a/labs/SimonSpeck/SimonSpeck.md b/labs/SimonSpeck/SimonSpeck.md index a85ab13d..b7592b07 100644 --- a/labs/SimonSpeck/SimonSpeck.md +++ b/labs/SimonSpeck/SimonSpeck.md @@ -178,6 +178,8 @@ Here is the header for the `Simon.cry` base module: ```example module labs::SimonSpeck::Simon::Simon where +module labs::SimonSpeck::Simon::Simon where + parameter /** word size */ diff --git a/labs/SimonSpeck/SpeckAnswers/Speck.cry b/labs/SimonSpeck/SpeckAnswers/Speck.cry index cff8eafb..c458da26 100644 --- a/labs/SimonSpeck/SpeckAnswers/Speck.cry +++ b/labs/SimonSpeck/SpeckAnswers/Speck.cry @@ -17,8 +17,17 @@ parameter type blockSize = 2 * n type keySize = m * n +encrypt : [keySize] -> [blockSize] -> [blockSize] encrypt K P = join (last (encryptList K P)) +// ADDITIONAL EXERCISE (decryption and properties) +decrypt : [keySize] -> [blockSize] -> [blockSize] +decrypt K C = join (last (decryptList K C)) + +decrypt_inverts_encrypt : [keySize] -> [blockSize] -> Bool +property decrypt_inverts_encrypt K P = + decrypt K (encrypt K P) == P + private /** @@ -31,23 +40,46 @@ private where x' = ((x >>> a) + y) ^ k y' = (y <<< b) ^ x' - + KeyScheduleRound : [m][n] -> [n] -> [m][n] KeyScheduleRound (Ls # [li] # [ki]) i = S' where [l', ki'] = Rk [li, ki] i S' = [l'] # Ls # [ki'] - + KeySchedule : [keySize] -> [T][n] KeySchedule K = Ks where S0 = split K Ss = scanl KeyScheduleRound S0 [0 .. T-2] Ks = last (transpose Ss) - + encryptList: [keySize] -> [blockSize] -> [T+1][2][n] encryptList K P = Cs where Ps = split P Ks = KeySchedule K Cs = scanl Rk Ps Ks + + // ADDITIONAL EXERCISE (decryption and properties) + + Rk' : [2][n] -> [n] -> [2][n] + Rk' [x', y'] k = [x, y] + where + y = (y' ^ x') >>> b + x = ((x' ^ k) - y) <<< a + + decryptList: [keySize] -> [blockSize] -> [T+1][2][n] + decryptList K C = Ps + where + Cs = split C + Ks = KeySchedule K + Ps = scanl Rk' Cs (reverse Ks) + + Rk'_inverts_Rk : [2][n] -> [n] -> Bool + property Rk'_inverts_Rk [x, y] k = + Rk' (Rk [x, y] k) k == [x, y] + + decryptList_inverts_encryptList : [keySize] -> [blockSize] -> Bool + property decryptList_inverts_encryptList K P = + join (last (decryptList K (join (last (encryptList K P))))) == P From 842abdd82ac1882cbf55dfcd30c3f26d05c3cafb Mon Sep 17 00:00:00 2001 From: --get-all Date: Fri, 8 Dec 2023 01:49:11 +0000 Subject: [PATCH 05/15] Remove properties section and correct some formatting --- labs/NewModuleSystem/NewModuleSystem.md | 142 ++++++------------ .../NewModuleSystem/NewModuleSystemAnswers.md | 130 +++++----------- 2 files changed, 80 insertions(+), 192 deletions(-) diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index c31af20e..2d4ddd2e 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -119,7 +119,7 @@ block cipher over some number of blocks (or sub-blocks). # Interfaces Using prior techniques for parameterized modules, we might start each -mode's (sub)module with a parameter block (commented out here because +mode's (sub)module with a `parameter` block (commented out here because that's not how we'll finish...): (Here, we use the terms `encipher` and `decipher` to distinguish block @@ -143,7 +143,7 @@ parameter ``` But this approach already repeats itself, and we'd have to start each -(sub)module with the `parameter`s in this block (along with any others +(sub)module with the parameters in this block (along with any others unique to the mode). It would be better to reuse the common block cipher parameters, and we can do so by defining an **interface** with reusable **type aliases**: @@ -197,9 +197,9 @@ define additional type constraints on their implementations. As noted above, interfaces do not (yet) export their type aliases, but it would be nice for users to have access to these. We'll quickly do this with a functor that imports the same interface and exports the -(copied) type aliases. This also demonstrates an **interface alias** -to distinguish the interface's aliases (which would otherwise conflict -despite not being accessible) from the functor's: +(copied) type aliases. This also demonstrates an *interface alias* +to distinguish the interface's type aliases (which would otherwise +conflict despite not being accessible) from the functor's: ```cryptol submodule F_BlockCipher_Aliases where @@ -224,10 +224,12 @@ Then a `mode` can... ...and reuse these aliases in its own definitions. -Of course, parameterizable block cipher modes need block ciphers! We -recently implemeented the Simon and Speck block ciphers, which are -themselves parameterized. We just need to adapt these to our fancy -new interface... +Of course, parameterizable block cipher modes need block ciphers! In +the previous lab on +[Parameterized Modules](../SimonSpeck/SimonSpeck.md), we implemeented +the Simon and Speck block ciphers, which are themselves parameterized. +To use these for our block cipher modes, we just need to adapt them to +our fancy new interface... ## Explicit Instantiation @@ -408,9 +410,9 @@ generates a **signature** alongside the ciphertext.) ## Electronic Codebook (ECB) ECB is a simple block cipher mode that just enciphers each block, with -no intermediate transformations to **diffuse** the message, a weakness -that disqualifies it for use in security-critical settings. But just -as a pedagogical exercise, we can define ECB in Cryptol... +no intermediate transformations to **diffuse** the message -- a +weakness that disqualifies it for use in security-critical settings. +But just as a pedagogical exercise, we can define ECB in Cryptol... **Exercise**: In the specification of ECB mode below, define `decrypt` so that using the same shared key on an encrypted ciphertext message @@ -459,7 +461,7 @@ prominent warning not to use it in production. // submodule ECB_DES = submodule ... { submodule P_DES_BlockCipher } // Replace with your definition and uncomment. ``` -Your solution should pass the following test vectors [3]: +Your solution should pass the following test vectors [2]: ```cryptol /** ECB/DES Tests */ @@ -511,7 +513,7 @@ Q.E.D. block cipher) in ECB (a weak cipher mode). Add docstrings with prominent warnings not to use them in production. -Your solution should pass the following test vectors [4]: +Your solution should pass the following test vectors [3]: ```cryptol // Let's use the new module system to generate groups of related properties... @@ -702,18 +704,18 @@ important property of (useful) block ciphers, defined as Working backward from our generator expression for `encrypt`... -`ct@i = encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1)))` -(Apply `decipher key` to both sides.) -`decipher key (ct@i) = decipher key (encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1))))` -(Apply `block_recovery` on right.) -`decipher key (ct@i) = pt@i ^ ct'` -(Apply `(^) ct'` to both sides, where `ct'` is the `if`-expression.) -`decipher key (ct@i) ^ ct' = pt@i ^ ct' ^ ct' -(Apply `xor_inv_r` on right.) -`decipher key (ct@i) ^ ct' = pt@i` -(Flip sides for assignment to plaintext.) -`pt@i = decipher key (ct@i) ^ ct'` -(Substitute the `if`-expression back in for `ct'`.) +`ct@i = encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1)))` +(Apply `decipher key` to both sides.) +`decipher key (ct@i) = decipher key (encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1))))` +(Apply `block_recovery` on right.) +`decipher key (ct@i) = pt@i ^ ct'` +(Apply `(^) ct'` to both sides, where `ct'` is the `if`-expression.) +`decipher key (ct@i) ^ ct' = pt@i ^ ct' ^ ct'` +(Apply `xor_inv_r` on right.) +`decipher key (ct@i) ^ ct' = pt@i` +(Flip sides for assignment to plaintext.) +`pt@i = decipher key (ct@i) ^ ct'` +(Substitute the `if`-expression back in for `ct'`.) `pt@i = decipher key (ct@i) ^ (if i == 0 then iv else ct@(i-1)))` **Note**: Could you define `decrypt` without an initialization vector? @@ -741,7 +743,7 @@ warning not to use it in production. // submodule CBC_DES = ... // Replace with your definition and uncomment. ``` -Your solution should pass the following test vectors [3]: +Your solution should pass the following test vectors [2]: ```cryptol submodule CBC_DES_Test where @@ -771,7 +773,7 @@ Q.E.D. **Exercise**: Now define submodules that implement AES in CBC mode for each AES key size. -Your solution should pass the following test vectors: +Your solution should pass the following test vectors [3]: ```cryptol submodule F_KAT_CBC where @@ -948,15 +950,26 @@ given `ct_i = (encipher key iv) ^ pt_i`, solve for `pt_i`. **Exercise**: Define a submodule that implements DES (a weak block cipher) in full-block CFB mode (a cipher mode). Add a docstring with a prominent warning not to use it in production. Check these against -test vectors in [3]. - -**Solution**: +test vectors in [2]. ```cryptol // /** ... */ // submodule CFB_DES = ... // Replace with your definition and uncomment. ``` +**Exercise**: Now define submodules that implement AES in CFB mode for +each AES key size, and verify these against some test vectors in [3]. + +```cryptol +// /** ... */ +// submodule CFB_AES_128 = ... // Replace with your definition and uncomment. +// /** ... */ +// submodule CFB_AES_192 = ... // Replace with your definition and uncomment. +// /** ... */ +// submodule CFB_AES_256 = ... // Replace with your definition and uncomment. +``` + + ## Counter (CTR) Mode (Multiple Interfaces and Interface Constraints) Counter mode produces a stream cipher by applying a block cipher to @@ -1033,64 +1046,6 @@ it, and verify the test vectors. **Exercise**: Feel free to specify other basic block cipher modes such as PCBC, OFB, segmented CFB, etc. -# Properties and Verification (Interface Reuse) - -[2] defines the *CIA triad* of: - - *confidentiality* - - *integrity* - - *availability* - -In these terms, a cipher's purpose is to provide confidentiality (for -authorized persons to securely exchange message so that they are not -easily recoverable by unauthorized persons) and availability (messages -need to remain readable by authorized persons). - -In particular, at the expense of repeating ourselves, a cipher must -satisfy this familiar property, which you may have already used to -understand how to specify `decrypt` for block cipher modes: - -```cryptol -// property block_recovery key pt = decipher key (encipher key pt) == pt -``` - -In other words, a cipher must remain *available* to those with a shared -symmetric key. This must also hold true for cipher modes, e.g. for a -cipher mode with an initialization vector: - -```cryptol -// property mode_recovery = decrypt key iv (encrypt key iv pt) == pt -``` - -Another prerequisite of availability is that authorized parties agree -on how the cipher operates. Cryptol expresses algorithms as rigorous -formal specifications, but users more commonly confirm this agreement -using *test vectors*, e.g.: - -```cryptol -import submodule P_Simon_32_64_BlockCipher as S_32_64 -submodule S_32_64_Aliases = submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } -import submodule S_32_64_Aliases as S_32_64 - -S_32_64_test_key = 0x94eeea8b1f2ada84 -S_32_64_test_pt = 0xadf10331 -S_32_64_test_ct = 0x391e7f1a - -property test_simon_32_64_enc = S_32_64::encipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct -property test_simon_32_64_dec = S_32_64::decipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct -``` - -A strong cipher must resist cryptanalytic attacks of varying -sophistication. Cryptol and SMT solvers are not designed to perform -rigorous cryptanalysis, but can run some quick sanity checks. - -For example, it should not be easy to (ab)use SMT solvers to quickly -recover a cryptographic key from known or chosen plaintext: - -```xcryptol-session -// Try to recover `test_key` from known test plaintext/ciphertext -// labs::ModuleSystem> :sat \key -> S_32_64::encipher key test_pt == test_ct -``` - # Conclusion In this module, you learned how to define and reuse interfaces and @@ -1104,17 +1059,12 @@ complex nested module with multiple ciphers, modes, and properties. December 2001. https://csrc.nist.gov/pubs/sp/800/38/a/final -[2] NIST SP 800-12 Rev. 1. - "An Introduction to Information Security". - June 2017. - https://csrc.nist.gov/pubs/sp/800/12/r1/final - -[3] NIST FIPS 81 (withdrawn). +[2] NIST FIPS 81 (withdrawn). "DES MODES OF OPERATION" 2 December 1980. https://csrc.nist.gov/pubs/fips/81/final -[4] NIST Advanced Encryption Standard Algorithm Verification System +[3] NIST Advanced Encryption Standard Algorithm Verification System AES Multiblock Message Test (MMT) Sample Vectors https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmmt.zip diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md index 7ca8835b..991dfdf6 100644 --- a/labs/NewModuleSystem/NewModuleSystemAnswers.md +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -119,7 +119,7 @@ block cipher over some number of blocks (or sub-blocks). # Interfaces Using prior techniques for parameterized modules, we might start each -mode's (sub)module with a parameter block (commented out here because +mode's (sub)module with a `parameter` block (commented out here because that's not how we'll finish...): (Here, we use the terms `encipher` and `decipher` to distinguish block @@ -143,7 +143,7 @@ parameter ``` But this approach already repeats itself, and we'd have to start each -(sub)module with the `parameter`s in this block (along with any others +(sub)module with the parameters in this block (along with any others unique to the mode). It would be better to reuse the common block cipher parameters, and we can do so by defining an **interface** with reusable **type aliases**: @@ -197,9 +197,9 @@ define additional type constraints on their implementations. As noted above, interfaces do not (yet) export their type aliases, but it would be nice for users to have access to these. We'll quickly do this with a functor that imports the same interface and exports the -(copied) type aliases. This also demonstrates an **interface alias** -to distinguish the interface's aliases (which would otherwise conflict -despite not being accessible) from the functor's: +(copied) type aliases. This also demonstrates an *interface alias* +to distinguish the interface's type aliases (which would otherwise +conflict despite not being accessible) from the functor's: ```cryptol submodule F_BlockCipher_Aliases where @@ -224,10 +224,12 @@ Then a `mode` can... ...and reuse these aliases in its own definitions. -Of course, parameterizable block cipher modes need block ciphers! We -recently implemeented the Simon and Speck block ciphers, which are -themselves parameterized. We just need to adapt these to our fancy -new interface... +Of course, parameterizable block cipher modes need block ciphers! In +the previous lab on +[Parameterized Modules](../SimonSpeck/SimonSpeck.md), we implemeented +the Simon and Speck block ciphers, which are themselves parameterized. +To use these for our block cipher modes, we just need to adapt them to +our fancy new interface... ## Explicit Instantiation @@ -472,9 +474,9 @@ generates a **signature** alongside the ciphertext.) ## Electronic Codebook (ECB) ECB is a simple block cipher mode that just enciphers each block, with -no intermediate transformations to **diffuse** the message, a weakness -that disqualifies it for use in security-critical settings. But just -as a pedagogical exercise, we can define ECB in Cryptol... +no intermediate transformations to **diffuse** the message -- a +weakness that disqualifies it for use in security-critical settings. +But just as a pedagogical exercise, we can define ECB in Cryptol... **Exercise**: In the specification of ECB mode below, define `decrypt` so that using the same shared key on an encrypted ciphertext message @@ -581,7 +583,7 @@ prominent warning not to use it in production. submodule ECB_DES = submodule ECB { submodule P_DES_BlockCipher } ``` -Your solution should pass the following test vectors [3]: +Your solution should pass the following test vectors [2]: ```cryptol /** ECB/DES Tests */ @@ -644,7 +646,7 @@ submodule ECB_AES_192 = submodule ECB { submodule P_AES_192_BlockCipher } submodule ECB_AES_256 = submodule ECB { submodule P_AES_256_BlockCipher } ``` -Your solution should pass the following test vectors [4]: +Your solution should pass the following test vectors [3]: ```cryptol // Let's use the new module system to generate groups of related properties... @@ -841,18 +843,18 @@ important property of (useful) block ciphers, defined as Working backward from our generator expression for `encrypt`... -`ct@i = encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1)))` -(Apply `decipher key` to both sides.) -`decipher key (ct@i) = decipher key (encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1))))` -(Apply `block_recovery` on right.) -`decipher key (ct@i) = pt@i ^ ct'` -(Apply `(^) ct'` to both sides, where `ct'` is the `if`-expression.) -`decipher key (ct@i) ^ ct' = pt@i ^ ct' ^ ct' -(Apply `xor_inv_r` on right.) -`decipher key (ct@i) ^ ct' = pt@i` -(Flip sides for assignment to plaintext.) -`pt@i = decipher key (ct@i) ^ ct'` -(Substitute the `if`-expression back in for `ct'`.) +`ct@i = encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1)))` +(Apply `decipher key` to both sides.) +`decipher key (ct@i) = decipher key (encipher key (pt@i ^ (if i == 0 then iv else ct@(i-1))))` +(Apply `block_recovery` on right.) +`decipher key (ct@i) = pt@i ^ ct'` +(Apply `(^) ct'` to both sides, where `ct'` is the `if`-expression.) +`decipher key (ct@i) ^ ct' = pt@i ^ ct' ^ ct'` +(Apply `xor_inv_r` on right.) +`decipher key (ct@i) ^ ct' = pt@i` +(Flip sides for assignment to plaintext.) +`pt@i = decipher key (ct@i) ^ ct'` +(Substitute the `if`-expression back in for `ct'`.) `pt@i = decipher key (ct@i) ^ (if i == 0 then iv else ct@(i-1)))` **Note**: Could you define `decrypt` without an initialization vector? @@ -939,7 +941,7 @@ warning not to use it in production. submodule CBC_DES = submodule CBC { submodule P_DES_BlockCipher } ``` -Your solution should pass the following test vectors [3]: +Your solution should pass the following test vectors [2]: ```cryptol submodule CBC_DES_Test where @@ -980,7 +982,7 @@ submodule CBC_AES_192 = submodule CBC { submodule P_AES_192_BlockCipher } submodule CBC_AES_256 = submodule CBC { submodule P_AES_256_BlockCipher } ``` -Your solution should pass the following test vectors: +Your solution should pass the following test vectors [3]: ```cryptol submodule F_KAT_CBC where @@ -1167,7 +1169,7 @@ given `ct_i = (encipher key iv) ^ pt_i`, solve for `pt_i`. **Exercise**: Define a submodule that implements DES (a weak block cipher) in full-block CFB mode (a cipher mode). Add a docstring with a prominent warning not to use it in production. Check these against -test vectors in [3]. +test vectors in [2]. **Solution**: @@ -1202,7 +1204,7 @@ Q.E.D. ``` **Exercise**: Now define submodules that implement AES in CFB mode for -each AES key size, and verify these against some test vectors in [4]. +each AES key size, and verify these against some test vectors in [3]. **Solution**: @@ -1427,65 +1429,6 @@ it, and verify the test vectors. **Exercise**: Feel free to specify other basic block cipher modes such as PCBC, OFB, segmented CFB, etc. -# Properties and Verification (Interface Reuse) - -[2] defines the *CIA triad* of: - - *confidentiality* - - *integrity* - - *availability* - -In these terms, a cipher's purpose is to provide confidentiality (for -authorized persons to securely exchange message so that they are not -easily recoverable by unauthorized persons) and availability (messages -need to remain readable by authorized persons). - -In particular, at the expense of repeating ourselves, a cipher must -satisfy this familiar property, which you may have already used to -understand how to specify `decrypt` for block cipher modes: - -```cryptol -// property block_recovery key pt = decipher key (encipher key pt) == pt -``` - -In other words, a cipher must remain *available* to those with a shared -symmetric key. This must also hold true for cipher modes, e.g. for a -cipher mode with an initialization vector: - -```cryptol -// property mode_recovery = decrypt key iv (encrypt key iv pt) == pt -``` - -Another prerequisite of availability is that authorized parties agree -on how the cipher operates. Cryptol expresses algorithms as rigorous -formal specifications, but users more commonly confirm this agreement -using *test vectors*, e.g.: - -```cryptol -import submodule P_Simon_32_64_BlockCipher as S_32_64 -submodule S_32_64_Aliases = submodule F_BlockCipher_Aliases { submodule P_Simon_32_64_BlockCipher } -import submodule S_32_64_Aliases as S_32_64 - -S_32_64_test_key = 0x94eeea8b1f2ada84 -S_32_64_test_pt = 0xadf10331 -S_32_64_test_ct = 0x391e7f1a - -property test_simon_32_64_enc = S_32_64::encipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct -property test_simon_32_64_dec = S_32_64::decipher S_32_64_test_key S_32_64_test_pt == S_32_64_test_ct -``` - -A strong cipher must resist cryptanalytic attacks of varying -sophistication. Cryptol and SMT solvers are not designed to perform -rigorous cryptanalysis, but can run some quick sanity checks. - -For example, it should not be easy to (ab)use SMT solvers to quickly -recover a cryptographic key from known or chosen plaintext: - -```xcryptol-session -// Try to recover `test_key` from known test plaintext/ciphertext -// labs::ModuleSystem> :sat \key -> S_32_64::encipher key S_32_64_test_pt == S_32_64_test_ct -// ... -``` - # Conclusion In this module, you learned how to define and reuse interfaces and @@ -1499,17 +1442,12 @@ complex nested module with multiple ciphers, modes, and properties. December 2001. https://csrc.nist.gov/pubs/sp/800/38/a/final -[2] NIST SP 800-12 Rev. 1. - "An Introduction to Information Security". - June 2017. - https://csrc.nist.gov/pubs/sp/800/12/r1/final - -[3] NIST FIPS 81 (withdrawn). +[2] NIST FIPS 81 (withdrawn). "DES MODES OF OPERATION" 2 December 1980. https://csrc.nist.gov/pubs/fips/81/final -[4] NIST Advanced Encryption Standard Algorithm Verification System +[3] NIST Advanced Encryption Standard Algorithm Verification System AES Multiblock Message Test (MMT) Sample Vectors https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/aes/aesmmt.zip From da47f979bd0df281f04b00509a3a4c6f1e138ecf Mon Sep 17 00:00:00 2001 From: WeeknightMVP Date: Sun, 10 Dec 2023 01:48:02 -0500 Subject: [PATCH 06/15] Comment out specs that depend on unfinished exercises --- labs/NewModuleSystem/NewModuleSystem.md | 108 +++++++++++------- .../NewModuleSystem/NewModuleSystemAnswers.md | 98 ++++++++-------- 2 files changed, 114 insertions(+), 92 deletions(-) diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index 2d4ddd2e..34fa5550 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -10,7 +10,7 @@ Before working through this lab, you'll need You'll also need experience with * loading modules and evaluating functions in the interpreter, * writing functions and properties, - * testing properties by using the `:prove` and `:check` commands, + * testing properties by using the `:prove` and `:exhaust` commands, * sequence comprehensions, * functions with curried parameters, and * parameterized modules. @@ -98,7 +98,9 @@ This is a new feature that enables us to present multiple `submodule`s -- e.g. multiple instances of a common parameterized module -- in a single file, which was not previously an option for examples like the earlier -[**Parameterized Modules**](../SimonSpeck/SimonSpeck.md) lab. +[**Parameterized Modules**](../SimonSpeck/SimonSpeck.md) lab. Some +exercises will require you to define submodules, after which you will +have to uncomment any test vectors that depend on these. # Symmetric Block Ciphers and Modes of Operation @@ -130,13 +132,13 @@ cipher operations from those for modes, in keeping with [1].) parameter /** arbitrary key type */ type K : * - + /** block size (in bits) */ type b : # - + /** forward cipher operator */ encipher : K -> [b] -> [b] - + /** reverse cipher operator */ decipher : K -> [b] -> [b] */ @@ -153,19 +155,19 @@ reusable **type aliases**: interface submodule I_BlockCipher where /** arbitrary key type */ type Key : * - + /** block size (in bits) */ type b : # - + /** type alias for a block, a bit string of width `b` */ type Block = [b] - + /** common type for `encipher` and `decipher` */ type Op = Key -> Block -> Block - + /** forward cipher operator */ encipher : Op - + /** reverse cipher operator */ decipher : Op ``` @@ -177,7 +179,7 @@ same purpose as previously for `parameter` blocks: to indicate that the module needs these parameters: ```cryptol -/* (Not quite ready to define these modes yet... +/* Not quite ready to define these modes yet... submodule ECB where import interface submodule I_BlockCipher @@ -208,7 +210,7 @@ submodule F_BlockCipher_Aliases where /** type alias for a block, a bit string of width `b` */ type Block = [I::b] - + /** common type for `encipher` and `decipher` */ type Op = I::Key -> Block -> Block ``` @@ -302,7 +304,7 @@ Start with explicitly named submodules. These can always be ```cryptol /** interface to collect relevant definitions from Simon instance */ -interface submodule I_Simon_Inst where +interface submodule I_SimonSpeck_Inst where type keySize : # type blockSize : # @@ -316,10 +318,10 @@ interface submodule I_Simon_Inst where // generalization of `Simon_32_64_BlockCipher` above /** generate `I_BlockCipher` implementation from Simon instance */ submodule F_SimonSpeck_BlockCipher where - import interface submodule I_Simon_Inst as S + import interface submodule I_SimonSpeck_Inst as S // export definitions for `I_BlockCipher` parameters - // type Key = [S::keySize] + type Key = [S::keySize] // type b = // Define and uncomment. encipher = S::encrypt @@ -443,12 +445,13 @@ Again, please do not ever use this weak cipher mode in the real world. in ECB mode. ```cryptol +/* Uncomment after finishing your definition of `F_SimonSpeck_BlockCipher` /** Simon 32/64 in ECB mode */ submodule ECB_Simon_32_64 = submodule ECB { submodule P_Simon_32_64_BlockCipher } +*/ // /** Simon 48/72 in ECB mode */ // submodule ECB_Simon_48_72 = ... // Replace with your definition and uncomment. - // Repeat for each Simon and Speck parameter set. ``` @@ -464,6 +467,7 @@ prominent warning not to use it in production. Your solution should pass the following test vectors [2]: ```cryptol +/* Uncomment after defining `ECB_DES` /** ECB/DES Tests */ submodule ECB_DES_Test where import submodule ECB_DES as ECB_DES @@ -480,14 +484,15 @@ submodule ECB_DES_Test where v35 = (0x1111111111111111, [0x1111111111111111, 0x0123456789ABCDEF], [0xF40379AB9E0EC533, 0x8A5AE1F81AB8F2DD]) property e35 = e v35 property d35 = d v35 +*/ ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :check ECB_DES_Test::e_fips_81 +labs::NewModuleSystem::NewModuleSystem> :exhaust ECB_DES_Test::e_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check ECB_DES_Test::d_fips_81 +labs::NewModuleSystem::NewModuleSystem> :exhaust ECB_DES_Test::d_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -513,6 +518,15 @@ Q.E.D. block cipher) in ECB (a weak cipher mode). Add docstrings with prominent warnings not to use them in production. +/* Uncomment after defining submodules +// /** Add a docstring */ +submodule ECB_AES_128 = // ... +// /** Add a docstring */ +submodule ECB_AES_192 = // ... +// /** Add a docstring */ +submodule ECB_AES_256 = // ... +*/ + Your solution should pass the following test vectors [3]: ```cryptol @@ -576,6 +590,7 @@ submodule P_KAT_ECB_AES_256_DECRYPT_9 where CIPHERTEXT = split 0x2c487fa96f4090c56aa1b5be81918a934c9492878fb0cd686dcf8d17d86485454c51237bbd09205dcef1552f430dd098b9d827a694730c133a0222c77f540f9d5fc2d36af359583c9e3b49df884228a64de79b67f66207c8281360b99b214042ce61367ff97960e944453cd63679bb44708897d29bc5e70f9fc8f1f715143fbb00f7f5c1b7b161ec26d8d41d36fab0fa8a85c3ee6ce4d37007eb7a89d6753590 PLAINTEXT = split 0x31fd5a307e279b2f34581e2c432379df8eccbaf79532938916711cd377540b9045373e47f2214b8f876040af733f6c9d8f03a7c58f8714d2fbb4c14af59c75b483adc718946ee907a18286cc4efd206789064b6f1b195f0d0d234468e4f00e6f1cad5cd3b9c0a643b3c0dd09280ff2e2a5929183409384dd72dc94e39687ea2b623d5d776700bd8b36e6130ffde966f134c4b1f35f29c5cc4a03297e1ccc9539 +/* Uncomment after defining `P_AES_..._BlockCipher` import submodule F_KAT_ECB { submodule P_AES_128_BlockCipher } as F_KAT_ECB_AES_128 (F_KAT) import submodule F_KAT_ECB_AES_128::F_KAT { submodule P_KAT_ECB_AES_128_ENCRYPT_9 } as KAT_ECB_AES_128_ENCRYPT_9 import submodule F_KAT_ECB_AES_128::F_KAT { submodule P_KAT_ECB_AES_128_DECRYPT_9 } as KAT_ECB_AES_128_DECRYPT_9 @@ -587,54 +602,55 @@ import submodule F_KAT_ECB_AES_192::F_KAT { submodule P_KAT_ECB_AES_192_DECRYPT_ import submodule F_KAT_ECB { submodule P_AES_256_BlockCipher } as F_KAT_ECB_AES_256 (F_KAT) import submodule F_KAT_ECB_AES_256::F_KAT { submodule P_KAT_ECB_AES_256_ENCRYPT_9 } as KAT_ECB_AES_256_ENCRYPT_9 import submodule F_KAT_ECB_AES_256::F_KAT { submodule P_KAT_ECB_AES_256_DECRYPT_9 } as KAT_ECB_AES_256_DECRYPT_9 +*/ ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_128_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_128_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_128_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_128_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_128_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_192_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_192_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_192_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_192_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_192_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_256_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_256_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_256_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check KAT_ECB_AES_256_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystem> :exhaust KAT_ECB_AES_256_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -746,6 +762,7 @@ warning not to use it in production. Your solution should pass the following test vectors [2]: ```cryptol +/* Uncomment after defining `CBC_DES` submodule CBC_DES_Test where import submodule CBC_DES as CBC_DES @@ -757,14 +774,15 @@ submodule CBC_DES_Test where property e_fips_81 = e v_fips_81 property d_fips_81 = d v_fips_81 +*/ ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystem> :check CBC_DES_Test::e_fips_81 +labs::NewModuleSystem::NewModuleSystem> :exhaust CBC_DES_Test::e_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystem> :check CBC_DES_Test::d_fips_81 +labs::NewModuleSystem::NewModuleSystem> :exhaust CBC_DES_Test::d_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -841,6 +859,7 @@ submodule P_KAT_CBC_AES_256_DECRYPT_9 where CIPHERTEXT = split 0x5b97a9d423f4b97413f388d9a341e727bb339f8e18a3fac2f2fb85abdc8f135deb30054a1afdc9b6ed7da16c55eba6b0d4d10c74e1d9a7cf8edfaeaa684ac0bd9f9d24ba674955c79dc6be32aee1c260b558ff07e3a4d49d24162011ff254db8be078e8ad07e648e6bf5679376cb4321a5ef01afe6ad8816fcc7634669c8c4389295c9241e45fff39f3225f7745032daeebe99d4b19bcb215d1bfdb36eda2c24 PLAINTEXT = split 0xbfe5c6354b7a3ff3e192e05775b9b75807de12e38a626b8bf0e12d5fff78e4f1775aa7d792d885162e66d88930f9c3b2cdf8654f56972504803190386270f0aa43645db187af41fcea639b1f8026ccdd0c23e0de37094a8b941ecb7602998a4b2604e69fc04219585d854600e0ad6f99a53b2504043c08b1c3e214d17cde053cbdf91daa999ed5b47c37983ba3ee254bc5c793837daaa8c85cfc12f7f54f699f +/* Uncomment after defining `P_AES_..._BlockCipher` import submodule F_KAT_CBC { submodule P_AES_128_BlockCipher } as F_KAT_CBC_AES_128 (F_KAT) import submodule F_KAT_CBC_AES_128::F_KAT { submodule P_KAT_CBC_AES_128_ENCRYPT_9 } as KAT_CBC_AES_128_ENCRYPT_9 import submodule F_KAT_CBC_AES_128::F_KAT { submodule P_KAT_CBC_AES_128_DECRYPT_9 } as KAT_CBC_AES_128_DECRYPT_9 @@ -852,54 +871,55 @@ import submodule F_KAT_CBC_AES_192::F_KAT { submodule P_KAT_CBC_AES_192_DECRYPT_ import submodule F_KAT_CBC { submodule P_AES_256_BlockCipher } as F_KAT_CBC_AES_256 (F_KAT) import submodule F_KAT_CBC_AES_256::F_KAT { submodule P_KAT_CBC_AES_256_ENCRYPT_9 } as KAT_CBC_AES_256_ENCRYPT_9 import submodule F_KAT_CBC_AES_256::F_KAT { submodule P_KAT_CBC_AES_256_DECRYPT_9 } as KAT_CBC_AES_256_DECRYPT_9 +*/ ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md index 991dfdf6..730806d6 100644 --- a/labs/NewModuleSystem/NewModuleSystemAnswers.md +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -10,7 +10,7 @@ Before working through this lab, you'll need You'll also need experience with * loading modules and evaluating functions in the interpreter, * writing functions and properties, - * testing properties by using the `:prove` and `:check` commands, + * testing properties by using the `:prove` and `:exhaust` commands, * sequence comprehensions, * functions with curried parameters, and * parameterized modules. @@ -98,7 +98,9 @@ This is a new feature that enables us to present multiple `submodule`s -- e.g. multiple instances of a common parameterized module -- in a single file, which was not previously an option for examples like the earlier -[**Parameterized Modules**](../SimonSpeck/SimonSpeck.md) lab. +[**Parameterized Modules**](../SimonSpeck/SimonSpeck.md) lab. Some +exercises will require you to define submodules, after which you will +have to uncomment any test vectors that depend on these. # Symmetric Block Ciphers and Modes of Operation @@ -177,7 +179,7 @@ same purpose as previously for `parameter` blocks: to indicate that the module needs these parameters: ```cryptol -/* (Not quite ready to define these modes yet... +/* Not quite ready to define these modes yet... submodule ECB where import interface submodule I_BlockCipher @@ -307,11 +309,11 @@ Start with explicitly named submodules. These can always be interface submodule I_SimonSpeck_Inst where type keySize : # type blockSize : # - + type Key = [keySize] type Block = [blockSize] type Op = Key -> Block -> Block - + encrypt : Op decrypt : Op @@ -323,7 +325,7 @@ submodule F_SimonSpeck_BlockCipher where // export definitions for `I_BlockCipher` parameters type Key = [S::keySize] type b = S::blockSize - + encipher = S::encrypt decipher = S::decrypt @@ -605,11 +607,11 @@ submodule ECB_DES_Test where ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check ECB_DES_Test::e_fips_81 +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust ECB_DES_Test::e_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check ECB_DES_Test::d_fips_81 +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust ECB_DES_Test::d_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -723,51 +725,51 @@ import submodule F_KAT_ECB_AES_256::F_KAT { submodule P_KAT_ECB_AES_256_DECRYPT_ ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_128_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_128_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_128_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_128_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_128_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_192_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_192_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_192_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_192_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_192_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_256_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_256_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_256_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_ECB_AES_256_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_ECB_AES_256_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -958,11 +960,11 @@ submodule CBC_DES_Test where ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check CBC_DES_Test::e_fips_81 +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust CBC_DES_Test::e_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check CBC_DES_Test::d_fips_81 +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust CBC_DES_Test::d_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -1064,51 +1066,51 @@ import submodule F_KAT_CBC_AES_256::F_KAT { submodule P_KAT_CBC_AES_256_DECRYPT_ ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_128_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_128_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_192_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_192_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CBC_AES_256_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CBC_AES_256_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -1193,11 +1195,11 @@ submodule CFB_DES_Test where ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check CFB_DES_Test::e_fips_81 +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust CFB_DES_Test::e_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check CFB_DES_Test::d_fips_81 +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust CFB_DES_Test::d_fips_81 Using exhaustive testing. Passed 1 tests. Q.E.D. @@ -1297,51 +1299,51 @@ import submodule F_KAT_CFB_AES_256::F_KAT { submodule P_KAT_CFB_AES_256_DECRYPT_ ``` ```xcryptol-session -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_128_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_128_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_128_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_128_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_128_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_192_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_192_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_192_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_192_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_192_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_ENCRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_256_ENCRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_ENCRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_256_ENCRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_DECRYPT_9::test_encrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_256_DECRYPT_9::test_encrypt Using exhaustive testing. Passed 1 tests. Q.E.D. -labs::NewModuleSystem::NewModuleSystemAnswers> :check KAT_CFB_AES_256_DECRYPT_9::test_decrypt +labs::NewModuleSystem::NewModuleSystemAnswers> :exhaust KAT_CFB_AES_256_DECRYPT_9::test_decrypt Using exhaustive testing. Passed 1 tests. Q.E.D. From 8438e553b22f983096d1759af4645058543e1c93 Mon Sep 17 00:00:00 2001 From: WeeknightMVP <6430915+WeeknightMVP@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:16:12 +0000 Subject: [PATCH 07/15] Fix typo in `labs/NewModuleSystem` --- labs/NewModuleSystem/NewModuleSystem.md | 2 +- labs/NewModuleSystem/NewModuleSystemAnswers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index 34fa5550..f3bc31e8 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -228,7 +228,7 @@ Then a `mode` can... Of course, parameterizable block cipher modes need block ciphers! In the previous lab on -[Parameterized Modules](../SimonSpeck/SimonSpeck.md), we implemeented +[Parameterized Modules](../SimonSpeck/SimonSpeck.md), we implemented the Simon and Speck block ciphers, which are themselves parameterized. To use these for our block cipher modes, we just need to adapt them to our fancy new interface... diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md index 730806d6..766196b3 100644 --- a/labs/NewModuleSystem/NewModuleSystemAnswers.md +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -228,7 +228,7 @@ Then a `mode` can... Of course, parameterizable block cipher modes need block ciphers! In the previous lab on -[Parameterized Modules](../SimonSpeck/SimonSpeck.md), we implemeented +[Parameterized Modules](../SimonSpeck/SimonSpeck.md), we implemented the Simon and Speck block ciphers, which are themselves parameterized. To use these for our block cipher modes, we just need to adapt them to our fancy new interface... From 14cc534e9118c3c1f18176813cda8034b2f82a61 Mon Sep 17 00:00:00 2001 From: WeeknightMVP <6430915+WeeknightMVP@users.noreply.github.com> Date: Wed, 30 Oct 2024 21:20:39 +0000 Subject: [PATCH 08/15] Clarify term for `generator expression` in `labs/NewModuleSystem` --- labs/NewModuleSystem/NewModuleSystem.md | 2 +- labs/NewModuleSystem/NewModuleSystemAnswers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index f3bc31e8..10f131f6 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -691,7 +691,7 @@ submodule CBC where ``` **Hint**: The above definition can be directly translated to a Cryptol -[generating function](https://galoisinc.github.io/cryptol/master/BasicTypes.html#sequences). +[generator expression](https://galoisinc.github.io/cryptol/master/BasicTypes.html#sequences). (See `generate` and the examples following it.) So for `encrypt`, you can say `encrypt key iv pt = ct where ct@i = ...` and then fill in the rest. Remember to account for the initialization vector and the first diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md index 766196b3..130a8ab3 100644 --- a/labs/NewModuleSystem/NewModuleSystemAnswers.md +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -816,7 +816,7 @@ submodule CBC where ``` **Hint**: The above definition can be directly translated to a Cryptol -[generating function](https://galoisinc.github.io/cryptol/master/BasicTypes.html#sequences). +[generator expression](https://galoisinc.github.io/cryptol/master/BasicTypes.html#sequences). (See `generate` and the examples following it.) So for `encrypt`, you can say `encrypt key iv pt = ct where ct@i = ...` and then fill in the rest. Remember to account for the initialization vector and the first From d9a59bd995175e9114330f488ee6a3cbb123003f Mon Sep 17 00:00:00 2001 From: WeeknightMVP <6430915+WeeknightMVP@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:33:27 +0000 Subject: [PATCH 09/15] Reroute recommended paths; add multi-lab landing page for module system --- labs/ModuleSystem.md | 26 +++ labs/NewModuleSystem/NewModuleSystem.md | 2 +- .../NewModuleSystem/NewModuleSystemAnswers.md | 2 +- labs/SimonSpeck/SimonSpeck.md | 2 +- misc/CryptolCourse.gv | 16 +- misc/CryptolCourse.gv.png | Bin 71245 -> 70736 bytes misc/CryptolCourse.gv.svg | 187 +++++++++--------- misc/ModuleSystem.gv | 22 +++ misc/ModuleSystem.gv.png | Bin 0 -> 7703 bytes misc/ModuleSystem.gv.svg | 39 ++++ 10 files changed, 192 insertions(+), 104 deletions(-) create mode 100644 labs/ModuleSystem.md create mode 100644 misc/ModuleSystem.gv create mode 100644 misc/ModuleSystem.gv.png create mode 100644 misc/ModuleSystem.gv.svg diff --git a/labs/ModuleSystem.md b/labs/ModuleSystem.md new file mode 100644 index 00000000..452c2efb --- /dev/null +++ b/labs/ModuleSystem.md @@ -0,0 +1,26 @@ +# Module System + +* [Parameterized Modules](./SimonSpeck/SimonSpeck.md) + Cut your teeth on Cryptol modules with the Simon/Speck family of + related block ciphers. +* [New Module System](./NewModuleSystem/NewModuleSystem.md) + Unleash the full power of Cryptol 3's new module system on + modes of operation for any compatible block cipher. + + + Cryptol Demos - Suggested Flow + + +# Solicitation + +How was your experience with this lab? Suggestions are welcome in the +form of a ticket on the course GitHub page: +https://github.com/weaversa/cryptol-course/issues + +# From here, you can go somewhere! + +|||| +|-:|:-:|-| +|| [< Cryptographic Properties](../CryptoProofs/CryptoProofs.md) || +|| **Module System** || +|| [v Parameterized Modules](./SimonSpeck/SimonSpeck.md) || diff --git a/labs/NewModuleSystem/NewModuleSystem.md b/labs/NewModuleSystem/NewModuleSystem.md index 10f131f6..3ec61de8 100644 --- a/labs/NewModuleSystem/NewModuleSystem.md +++ b/labs/NewModuleSystem/NewModuleSystem.md @@ -1098,6 +1098,6 @@ https://github.com/weaversa/cryptol-course/issues |||| |-:|:-:|-| -|| [- Key Wrapping](../KeyWrapping/KeyWrapping.md) || +|| [^ Module System](../ModuleSystem.md) || | [< Parameterized Modules](../SimonSpeck/SimonSpeck.md) | **New Module System** || || [! New Module System (Answers)](./NewModuleSystemAnswers.md) || diff --git a/labs/NewModuleSystem/NewModuleSystemAnswers.md b/labs/NewModuleSystem/NewModuleSystemAnswers.md index 130a8ab3..d7131f05 100644 --- a/labs/NewModuleSystem/NewModuleSystemAnswers.md +++ b/labs/NewModuleSystem/NewModuleSystemAnswers.md @@ -1463,6 +1463,6 @@ https://github.com/weaversa/cryptol-course/issues |||| |-:|:-:|-| -|| [- Key Wrapping](../KeyWrapping/KeyWrapping.md) || +|| [^ Module System](../ModuleSystem.md) || | [< Parameterized Modules](../SimonSpeck/SimonSpeck.md) | **New Module System (Answers)** || || [? New Module System](./NewModuleSystem.md) || diff --git a/labs/SimonSpeck/SimonSpeck.md b/labs/SimonSpeck/SimonSpeck.md index b7592b07..46919035 100644 --- a/labs/SimonSpeck/SimonSpeck.md +++ b/labs/SimonSpeck/SimonSpeck.md @@ -407,5 +407,5 @@ https://github.com/weaversa/cryptol-course/issues |||| |-:|:-:|-| -|| [- Key Wrapping](../KeyWrapping/KeyWrapping.md) || +|| [^ Module System](../ModuleSystem.md) || || **Parameterized Modules** | [New Module System >](../NewModuleSystem/NewModuleSystem.md) | diff --git a/misc/CryptolCourse.gv b/misc/CryptolCourse.gv index 4baf51aa..4e5c68ea 100644 --- a/misc/CryptolCourse.gv +++ b/misc/CryptolCourse.gv @@ -18,7 +18,7 @@ digraph { CRC [URL="../labs/CRC/CRC.html"]; Salsa20 [URL="../labs/Salsa20/Salsa20.html"]; CryptographicProperties [URL="../labs/CryptoProofs/CryptoProofs.html"]; - KeyWrapping [URL="../labs/KeyWrapping/KeyWrapping.html"]; + ModuleSystem [URL="../labs/ModuleSystem.html"]; Capstone [URL="../labs/LoremIpsum/LoremIpsum.html"]; // branch nodes @@ -32,7 +32,7 @@ digraph { TranspositionCiphers [URL="../labs/Transposition/Contents.html"]; ProjectEuler [URL="../labs/ProjectEuler/ProjectEuler.html"]; ContinuousReasoning [URL="../labs/SAW/SAW.html"]; - ParameterizedModules [URL="../labs/SimonSpeck/SimonSpeck.html"]; + KeyWrapping [URL="../labs/KeyWrapping/KeyWrapping.html"]; // newline/space labels @@ -43,7 +43,7 @@ digraph { CryptolDemos [label = "Cryptol Demos"] SAWDemos [label = "SAW Demos"] TranspositionCiphers [label = "Transposition\nCiphers"] - ParameterizedModules [label = "Parameterized\nModules"] + ModuleSystem [label = "Module\nSystem"] Salsa20Properties [label = "Salsa20\nProperties"] ContinuousReasoning [label = "SAW\nContinuous Reasoning"]; StyleGuide [label = "Style Guide"] @@ -58,8 +58,8 @@ digraph { LanguageBasics -> CRC; CRC -> Salsa20; Salsa20 -> CryptographicProperties; - CryptographicProperties -> KeyWrapping; - KeyWrapping -> Capstone; + CryptographicProperties -> ModuleSystem; + ModuleSystem -> Capstone; // branches edge [color=black]; @@ -72,7 +72,7 @@ digraph { CryptographicProperties -> TranspositionCiphers; CryptographicProperties -> ProjectEuler; CryptographicProperties -> ContinuousReasoning - KeyWrapping -> ParameterizedModules; + ModuleSystem -> KeyWrapping; // ranks @@ -98,7 +98,7 @@ digraph { { // Cryptographic Properties rank = same; - KeyWrapping; + ModuleSystem; Salsa20Properties; TranspositionCiphers; ProjectEuler; @@ -109,7 +109,7 @@ digraph { // Key Wrapping rank = same; Capstone; - ParameterizedModules; + KeyWrapping; } } \ No newline at end of file diff --git a/misc/CryptolCourse.gv.png b/misc/CryptolCourse.gv.png index 5f690db0ac349cead0fde688d9d9bea7a172a95e..4ca6a924717efb989adb88fa4c7e47f5861c15eb 100644 GIT binary patch literal 70736 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 NzQxKSbJM|J{|7%QYoP!D diff --git a/misc/CryptolCourse.gv.svg b/misc/CryptolCourse.gv.svg index ff8c8253..a4d284bb 100644 --- a/misc/CryptolCourse.gv.svg +++ b/misc/CryptolCourse.gv.svg @@ -1,18 +1,19 @@ - - - - + + + +%3 Installation - -Installation + +Installation @@ -20,262 +21,262 @@ Overview - -Overview + +Overview Installation->Overview - - + + Interpreter - -Interpreter + +Interpreter Overview->Interpreter - - + + LanguageBasics - -Language -Basics + +Language +Basics Interpreter->LanguageBasics - - + + CRC - -CRC + +CRC LanguageBasics->CRC - - + + StyleGuide - -Style Guide + +Style Guide LanguageBasics->StyleGuide - - + + CryptolDemos - -Cryptol Demos + +Cryptol Demos LanguageBasics->CryptolDemos - - + + SAWDemos - -SAW Demos + +SAW Demos LanguageBasics->SAWDemos - - + + TypeHackery - -Type Hackery + +Type Hackery LanguageBasics->TypeHackery - - + + Salsa20 - -Salsa20 + +Salsa20 CRC->Salsa20 - - + + CryptographicProperties - -Cryptographic -Properties + +Cryptographic +Properties Salsa20->CryptographicProperties - - + + - + -KeyWrapping - - -Key Wrapping +ModuleSystem + + +Module +System - + -CryptographicProperties->KeyWrapping - - +CryptographicProperties->ModuleSystem + + Salsa20Properties - -Salsa20 -Properties + +Salsa20 +Properties CryptographicProperties->Salsa20Properties - - + + TranspositionCiphers - -Transposition -Ciphers + +Transposition +Ciphers CryptographicProperties->TranspositionCiphers - - + + ProjectEuler - -Project Euler + +Project Euler CryptographicProperties->ProjectEuler - - + + ContinuousReasoning - -SAW -Continuous Reasoning + +SAW +Continuous Reasoning CryptographicProperties->ContinuousReasoning - - + + Capstone - -Capstone + +Capstone - + -KeyWrapping->Capstone - - +ModuleSystem->Capstone + + - + -ParameterizedModules - - -Parameterized -Modules +KeyWrapping + + +Key Wrapping - + -KeyWrapping->ParameterizedModules - - +ModuleSystem->KeyWrapping + + diff --git a/misc/ModuleSystem.gv b/misc/ModuleSystem.gv new file mode 100644 index 00000000..9be15152 --- /dev/null +++ b/misc/ModuleSystem.gv @@ -0,0 +1,22 @@ +digraph { + bgcolor="transparent"; + rankdir="LR"; + + node [shape="box", style="rounded,filled", fillcolor="white", penwidth = 2]; + edge [penwidth = 1]; + + // recommended + + ParameterizedModules [URL="../labs/SimonSpeck/SimonSpeck.html"]; + NewModuleSystem [URL="../labs/NewModuleSystem/NewModuleSystem.html"]; + + // newline/space labels + + ParameterizedModules [label = "Parameterized\nModules"]; + NewModuleSystem [label = "New\nModule\nSystem"]; + + // recommended flow + edge [color=red]; + + ParameterizedModules -> NewModuleSystem; +} \ No newline at end of file diff --git a/misc/ModuleSystem.gv.png b/misc/ModuleSystem.gv.png new file mode 100644 index 0000000000000000000000000000000000000000..5320abedbdd5660cdd3d5157252205d4eb93a8cf GIT binary patch literal 7703 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 + + + + + +%3 + + +ParameterizedModules + + +Parameterized +Modules + + + + + +NewModuleSystem + + +New +Module +System + + + + + +ParameterizedModules->NewModuleSystem + + + + + From 644b1a8719ec72af000bca10ad99654eddb410e6 Mon Sep 17 00:00:00 2001 From: WeeknightMVP <6430915+WeeknightMVP@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:22:05 +0000 Subject: [PATCH 10/15] Update `Suggested Lab Order` in `README.md` --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 16f2a39b..1e0bae3e 100644 --- a/README.md +++ b/README.md @@ -126,12 +126,14 @@ first lab walks you through [installing and running Cryptol](INSTALL.md). * [Continuous Reasoning with SAW](./labs/SAW/SAW.md): Learn how to use Python to drive SAW and enforce formal invariants on cryptographic implementations at every check-in to a repository. -8. [Methods for Key Wrapping](./labs/KeyWrapping/KeyWrapping.md): - Create a Cryptol specification of NIST's [SP800-38F key wrap - standard](https://csrc.nist.gov/publications/detail/sp/800-38f/final). - * [Parameterized Modules: Simon and Speck](./labs/SimonSpeck/SimonSpeck.md): - Learn about Cryptol's parameterized modules by creating a - Cryptol specification of NSA's Speck block cipher. +8. [Module System](./labs/ModuleSystem.md): + Parameterize modules to reuse cores of related specifications. + * [Parameterized Modules: Simon and Speck](./SimonSpeck/SimonSpeck.md) + Cut your teeth on Cryptol modules with the Simon/Speck family of + related block ciphers. + * [New Module System: Block Cipher Modes](./NewModuleSystem/NewModuleSystem.md) + Unleash the full power of Cryptol 3's new module system on + modes of operation for any compatible block cipher. 9. [Capstone: Putting it all together](./labs/LoremIpsum/LoremIpsum.md): Use components and techniques from other labs to decrypt a series of secret messages by feeding wrapped keys into the anomalous KLI20 From 6893e14db238611a5a93c3a5bbf3bcc1b61a3e25 Mon Sep 17 00:00:00 2001 From: WeeknightMVP <6430915+WeeknightMVP@users.noreply.github.com> Date: Thu, 31 Oct 2024 18:30:56 +0000 Subject: [PATCH 11/15] Correct graphic in Module System landing page --- labs/ModuleSystem.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/labs/ModuleSystem.md b/labs/ModuleSystem.md index 452c2efb..76ffbd02 100644 --- a/labs/ModuleSystem.md +++ b/labs/ModuleSystem.md @@ -7,8 +7,8 @@ Unleash the full power of Cryptol 3's new module system on modes of operation for any compatible block cipher. - - Cryptol Demos - Suggested Flow + + Module System - Suggested Flow # Solicitation From 87d758774ec705cc840e2e49c533dcec56d1bdb8 Mon Sep 17 00:00:00 2001 From: WeeknightMVP <6430915+WeeknightMVP@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:05:15 +0000 Subject: [PATCH 12/15] Restore `Key Wrapping` lab description in `README.md` --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1e0bae3e..2938bd6f 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,11 @@ first lab walks you through [installing and running Cryptol](INSTALL.md). Use components and techniques from other labs to decrypt a series of secret messages by feeding wrapped keys into the anomalous KLI20 cryptographic engine. + * [Methods for Key Wrapping](./labs/KeyWrapping/KeyWrapping.md): + Create a Cryptol specification of NIST's [SP800-38F key wrap + standard](https://csrc.nist.gov/publications/detail/sp/800-38f/final). + (Weaves in most concepts learned throughout the course; + good prep for the Capstone.) ## Graphical View of the Course From 5c2c1045cfd4773a0cae9db0031eea0225bfa982 Mon Sep 17 00:00:00 2001 From: WeeknightMVP <6430915+WeeknightMVP@users.noreply.github.com> Date: Mon, 4 Nov 2024 19:45:46 +0000 Subject: [PATCH 13/15] Promote `Key Wrapping` back into recommended path --- README.md | 12 ++--- misc/CryptolCourse.gv | 8 ++-- misc/CryptolCourse.gv.png | Bin 70736 -> 68600 bytes misc/CryptolCourse.gv.svg | 94 +++++++++++++++++++------------------- 4 files changed, 57 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 2938bd6f..395b2018 100644 --- a/README.md +++ b/README.md @@ -134,15 +134,15 @@ first lab walks you through [installing and running Cryptol](INSTALL.md). * [New Module System: Block Cipher Modes](./NewModuleSystem/NewModuleSystem.md) Unleash the full power of Cryptol 3's new module system on modes of operation for any compatible block cipher. -9. [Capstone: Putting it all together](./labs/LoremIpsum/LoremIpsum.md): +9. [Methods for Key Wrapping](./labs/KeyWrapping/KeyWrapping.md): + Create a Cryptol specification of NIST's [SP800-38F key wrap + standard](https://csrc.nist.gov/publications/detail/sp/800-38f/final) + by combining most concepts learned throughout the course. +10. [Capstone: Putting it all together](./labs/LoremIpsum/LoremIpsum.md): Use components and techniques from other labs to decrypt a series of secret messages by feeding wrapped keys into the anomalous KLI20 cryptographic engine. - * [Methods for Key Wrapping](./labs/KeyWrapping/KeyWrapping.md): - Create a Cryptol specification of NIST's [SP800-38F key wrap - standard](https://csrc.nist.gov/publications/detail/sp/800-38f/final). - (Weaves in most concepts learned throughout the course; - good prep for the Capstone.) + ## Graphical View of the Course diff --git a/misc/CryptolCourse.gv b/misc/CryptolCourse.gv index 4e5c68ea..600df7b4 100644 --- a/misc/CryptolCourse.gv +++ b/misc/CryptolCourse.gv @@ -19,6 +19,7 @@ digraph { Salsa20 [URL="../labs/Salsa20/Salsa20.html"]; CryptographicProperties [URL="../labs/CryptoProofs/CryptoProofs.html"]; ModuleSystem [URL="../labs/ModuleSystem.html"]; + KeyWrapping [URL="../labs/KeyWrapping/KeyWrapping.html"]; Capstone [URL="../labs/LoremIpsum/LoremIpsum.html"]; // branch nodes @@ -32,7 +33,6 @@ digraph { TranspositionCiphers [URL="../labs/Transposition/Contents.html"]; ProjectEuler [URL="../labs/ProjectEuler/ProjectEuler.html"]; ContinuousReasoning [URL="../labs/SAW/SAW.html"]; - KeyWrapping [URL="../labs/KeyWrapping/KeyWrapping.html"]; // newline/space labels @@ -59,7 +59,8 @@ digraph { CRC -> Salsa20; Salsa20 -> CryptographicProperties; CryptographicProperties -> ModuleSystem; - ModuleSystem -> Capstone; + ModuleSystem -> KeyWrapping; + KeyWrapping -> Capstone; // branches edge [color=black]; @@ -72,7 +73,6 @@ digraph { CryptographicProperties -> TranspositionCiphers; CryptographicProperties -> ProjectEuler; CryptographicProperties -> ContinuousReasoning - ModuleSystem -> KeyWrapping; // ranks @@ -108,8 +108,8 @@ digraph { { // Key Wrapping rank = same; - Capstone; KeyWrapping; + Capstone; } } \ No newline at end of file diff --git a/misc/CryptolCourse.gv.png b/misc/CryptolCourse.gv.png index 4ca6a924717efb989adb88fa4c7e47f5861c15eb..4ebf94a9f05caf33ebab055eae8bb6cd7c5d693c 100644 GIT binary patch literal 68600 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