Module: Nuckle::Internals::Poly1305

Defined in:
lib/nuckle/internals/poly1305.rb

Overview

Poly1305 one-time message authenticator.

Reference: cr.yp.to/mac/poly1305-20050329.pdf

RFC 8439 Section 2.5

Constant Summary collapse

P =
(1 << 130) - 5
R_CLAMP =

Clamp mask for r (clear specific bits per spec)

0x0ffffffc0ffffffc0ffffffc0fffffff
MASK128 =
(1 << 128) - 1

Class Method Summary collapse

Class Method Details

.int_to_le_bytes(n) ⇒ Object

Write a 128-bit integer as 16 little-endian bytes.



65
66
67
# File 'lib/nuckle/internals/poly1305.rb', line 65

def int_to_le_bytes(n)
  [n & 0xFFFFFFFFFFFFFFFF, (n >> 64) & 0xFFFFFFFFFFFFFFFF].pack("Q<2")
end

.le_bytes_to_int(bytes, offset, length) ⇒ Object

Read little-endian integer from a string at offset, length bytes.



53
54
55
56
57
58
59
60
61
62
# File 'lib/nuckle/internals/poly1305.rb', line 53

def le_bytes_to_int(bytes, offset, length)
  if length == 16
    lo, hi = bytes.unpack("@#{offset}Q<2")
    lo | (hi << 64)
  else
    n = 0
    length.times { |i| n |= bytes.getbyte(offset + i) << (8 * i) }
    n
  end
end

.mac(key, message) ⇒ String

Compute Poly1305 MAC.

Parameters:

  • key (String)

    32-byte one-time key (r || s)

  • message (String)

    message to authenticate

Returns:

  • (String)

    16-byte authentication tag



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/nuckle/internals/poly1305.rb', line 26

def mac(key, message)
  r = le_bytes_to_int(key, 0, 16) & R_CLAMP
  s = le_bytes_to_int(key, 16, 16)

  h   = 0
  msg = message.b
  len = msg.bytesize
  off = 0

  while off < len
    remaining = len - off
    take      = remaining < 16 ? remaining : 16
    block     = le_bytes_to_int(msg, off, take)

    # Add high bit (2^(8*take)) to mark block as non-zero-padded
    block |= 1 << (take * 8)

    h = ((h + block) * r) % P
    off += take
  end

  # Final: (h + s) mod 2^128
  tag = (h + s) & MASK128
  int_to_le_bytes(tag)
end