-
Notifications
You must be signed in to change notification settings - Fork 8
Poly1305 RFC to hacpsec and F*
Franziskus Kiefer edited this page Nov 6, 2018
·
1 revision
ChaCha20Poly1305 is specified in RFC 7539. It specifies Poly1305 as follows in Section 2.5.1.
clamp(r): r &= 0x0ffffffc0ffffffc0ffffffc0fffffff
poly1305_mac(msg, key):
r = (le_bytes_to_num(key[0..15])
clamp(r)
s = le_num(key[16..31])
accumulator = 0
p = (1<<130)-5
for i=1 upto ceil(msg length in bytes / 16)
n = le_bytes_to_num(msg[((i-1)*16)..(i*16)] | [0x01])
a += n
a = (r * a) % p
end
a += s
return num_to_16_le_bytes(a)
end
The algorithm can be easily written in hacspec. The following gives the hacspec versions of the corresponding RFC definitions for poly1305 as well as the compiled F*.
The RFC defines the Poly1305 parameters as:
A 256-bit one-time key
The output is a 128-bit tag.
set the constant prime "P" be 2^130-5: 3fffffffffffffffffffffffffffffffb.
divide the message into 16-byte blocks
blocksize:int = 16
block_t = bytes_t(16)
key_t = bytes_t(32)
tag_t = bytes_t(16)
subblock_t = refine_t(vlbytes_t, lambda x: bytes.length(x) <= 16)
p130m5 : nat_t = (2 ** 130) - 5
felem_t = natmod_t(p130m5)
def felem(n:nat_t) -> felem_t:
return natmod(n,p130m5)
let blocksize: int = 16
let block_t: Type0 = array_t uint8_t 16
let key_t: Type0 = array_t uint8_t 32
let tag_t: Type0 = array_t uint8_t 16
let subblock_t: Type0 = (x: vlbytes_t{(bytes_length x) <=. 16})
let p130m5: nat_t = (2 **. 130) -. 5
let felem_t: Type0 = natmod_t p130m5
let felem (n: nat_t) : felem_t = natmod n p130m5
le_bytes_to_num(msg[((i-1)*16)..(i*16)] | [0x01])
def encode(block: subblock_t) -> felem_t:
b : block_t = array.create(16, uint8(0))
b[0:bytes.length(block)] = block
welem : felem_t = felem(bytes.to_nat_le(b))
lelem : felem_t = felem(2 ** (8 * array.length(block)))
return lelem + welem
let encode (block: subblock_t) : felem_t =
let b = array_create 16 (uint8 0) in
let b = array_update_slice b 0 (bytes_length block) (block) in
let welem = felem (bytes_to_nat_le b) in
let lelem = felem (2 **. (8 *. (array_length block))) in
lelem +. welem
clamp(r): r &= 0x0ffffffc0ffffffc0ffffffc0fffffff
def encode_r(r: block_t) -> felem_t:
ruint : uint128_t = bytes.to_uint128_le(r) # r = (le_bytes_to_num(key[0..15])
ruint = ruint & uint128(0x0ffffffc0ffffffc0ffffffc0fffffff) # r &= 0x0ffffffc0ffffffc0ffffffc0fffffff
r_nat : nat_t = uintn.to_nat(ruint)
return felem(r_nat)
let encode_r (r: block_t) : felem_t =
let ruint = bytes_to_uint128_le r in
let ruint = ruint &. (uint128 21267647620597763993911028882763415551) in
let r_nat = uintn_to_nat ruint in
felem r_nat
a = 0
for i=1 upto ceil(msg length in bytes / 16)
n = le_bytes_to_num(msg[((i-1)*16)..(i*16)] | [0x01])
a += n
a = (r * a) % p
e
def poly(text: vlbytes_t, r: felem_t) -> felem_t:
blocks : vlarray_t(block_t)
last : subblock_t
blocks, last = array.split_blocks(text, blocksize)
acc : felem_t = felem(0)
for i in range(array.length(blocks)): # for i=1 upto ceil(msg length in bytes / 16)
acc = (acc + encode(blocks[i])) * r # n = le_bytes_to_num(msg[((i-1)*16)..(i*16)] | [0x01])
# a += n
# a = (r * a) % p
if (array.length(last) > 0):
acc = (acc + encode(last)) * r
return acc
let poly (text: vlbytes_t) (r: felem_t) : felem_t =
let blocks, last = array_split_blocks text blocksize in
let acc = felem 0 in
let acc = repeati (array_length blocks)
(fun i acc -> (acc +. (encode blocks.[ i ])) *. r) acc in
let acc = if (array_length last) >. 0 then
(acc +. (encode last)) *. r else acc in
acc
poly1305_mac(msg, key):
r = (le_bytes_to_num(key[0..15])
clamp(r)
s = le_num(key[16..31])
p = (1<<130)-5
a = poly(msg, r)
a += s
return num_to_16_le_bytes(a)
end
def poly1305_mac(text: vlbytes_t, k: key_t) -> tag_t:
r : block_t = k[0:blocksize] # key[0..15]
s : block_t = k[blocksize:2*blocksize] # key[16..31]
relem : felem_t = encode_r(r)
selem : uint128_t = bytes.to_uint128_le(s) # s = le_num(key[16..31])
a : felem_t = poly(text, relem)
n : uint128_t = uint128(natmod.to_nat(a)) + selem # a += s
return bytes.from_uint128_le(n) # num_to_16_le_bytes(a)
let poly1305_mac (text: vlbytes_t) (k: key_t) : tag_t =
let r = array_slice k 0 blocksize in
let s = array_slice k blocksize (2 *. blocksize) in
let relem = encode_r r in
let selem = bytes_to_uint128_le s in
let a = poly text relem in
let n = (uint128 ((natmod_to_nat a) % (2 **. 128))) +. selem in
bytes_from_uint128_le n