Compare commits

...

12 Commits

Author SHA1 Message Date
6287904204 Update tests for AEAD change 2025-09-09 20:55:16 -07:00
Eric Mertens
a3d978a4b3 Fixup documentation for tag change 2025-09-08 11:08:52 -07:00
8813e6066f Fixup saw proofs 2025-09-07 20:27:48 -07:00
1acbbe633f Documentation and layout 2025-09-07 17:37:10 -07:00
639ad1f1dd More inline equations from the spec 2025-09-07 11:37:34 -07:00
8c9c356220 Add saw proof for AEAD inversion 2025-09-07 10:41:52 -07:00
671aac130f Add a license file 2025-09-05 22:37:07 -07:00
593a440b84 Documentation and visibility 2025-09-05 09:12:08 -07:00
86aab13dc4 Comments and fix error introduce in previous cleanup 2025-09-04 20:58:32 -07:00
ce7c9a8798 Fixup the docstrings 2025-09-04 20:37:05 -07:00
Eric Mertens
be65d0dd4e Unify into a single implementation file 2025-09-04 18:30:47 -07:00
51dbdbf415 Documentation and improved bit-order treatment 2025-09-04 14:55:46 -07:00
10 changed files with 618 additions and 189 deletions

639
Ascon.cry
View File

@@ -1,102 +1,581 @@
/**
Implementation of Ascon-Based Lightweight Cryptography
Reference:
Meltem Sönmez Turan, Kerry A. McKay, Donghoon Chang, Jinkeon Kang,
John Kelsey (2025) Ascon-Based Lightweight Cryptography Standards
for Constrained Devices. (National Institute of Standards and Technology,
Gaithersburg, MD), NIST Special Publication (SP) NIST SP 800-232.
https://doi.org/10.6028/NIST.SP.800-232
*/
/** Ascon-Based Lightweight Cryptography
*
* Author: Eric Mertens <emertens@gmail.com>
* License: ISC
*
* Key algorithms:
* - AEAD128_encrypt/decrypt: Authenticated encryption
* - Hash256: Cryptographic hash function
* - XOF128: Extendable output function
* - CXOF128: Customizable extendable output function
*
* Reference:
*
* Meltem Sönmez Turan, Kerry A. McKay, Donghoon Chang, Jinkeon Kang,
* John Kelsey (2025) Ascon-Based Lightweight Cryptography Standards
* for Constrained Devices. (National Institute of Standards and Technology,
* Gaithersburg, MD), NIST Special Publication (SP) NIST SP 800-232.
* https://doi.org/10.6028/NIST.SP.800-232
*/
module Ascon where
// 2.1. Auxiliary Functions
private
// 2.1. Auxiliary Functions
/// Parse function.
parse : {r, n} (fin n, fin r, 0 < r) => [n] -> ([n/r][r], [n%r])
parse (M_ # Ml) = (split M_, Ml)
/** Parse function.
*
* The parse(𝑋, 𝑟) function parses the input bitstring 𝑋 into a sequence
* of blocks 𝑋₀, 𝑋₁, …, 𝑋͠ℓ, where 𝓁 ← ⌊|𝑋|/𝑟⌋ (i.e., 𝑋 ← 𝑋₀ ∥ 𝑋₁ ∥ … ∥ 𝑋͠ℓ).
* The 𝑋ᵢ blocks for 0 ≤ i ≤ 𝓁 1 each have a bit length 𝑟, whereas
* 0 ≤ |𝑋͠ℓ| ≤ 𝑟 1 (see Algorithm 1). When |𝑋| mod 𝑟 = 0, the final
* block is empty (i.e., |𝑋͠ℓ| = 0).
*/
parse : {r, m} (fin m, fin r, r >= 1) => [m] -> ([m / r][r], [m % r])
parse (M # Ml) = (split M, Ml)
/// Padding rule.
pad : {r, n} (n < r, fin r) => [n] -> [r]
pad M = M # 0b1 # 0
/** Padding rule.
*
* The function pad(𝑋, 𝑟) appends the bit 1 to the bitstring 𝑋, followed
* by the bitstring 0ʲ, where 𝑗 is equal to (|𝑋|1) mod 𝑟. The length of
* the output bitstring is a multiple of 𝑟 (see Algorithm 2). For examples
* of padding when representing the data as 64-bit unsigned integers, see
* Appendix A.2.
*/
pad : {r, m} (m < r, fin r) => [m] -> [r]
pad M = M # 0b1 # 0
toBlocks : {r, n} (r >= 1, fin r, fin n) => [n] -> [n / r + 1][r]
toBlocks M = M1 # [pad M2]
/**
* Combination of parse and pad that splits a bitstring into a sequence
* of integers using Cryptol's native big-endian representation.
*/
toBlocks : {r, m} (r >= 1, fin r, fin m) => [m] -> [m / r + 1][r]
toBlocks M = bitsToWords (M # 0b1 # 0)
// 3. Ascon Permutations
/** Predicate on valid round counts. */
type constraint ValidRnd rnd = rnd <= 16
/** Core permutation function parameterized by a round count. */
Ascon_p : {rnd} (ValidRnd rnd) => State -> State
Ascon_p S = foldl round S (drop`{back=rnd} Const)
/**
* Single round of the Ascon-p permutation parameterized by the round
* constant.
*/
round : State -> [64] -> State
round S ci = pL (pS (pC S ci))
// 3.1. Internal State
/**
* The permutations operate on the 320-bit state 𝑆, which is represented
* as five 64-bit words denoted as 𝑆ᵢ for 0 ≤ 𝑖 ≤ 4:
*
* 𝑆 = 𝑆₀ ‖ 𝑆₁ ‖ 𝑆₂ ‖ 𝑆₃ ‖ 𝑆₄
*
* Let 𝑠ᵢⱼ represent the 𝑗th bit of 𝑆ᵢ, 0 ≤ 𝑗 < 64. In this specification
* of the Ascon permutation, each state word represents a 64-bit unsigned
* integer, where the least significant bit is the rightmost bit. Details
* on other representations of the state can be found in Appendix A.
*/
type State = [5][64]
// 3.2. Constant-Addition Layer pC
// The constant cᵢ of round 𝑖 of the Ascon permutation Ascon-p[𝑟𝑛𝑑]
// (instantiated with 𝑟𝑛𝑑 rounds) for 𝑟𝑛𝑑 ≤ 16 and 0 ≤ 𝑖 < 𝑟𝑛𝑑 1
// is defined as
//
// cᵢ = const[16 𝑟𝑛𝑑 + 𝑖]
//
// where const[0],…,const[15] are defined in Table 5.
/**
* The constant-addition layer 𝑃𝑐 adds a 64-bit round constant cᵢ to 𝑆₂
* in round 𝑖, for i ≥ 0, 𝑆₂ = 𝑆₂ ⊕ cᵢ.
*/
pC : State -> [64] -> State
pC [S0, S1, S2, S3, S4] ci = [S0, S1, S2 ^ ci, S3, S4]
/**
* Table 5. The constants constᵢ to derive round constants of the Ascon
* permutations
*/
Const : [16][64]
Const =
[ 0x000000000000003c
, 0x000000000000002d
, 0x000000000000001e
, 0x000000000000000f
, 0x00000000000000f0
, 0x00000000000000e1
, 0x00000000000000d2
, 0x00000000000000c3
, 0x00000000000000b4
, 0x00000000000000a5
, 0x0000000000000096
, 0x0000000000000087
, 0x0000000000000078
, 0x0000000000000069
, 0x000000000000005a
, 0x000000000000004b
]
// 3.3. Substitution Layer pS
/**
* The substitution layer 𝑝𝑆 updates the state S with 64 parallel
* applications of the 5-bit substitution box SBOX
*/
pS : State -> State
pS S = transpose (map SBox (transpose S))
/** 5-bit substitution used in pS. */
SBox : [5] -> [5]
SBox i = SBoxTable@i
/** Table 6. Lookup table for SBox */
SBoxTable : [32][5]
SBoxTable =
map drop
[ 0x04, 0x0b, 0x1f, 0x14, 0x1a, 0x15, 0x09, 0x02
, 0x1b, 0x05, 0x08, 0x12, 0x1d, 0x03, 0x06, 0x1c
, 0x1e, 0x13, 0x07, 0x0e, 0x00, 0x0d, 0x11, 0x18
, 0x10, 0x0c, 0x01, 0x19, 0x16, 0x0a, 0x0f, 0x17
]
property
SBoxCorrect x0 x1 x2 x3 x4 =
[y0, y1, y2, y3, y4] == SBox [x0, x1, x2, x3, x4]
where
y0 = x4&&x1 ^ x3 ^ x2&&x1 ^ x2 ^ x1&&x0 ^ x1 ^ x0
y1 = x4 ^ x3&&x2 ^ x3&&x1 ^ x3 ^ x2&&x1 ^ x2 ^ x1 ^ x0
y2 = x4&&x3 ^ x4 ^ x2 ^ x1 ^ 1
y3 = x4&&x0 ^ x4 ^ x3&&x0 ^ x3 ^ x2 ^ x1 ^ x0
y4 = x4&&x1 ^ x4 ^ x3 ^ x1&&x0 ^ x1
// 3.4. Linear Diffusion Layer pL
/** The linear diffusion layer 𝑝𝐿 provides diffusion within each 64-bit
* word 𝑆𝑖.
*/
pL : State -> State
pL [S0, S1, S2, S3, S4] =
[ sigma S0 19 28
, sigma S1 61 39
, sigma S2 1 6
, sigma S3 10 17
, sigma S4 7 41
]
where
sigma : [64] -> [6] -> [6] -> [64]
sigma x i j = x ^ x>>>i ^ x>>>j
/** Concatenate a sequence of integers into a little-endian bitstream. */
wordsToBits : {w, n} (fin w) => [n][w] -> [w*n]
wordsToBits M = join (map reverse M)
/** Split a little-endian bitstream in to a sequence of integers. */
bitsToWords : {w, n} (fin w) => [w*n] -> [n][w]
bitsToWords M = map reverse (split M)
// 4. Authenticated Encryption Schema: Ascon-AEAD128
/** Ascon-AEAD128 encryption algorithm on bitstreams
*
* Type parameters:
* - a: Bit-length of associated data
* - p: Bit-length of plaintext
*
* Parameters:
* - K: Key
* - N: Nonce
* - A: Associated data
* - P: Plaintext
*
* Returns:
* - Ciphertext
* - Tag
*/
AEAD128_encrypt :
{a, p} (fin a, fin p) =>
[128] -> [128] -> [a] -> [p] -> ([p], [128])
AEAD128_encrypt K N A P = (C, T)
where
(M1, M2) = parse M
[K0, K1] = bitsToWords K
[N0, N1] = bitsToWords N
// 3. Ascon Permutations
// 𝑆𝐼𝑉𝐾𝑁 (15)
// 𝑆 ← Ascon-p[12](𝑆) (16)
// 𝑆𝑆 ⊕ (0¹⁹² ‖ 𝐾) (17)
S0 = Ascon_p`{12} [AEAD128_IV, K0, K1, N0, N1] ^ [0, 0, 0, K0, K1]
type constraint ValidRnd rnd = (1 <= rnd, rnd <= 16)
SA = AddAD S0 A
Ascon_p : {rnd} (ValidRnd rnd) => State -> State
Ascon_p S = foldl p`{rnd} S (drop`{back=rnd} Const)
// 𝑃₀, 𝑃₁, …, 𝑃ₙ₋₁, 𝑃͠ₙ ← parse(𝑃,128) (23)
// For each 𝑃ᵢ, 0 ≤ 𝑖𝑛1
// 𝑆[0:127] ← 𝑆[0:127] ⊕ 𝑃ᵢ (24)
// 𝐶ᵢ ← 𝑆[0:127] (25)
// 𝑆 ← Ascon-p[8](𝑆) (26)
// For the last block 𝑃͠ₙ
// 𝑆[0:127] ← 𝑆[0:127] ⊕ pad(𝑃͠ₙ,128) (27)
// 𝐶͠ₙ ← 𝑆[0:1] (28)
SCs = [XorBlock s p | s <- [SA] # map Ascon_p`{8} SCs | p <- toBlocks P]
p : {rnd} (ValidRnd rnd) => State -> [64] -> State
p S ci = pL (pS (pC S ci))
// 𝐶 ← 𝐶₀ ‖ … ‖ 𝐶ₙ₋₁ ‖ 𝐶͠ₙ (29)
C = take (join (map ExtractC SCs))
// 3.1. Internal State
// 𝑆𝑆 ⊕ (0¹²⁸ ‖ 𝐾 ‖ 0⁶⁴) (30)
// 𝑆 ← Ascon-p[12](𝑆) (31)
// 𝑇𝑆[192:319] ⊕ 𝐾 (32)
ST = Ascon_p`{12} (last SCs ^ [0, 0, K0, K1, 0])
T = ExtractT ST ^ K
type State = [5][64]
/** Ascon-AEAD128 decryption algorithm on bitstreams.
*
* Type parameters:
* - a: Bit-length of associated data
* - p: Bit-length of plaintext
*
* Parameters:
* - K: Key
* - N: Nonce
* - A: Associated data
* - C: Ciphertext
* - T: Tag
*
* Returns:
* - Some plaintext on authentication success
* - None on authentication failure
*/
AEAD128_decrypt :
{a, p} (fin a, fin p) =>
[128] -> [128] -> [a] -> [p] -> [128] -> Option [p]
AEAD128_decrypt K N A C T1 = if T1 == T2 then Some P else None
where
(P, T2) = AEAD128_decrypt_raw K N A C
// 3.2. Constant-Addition Layer pC
/** Raw Ascon-AEAD128 decryption algorithm on bitstreams.
*
* The tag returned by this function should be compared to the
* tag on the associated ciphertext to authenticate it.
*
* Type parameters:
* - a: Bit-length of associated data
* - p: Bit-length of plaintext
*
* Parameters:
* - K: Key
* - N: Nonce
* - A: Associated data
* - C: Ciphertext
*
* Returns:
* - P: Plaintext
* - T: Computed tag
*/
AEAD128_decrypt_raw :
{a, p} (fin a, fin p) =>
[128] -> [128] -> [a] -> [p] -> ([p], [128])
AEAD128_decrypt_raw K N A C = (P, T)
where
// key and nonce as two 64-bit integers
[K0, K1] = bitsToWords K
[N0, N1] = bitsToWords N
pC : State -> [64] -> State
pC [S0, S1, S2, S3, S4] ci = [S0, S1, S2 ^ ci, S3, S4]
S0 = Ascon_p`{12} [AEAD128_IV, K0, K1, N0, N1] ^ [0, 0, 0, K0, K1]
SA = AddAD S0 A
/// Table 5. The constants constᵢ to derive round constants of the Ascon permutations
Const : [16][64]
Const =
[ 0x000000000000003c
, 0x000000000000002d
, 0x000000000000001e
, 0x000000000000000f
, 0x00000000000000f0
, 0x00000000000000e1
, 0x00000000000000d2
, 0x00000000000000c3
, 0x00000000000000b4
, 0x00000000000000a5
, 0x0000000000000096
, 0x0000000000000087
, 0x0000000000000078
, 0x0000000000000069
, 0x000000000000005a
, 0x000000000000004b
]
(Cs, Cl1) = parse C
SCs = [SA] # [Ascon_p`{8} (AssignC s c) | s <- SCs | c <- Cs]
// 3.3. Substitution Layer pS
Mask # Cl2 = join (map ExtractC SCs)
P = Mask ^ C
pS : State -> State
pS S = transpose (map SBox (transpose S))
Cl = Cl1 # (0b1#0 ^ Cl2) // xor toggles the padding bit
ST1 = AssignC (last SCs) Cl
ST2 = Ascon_p`{12} (ST1 ^ [0, 0, K0, K1, 0])
T = ExtractT ST2 ^ K
SBox : [5] -> [5]
SBox i = SBoxTable@i
/** Ascon-AEAD128 encryption algorithm on bytes.
*
* Type parameters:
* - a: Byte-length of associated data
* - p: Byte-length of plaintext
*
* Parameters:
* - K: Key
* - N: Nonce
* - A: Associated data
* - P: Plaintext
*
* Returns:
* - C: Ciphertext
* - T: Tag
*/
AEAD128_encrypt_bytes :
{a, p} (fin a, fin p) =>
[16][8] -> [16][8] -> [a][8] -> [p][8] -> ([p][8], [16][8])
AEAD128_encrypt_bytes K N A P = (bitsToWords C, bitsToWords T)
where
(C, T) = AEAD128_encrypt K' N' A' P'
K' = wordsToBits K
N' = wordsToBits N
A' = wordsToBits A
P' = wordsToBits P
/// Table 6.
SBoxTable : [32][5]
SBoxTable =
map drop
[ 0x04, 0x0b, 0x1f, 0x14, 0x1a, 0x15, 0x09, 0x02, 0x1b, 0x05, 0x08, 0x12, 0x1d, 0x03, 0x06, 0x1c
, 0x1e, 0x13, 0x07, 0x0e, 0x00, 0x0d, 0x11, 0x18, 0x10, 0x0c, 0x01, 0x19, 0x16, 0x0a, 0x0f, 0x17
]
/** Ascon-AEAD128 decryption algorithm on bytes.
*
* Type parameters:
* - a: Byte-length of associated data
* - p: Byte-length of plaintext
*
* Parameters:
* - K: Key
* - N: Nonce
* - A: Associated data
* - C: Ciphertext
* - T: Tag
*
* Returns:
* - Some plaintext on authentication success
* - None on authentication failure
*/
AEAD128_decrypt_bytes :
{a, p} (fin a, fin p) =>
[16][8] -> [16][8] -> [a][8] -> [p][8] -> [16][8] -> Option ([p][8])
AEAD128_decrypt_bytes K N A C T =
case AEAD128_decrypt K' N' A' C' T' of
None -> None
Some p -> Some (bitsToWords p)
where
K' = wordsToBits K
N' = wordsToBits N
A' = wordsToBits A
C' = wordsToBits C
T' = wordsToBits T
// 3.4. Linear Diffusion Layer pL
private
/** Absorb all of the associated data into the permutation state. */
AddAD : {a} (fin a) => State -> [a] -> State
AddAD S A
pL : State -> State
pL [S0, S1, S2, S3, S4] =
[ sigma S0 19 28
, sigma S1 61 39
, sigma S2 1 6
, sigma S3 10 17
, sigma S4 7 41
]
where
sigma : [64] -> [6] -> [6] -> [64]
sigma x i j = x ^ (x >>> i) ^ (x >>> j)
/* If the AD is non-empty (i.e., |𝐴| > 0):
* 𝐴₀, 𝐴₁, …, 𝐴ₘ₋₁, 𝐴͠ₘ ← parse(𝐴,128) (18)
* 𝐴ₘ ← pad(𝐴͠ₘ,128) (19)
*/
| a > 0 => DomainSep (foldl AbsorbADBlock S (toBlocks A))
little_bytes : {n} (fin n) => [8*n] -> [8*n]
little_bytes M = join (map reverse (groupBy`{8} M))
/* If the AD is empty (i.e., |𝐴| = 0): Only the final step
* described in (22) is applied.
*/
| a == 0 => DomainSep S
/* Absorb a block in input into the state.
* 𝑆[0:127] ← 𝑆[0:127] ⊕ 𝐴ᵢ (20)
* 𝑆 ← Ascon-p[8](𝑆) (21)
**/
AbsorbADBlock : State -> [128] -> State
AbsorbADBlock S Ai = Ascon_p`{8} (XorBlock S Ai)
/** Toggle the domain separation bit indicating end of associated data.
* 𝑆𝑆 ⊕ (0³¹⁹ ‖ 1) (22)
*/
DomainSep : State -> State
DomainSep [s0, s1, s2, s3, s4] = [s0, s1, s2, s3, s4 ^ 0b1#0]
/** Add a single block of data into the permutation state. */
XorBlock : State -> [128] -> State
XorBlock [s0, s1, s2, s3, s4] (xhi # xlo) =
[s0 ^ xlo, s1 ^ xhi, s2, s3, s4]
/** Extracts the first two words of permutation state as a bitstream. */
ExtractC : State -> [128]
ExtractC [s0, s1, _, _, _] = wordsToBits [s0, s1]
/** Assigns a bitstream into the first two words of permutation state. */
AssignC : State -> [128] -> State
AssignC [_, _, s2, s3, s4] C = bitsToWords C # [s2, s3, s4]
/** Extracts the last two words of permutation state as a bitstream. */
ExtractT : State -> [128]
ExtractT [_, _, _, s3, s4] = wordsToBits [s3, s4]
/** Ascon-AEAD128 initialization vector */
AEAD128_IV : [64]
AEAD128_IV = 0x00001000808c0001
// 5. Hash and eXtendable-Output Functions (XOFs)
private
/** Hash function construction
*
* All three of the modes in Ascon use the same message
* absorbtion and
*
* Parameters:
* - Initialization vector
* - List of blocks as integers
*
* Returns:
* - Infinite message digest bitstream to be truncated by caller
*/
hashBlocks : {n} (fin n) => [64] -> [n][64] -> [inf]
hashBlocks IV Ms = H
where
// 𝑆 ← Ascon-p[12](𝐼𝑉 ‖ 0²⁵⁶) (54)
S0 = Ascon_p`{12} [IV, 0, 0, 0, 0]
// Each message block 𝑀ᵢ is XORed with the state as
// 𝑆[0:63] ← 𝑆[0:63] ⊕ 𝑀ᵢ (57)
// 𝑆 ← Ascon-p[12](𝑆) (58)
Sn = foldl AbsorbBlock64 S0 Ms
// Each output hash block is computed as
// 𝐻ᵢ ← 𝑆[0:63] (60)
// 𝑆 ← Ascon-p[12](𝑆) (61)
// 𝐻ᵢ ← 𝐻₀ ‖ 𝐻₁ ‖ …
H = wordsToBits [head S | S <- iterate Ascon_p`{12} Sn]
/** Absorb a block of hash input into the permutation state. */
AbsorbBlock64 : State -> [64] -> State
AbsorbBlock64 [s0, s1, s2, s3, s4] Mi = Ascon_p`{12} [s0 ^ Mi, s1, s2, s3, s4]
// 5.1. Specification of Ascon-Hash256
/** Ascon-Hash256 implementation on bitstreams.
*
* Type parameters:
* - m: Bit-length of message
*
* Parameters:
* - M: Message
*
* Returns:
* - Message digest
*/
Hash256 : {m} (fin m) => [m] -> [256]
Hash256 M = take (hashBlocks Hash256_IV (toBlocks M))
/** Ascon-Hash256 implementation on bytes.
*
* Type parameters:
* - m: Byte-length of message
*
* Parameters:
* - M: Message
*
* Returns:
* - Message digest
*/
Hash256_bytes : {m} (fin m) => [m][8] -> [32][8]
Hash256_bytes M = bitsToWords (Hash256 (wordsToBits M))
/** Ascon-Hash256 initialization vector */
Hash256_IV : [64]
private Hash256_IV = 0x0000080100cc0002
// 5.2. Specification of Ascon-XOF128
/** Ascon-XOF256 implementation on bitstreams.
*
* Type parameters:
* - r: Bit-length of digest
* - m: Bit-length of message
*
* Parameters:
* - M: Message
*
* Returns:
* - Variable-length message digest
*/
XOF128 : {r, m} (fin m, fin r) => [m] -> [r]
XOF128 M = take (hashBlocks XOF128_IV (toBlocks M))
/** Ascon-XOF256 implementation on bytes.
*
* Type parameters:
* - r: Byte-length of digest
* - m: Byte-length of message
*
* Parameters:
* - M: Message
*
* Returns:
* - Variable-length message digest
*/
XOF128_bytes : {r, n} (fin n, fin r) => [n][8] -> [r][8]
XOF128_bytes M = bitsToWords (XOF128 (wordsToBits M))
/** Ascon-XOF128 initialization vector */
XOF128_IV : [64]
private XOF128_IV = 0x0000080000cc0003
// 5.3. Specification of Ascon-CXOF128
/** Ascon-CXOF256 implementation on bitstreams.
*
* Type parameters:
* - r: Bit-length of digest
* - c: Bit-length of customization string
* - m: Bit-length of message
*
* Parameters:
* - Z: User-defined customization string
* - M: Message
*
* Returns:
* - Variable-length message digest
*/
CXOF128 :
{r, z, m} (fin m, fin r, 64 >= width z) =>
[z] -> [m] -> [r]
CXOF128 Z M = take (hashBlocks CXOF128_IV Ms)
where
Ms = [`z]
# toBlocks Z
# toBlocks M
/** Ascon-CXOF256 implementation on bytes.
*
* Type parameters:
* - r: Byte-length of digest
* - z: Byte-length of customization string
* - m: Byte-length of message
*
* Parameters:
* - Z: User-defined customization string
* - M: Message
*
* Returns:
* - Variable-length message digest
*/
CXOF128_bytes :
{r, z, m} (fin m, fin r, 61 >= width z) =>
[z][8] -> [m][8] -> [r][8]
CXOF128_bytes Z M = bitsToWords (CXOF128 (wordsToBits Z) (wordsToBits M))
/** Ascon-CXOF128 initialization vector */
CXOF128_IV : [64]
private CXOF128_IV = 0x0000080000cc0004
// Appendix B. Determination of the Initial Values
private
computeIV : [8] -> [4] -> [4] -> [16] -> [8] -> [64]
computeIV v a b t r =
// 𝐼𝑉 = 𝑣 ‖ 0⁸ ‖ 𝑎𝑏𝑡𝑟/8 ‖ 0¹⁶
reverse ( reverse v // is a unique identifier for the algorithm
# (0:[8])
# reverse a // is the number of rounds during initialization and finalization
# reverse b // is the number of rounds during the processing of AD, P, C, M
# reverse t // is 128 for AEAD128, 256 for Hash256, and 0 for XOF128/CXOF128
# reverse r // is the number of input bytes processed per permutation invocation
# (0:[16]))
property check_AEAD128_IV = AEAD128_IV == computeIV 1 12 8 128 16
property check_Hash256_IV = Hash256_IV == computeIV 2 12 12 256 8
property check_XOF128_IV = XOF128_IV == computeIV 3 12 12 0 8
property check_CXOF128_IV = CXOF128_IV == computeIV 4 12 12 0 8

View File

@@ -1,43 +0,0 @@
module AsconCipher where
import Ascon
// 4. Authenticated Encryption Schema: Ascon-AEAD128
Ascon_AEAD128 : {a, p} (fin a, fin p) => [128] -> [128] -> [a] -> [p] -> [p + 128]
Ascon_AEAD128 (Khi_ # Klo_) (Nhi_ # Nlo_) A P = C # reverse T
where
Khi = reverse Khi_
Klo = reverse Klo_
Nhi = reverse Nhi_
Nlo = reverse Nlo_
S0 = Ascon_p`{12} [Ascon_AEAD128_IV, Khi, Klo, Nhi, Nlo]
^ [0, 0, 0, Khi, Klo]
SA = AddAD S0 A
SCs = zipWith XorBlock (take ([SA] # map Ascon_p`{8} SCs)) (toBlocks P)
C = take (join [reverse s0 # reverse s1 | [s0, s1, _, _, _] <- SCs])
ST = Ascon_p`{12} (last SCs ^ [0, 0, Khi, Klo, 0])
T = ST@(4:[3]) # ST@(3:[3])
^ Klo # Khi
AddAD : {a} (fin a) => State -> [a] -> State
AddAD S A
| a == 0 => DomainSep S
| a > 0 => DomainSep (foldl AbsorbBlock S (toBlocks A))
XorBlock : State -> [128] -> State
XorBlock [s0, s1, s2, s3, s4] (x0 # x1) = [s0 ^ reverse x0, s1 ^ reverse x1, s2, s3, s4]
AbsorbBlock : State -> [128] -> State
AbsorbBlock S X = Ascon_p`{8} (XorBlock S X)
DomainSep : State -> State
DomainSep [s0, s1, s2, s3, s4] = [s0, s1, s2, s3, s4 ^ 0b1 # 0]
Ascon_AEAD128_IV : [64]
Ascon_AEAD128_IV = 0x00001000808c0001

View File

@@ -1,56 +0,0 @@
module AsconHash where
import Ascon
// 5. Hash and eXtendable-Output Functions (XOFs)
Ascon_Digest : {n} (fin n) => [64] -> [n][64] -> [inf]
Ascon_Digest IV Ms = join [reverse (head S) | S <- iterate Ascon_p`{12} Sn]
where
S0 = Ascon_p`{12} [IV, 0, 0, 0, 0]
Sn = foldl AbsorbBlock S0 Ms
AbsorbBlock : State -> [64] -> State
AbsorbBlock [s0, s1, s2, s3, s4] X = Ascon_p`{12} [reverse X ^ s0, s1, s2, s3, s4]
/// 5.1. Specification of Ascon-Hash256
Ascon_Hash256 : {n} (fin n) => [n] -> [256]
Ascon_Hash256 M = take (Ascon_Digest Ascon_Hash256_IV (toBlocks M))
Ascon_Hash256_IV : [64]
Ascon_Hash256_IV = 0x0000080100cc0002
Ascon_Hash256_bytes : {n} (fin n) => [n][8] -> [32][8]
Ascon_Hash256_bytes M = map reverse (split (Ascon_Hash256 (join (map reverse M))))
property
initial_value_works =
Ascon_p`{12} ([Ascon_Hash256_IV] # zero)
==
[0x9b1e5494e934d681, 0x4bc3a01e333751d2, 0xae65396c6b34b81a, 0x3c7fd4a4d56a4db3, 0x1a5c464906c5976d]
// 5.2. Specification of Ascon-XOF128
Ascon_XOF128 : {r, n} (fin n, fin r) => [n] -> [r]
Ascon_XOF128 M = take (Ascon_Digest Ascon_XOF128_IV (toBlocks M))
Ascon_XOF128_IV : [64]
Ascon_XOF128_IV = 0x0000080000cc0003
Ascon_XOF128_bytes : {r, n} (fin n, fin r) => [n][8] -> [r][8]
Ascon_XOF128_bytes M = map reverse (split (Ascon_XOF128 (join (map reverse M))))
// 5.3. Specification of Ascon-CXOF128
Ascon_CXOF128 : {r, c, n} (fin n, fin r, fin c, 64 >= width c) => [c] -> [n] -> [r]
Ascon_CXOF128 Z M = take (Ascon_Digest Ascon_CXOF128_IV Ms)
where
Ms = [reverse `c]
# toBlocks Z
# toBlocks M
Ascon_CXOF128_bytes : {r, z, n} (fin n, fin r, 61 >= width z) => [z][8] -> [n][8] -> [r][8]
Ascon_CXOF128_bytes Z M = map reverse (split (Ascon_CXOF128 (join (map reverse Z)) (join (map reverse M))))
Ascon_CXOF128_IV : [64]
Ascon_CXOF128_IV = 0x0000080000cc0004

13
LICENSE Normal file
View File

@@ -0,0 +1,13 @@
Copyright 2025 Eric Mertens
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *

21
Makefile Normal file
View File

@@ -0,0 +1,21 @@
.PHONY: test test-hash256 test-xof128 test-cxof128 test-aead128 saw-proofs
CRYPTOL ?= cryptol
SAW ?= saw
test: test-hash256 test-xof128 test-cxof128 test-aead128
test-hash256:
$(CRYPTOL) -c :exhaust TestAsconHash256.cry
test-xof128:
$(CRYPTOL) -c :exhaust TestAsconXOF128.cry
test-cxof128:
$(CRYPTOL) -c :exhaust TestAsconCXOF128.cry
test-aead128:
$(CRYPTOL) -c :exhaust TestAsconAEAD128.cry
saw-proofs:
$(SAW) verify.saw

View File

@@ -1,12 +1,14 @@
module TestAsconAEAD128 where
import Ascon
import AsconCipher
testcase : {n, m} (fin m, fin n) => [128] -> [128] -> [8 * m] -> [8 * n] -> [8 * (m + 16)] -> Bit
testcase K N P A C =
Ascon_AEAD128 (little_bytes K) (little_bytes N) (little_bytes A) (little_bytes P)
== little_bytes C
testcase K N P A (C # T) =
AEAD128_encrypt_bytes (split K) (split N) (split A) (split P) == (split C, split T)
/\
case AEAD128_decrypt_bytes (split K) (split N) (split A) (split C) (split T) of
None -> False
Some p -> p == split P
property
test1 = testcase 0x000102030405060708090A0B0C0D0E0F 0x101112131415161718191A1B1C1D1E1F [] 0x 0x4F9C278211BEC9316BF68F46EE8B2EC6

View File

@@ -1,9 +1,9 @@
module TestAsconCXOF128 where
import AsconHash
import Ascon
testcase : {r, z, n} (fin n, fin r, 61 >= width z) => [8*n] -> [8*z] -> [8*r] -> Bool
testcase M Z D = join (Ascon_CXOF128_bytes (split Z) (split M)) == D
testcase M Z D = CXOF128_bytes (split Z) (split M) == split D
property
test1 = testcase [] [] 0x4F50159EF70BB3DAD8807E034EAEBD44C4FA2CBBC8CF1F05511AB66CDCC529905CA12083FC186AD899B270B1473DC5F7EC88D1052082DCDFE69FB75D269E7B74

View File

@@ -1,9 +1,9 @@
module TestAsconHash256 where
import AsconHash
import Ascon
testcase : {n} (fin n) => [8*n] -> [256] -> Bool
testcase M D = join (Ascon_Hash256_bytes (split M)) == D
testcase M D = Hash256_bytes (split M) == split D
property
test1 = testcase [] 0x0B3BE5850F2F6B98CAF29F8FDEA89B64A1FA70AA249B8F839BD53BAA304D92B2

View File

@@ -1,9 +1,9 @@
module TestAsconXOF128 where
import AsconHash
import Ascon
testcase : {r, n} (fin n, fin r) => [8*n] -> [8*r] -> Bool
testcase M D = join (Ascon_XOF128_bytes (split M)) == D
testcase M D = XOF128_bytes (split M) == split D
property
test1 = testcase [] 0x473D5E6164F58B39DFD84AACDB8AE42EC2D91FED33388EE0D960D9B3993295C6AD77855A5D3B13FE6AD9E6098988373AF7D0956D05A8F1665D2C67D1A3AD10FF

13
verify.saw Normal file
View File

@@ -0,0 +1,13 @@
import "Ascon.cry";
for [0,1,2,16,63,64,65,127,128,129,255,256] (\a ->
for [0,1,2,16,63,64,65,127,128,129,255,256] (\p -> do {
print ("decrypt_encrypt",a,p);
prove_print (unint_yices ["Ascon::Ascon_p"]) {{
\K N (A : [0]) (P : [128]) ->
(T == T'
where
(C, T) = AEAD128_encrypt K N A P
(P', T') = AEAD128_decrypt_raw K N A C)
}};
}));