Files
ascon/Ascon.cry
2025-09-04 18:30:47 -07:00

300 lines
9.2 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
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
*/
module Ascon where
// 2.1. Auxiliary Functions
/// 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, n} (fin n, fin r, r >= 1) => [n] -> ([n/r][r], [n%r])
parse (M_ # Ml) = (split M_, Ml)
/// Padding rule.
///
/// The function pad(𝑋, 𝑟) appends the bit 1 to the bitstring 𝑋, followed
/// bythe 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, n} (n < r, fin r) => [n] -> [r]
pad M = M # 0b1 # 0
/// Combination of parse and pad that splits a bitstring into a sequence
/// of integers using Cryptol's native big-endian representation.
toBlocks : {r, n} (r >= 1, fin r, fin n) => [n] -> [n / r + 1][r]
toBlocks M = map reverse (M1 # [pad M2])
where
(M1, M2) = parse M
// 3. Ascon Permutations
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.
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
pS : State -> State
pS S = transpose (map SBox (transpose S))
SBox : [5] -> [5]
SBox i = SBoxTable@i
/// 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
]
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
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)
wordsToBits : {w,n} (fin w) => [n][w] -> [w*n]
wordsToBits M = join (map reverse M)
bitsToWords : {w,n} (fin w) => [w*n] -> [n][w]
bitsToWords M = map reverse (split M)
// 4. Authenticated Encryption Schema: Ascon-AEAD128
/// Encryption using Ascon-AEAD128
///
/// Parameters:
/// - Key
/// - Nonce
/// - Additional data
/// - Plaintext
///
/// Returns:
/// - Authenticated ciphertext
AEAD128_encrypt : {a, p} (fin a, fin p) => [128] -> [128] -> [a] -> [p] -> [p + 128]
AEAD128_encrypt K N A P = C # T
where
// key and nonce as two 64-bit integers
[K0,K1] = bitsToWords K
[N0,N1] = bitsToWords N
S0 = Ascon_p`{12} [Ascon_AEAD128_IV, K0, K1, N0, N1] ^ [0, 0, 0, K0, K1]
SA = AddAD S0 A
SCs = [XorBlock s p | s <- [SA] # map Ascon_p`{8} SCs | p <- toBlocks P]
C = take (join (map ExtractC SCs))
ST = Ascon_p`{12} (last SCs ^ [0, 0, K0, K1, 0])
T = ExtractT ST ^ K
/// Decryption using Ascon-AEAD128
///
/// Parameters:
/// - Key
/// - Nonce
/// - Additional data
/// - Ciphertext
///
/// Returns:
/// - Some plaintext on success
/// - None when message authentication fails
AEAD128_decrypt : {a, p} (fin a, fin p) => [128] -> [128] -> [a] -> [p + 128] -> Option [p]
AEAD128_decrypt K N A (Cs_ # Cl # T) =
if T == T' then Some P else trace "P" P None
where
// key and nonce as two 64-bit integers
[K0,K1] = bitsToWords K
[N0,N1] = bitsToWords N
S0 = Ascon_p`{12} [Ascon_AEAD128_IV, K0, K1, N0, N1] ^ [0, 0, 0, K0, K1]
SA = AddAD S0 A
Cs = split`{p/128, 128} Cs_
SCs # [SCl] = [SA] # [Ascon_p`{8} (AssignC s c) | s <- SCs | c <- Cs]
Plmask # SCl' = ExtractC SCl
Sl' = AssignC SCl (Cl # (0b1#0 ^ SCl'))
Ps = zipWith (\x y -> ExtractC x ^ y) SCs Cs
Pl = Plmask ^ Cl
P = join Ps # Pl
ST = Ascon_p`{12} (Sl' ^ [0, 0, K0, K1, 0])
T' = ExtractT ST ^ K
AEAD128_encrypt_bytes : {a, p} (fin a, fin p) => [16][8] -> [16][8] -> [a][8] -> [p][8] -> [p + 16][8]
AEAD128_encrypt_bytes K N A P =
bitsToWords (AEAD128_encrypt (wordsToBits K) (wordsToBits N) (wordsToBits A) (wordsToBits P))
AEAD128_decrypt_bytes : {a, p} (fin a, fin p) => [16][8] -> [16][8] -> [a][8] -> [p + 16][8] -> Option ([p][8])
AEAD128_decrypt_bytes K N A C =
case AEAD128_decrypt (wordsToBits K) (wordsToBits N) (wordsToBits A) (wordsToBits C) of
None -> None
Some p -> Some (bitsToWords p)
AddAD : {a} (fin a) => State -> [a] -> State
AddAD S A
| a == 0 => DomainSep S
| a > 0 => DomainSep (foldl AbsorbBlock128 S (toBlocks A))
DomainSep : State -> State
DomainSep [s0, s1, s2, s3, s4] = [s0, s1, s2, s3, s4 ^ 0b1 # 0]
AbsorbBlock128 : State -> [128] -> State
AbsorbBlock128 S X = Ascon_p`{8} (XorBlock S X)
XorBlock : State -> [128] -> State
XorBlock [s0, s1, s2, s3, s4] (xhi # xlo) = [s0 ^ xlo, s1 ^ xhi, s2, s3, s4]
ExtractC : State -> [128]
ExtractC [s0, s1, _, _, _] = wordsToBits [s0, s1]
AssignC : State -> [128] -> State
AssignC [_, _, s2, s3, s4] C = bitsToWords C # [s2, s3, s4]
ExtractT : State -> [128]
ExtractT [_, _, _, s3, s4] = wordsToBits [s3, s4]
Ascon_AEAD128_IV : [64]
Ascon_AEAD128_IV = 0x00001000808c0001
// 5. Hash and eXtendable-Output Functions (XOFs)
hashBlocks : {n} (fin n) => [64] -> [n][64] -> [inf]
hashBlocks IV Ms = wordsToBits [head S | S <- iterate Ascon_p`{12} Sn]
where
S0 = Ascon_p`{12} [IV, 0, 0, 0, 0]
Sn = foldl AbsorbBlock64 S0 Ms
AbsorbBlock64 : State -> [64] -> State
AbsorbBlock64 [s0, s1, s2, s3, s4] X = Ascon_p`{12} [X ^ s0, s1, s2, s3, s4]
/// 5.1. Specification of Ascon-Hash256
Hash256 : {n} (fin n) => [n] -> [256]
Hash256 M = take (hashBlocks Hash256_IV (toBlocks M))
Hash256_bytes : {n} (fin n) => [n][8] -> [32][8]
Hash256_bytes M = bitsToWords (Hash256 (wordsToBits M))
Hash256_IV : [64]
Hash256_IV = 0x0000080100cc0002
// 5.2. Specification of Ascon-XOF128
XOF128 : {r, n} (fin n, fin r) => [n] -> [r]
XOF128 M = take (hashBlocks XOF128_IV (toBlocks M))
XOF128_bytes : {r, n} (fin n, fin r) => [n][8] -> [r][8]
XOF128_bytes M = bitsToWords (XOF128 (wordsToBits M))
XOF128_IV : [64]
XOF128_IV = 0x0000080000cc0003
// 5.3. Specification of Ascon-CXOF128
CXOF128 : {r, c, n} (fin n, fin r, fin c, 64 >= width c) => [c] -> [n] -> [r]
CXOF128 Z M = take (hashBlocks CXOF128_IV Ms)
where
Ms = [`c]
# toBlocks Z
# toBlocks M
CXOF128_bytes : {r, z, n} (fin n, fin r, 61 >= width z) => [z][8] -> [n][8] -> [r][8]
CXOF128_bytes Z M = bitsToWords (CXOF128 (wordsToBits Z) (wordsToBits M))
CXOF128_IV : [64]
CXOF128_IV = 0x0000080000cc0004