class Hmac

class Hmac

lib/hmac.tya:6

Hmac provides keyed message authentication helpers.

Source
# Hmac provides keyed message authentication helpers.
class Hmac
  # Hmac.algorithm stores instance state.
  # @type Nil
  algorithm: nil

  # Hmac.key stores instance state.
  # @type Nil
  key: nil

  # Hmac.initialize stores the algorithm and key.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @return Self the initialized object.
  initialize: algorithm, key ->
    self.algorithm = algorithm
    self.key = key

  # Hmac.base64digest returns the HMAC digest as Base64.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @return Any the resulting value.
  base64digest: algorithm, key = nil, message = nil ->
    if self.algorithm != nil
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    base64.Base64.encode(Hmac(algorithm, key).digest(message))

  # Hmac.block_size returns the digest block size for algorithm.
  # @param algorithm Any algorithm value.
  # @return Any the resulting value.
  block_size: algorithm ->
    if algorithm == "sha256"
      return 64
    if algorithm == "sha384" or algorithm == "sha512"
      return 128
    raise error("hmac.algorithm: unsupported algorithm", { kind: "crypto", code: "unsupported_algorithm" })

  # Hmac.constant_time_equal compares equal-length byte sequences.
  # @param left Any left value.
  # @param right Any right value.
  # @return Any the resulting value.
  constant_time_equal: left, right ->
    a = bytes_array(left)
    b = bytes_array(right)
    if a.len() != b.len()
      return false
    diff = 0
    i = 0
    while i < a.len()
      diff = diff | a[i] ^ b[i]
      i = i + 1
    diff == 0

  # Hmac.digest returns the raw HMAC digest bytes.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @return Any the resulting value.
  digest: algorithm, key = nil, message = nil ->
    if self.algorithm != nil
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    hex.Hex(nil).decode(Hmac(algorithm, key).hexdigest(message))

  # Hmac.digest_size returns the digest byte size for algorithm.
  # @param algorithm Any algorithm value.
  # @return Any the resulting value.
  digest_size: algorithm ->
    if algorithm == "sha256"
      return 32
    if algorithm == "sha384"
      return 48
    if algorithm == "sha512"
      return 64
    raise error("hmac.algorithm: unsupported algorithm", { kind: "crypto", code: "unsupported_algorithm" })

  # Hmac.expected_bytes decodes a verification digest.
  # @param algorithm Any algorithm value.
  # @param expected Any expected value.
  # @param options Dict options value.
  # @return Any the resulting value.
  expected_bytes: algorithm, expected, options ->
    algorithm
    encoding = nil
    if options != nil
      if options.class != Dict
        raise error("hmac.verify: options must be a dictionary", { kind: "crypto", code: "invalid_options" })
      for entry in options
        if entry["key"] != "encoding"
          raise error("hmac.verify: unknown option " + entry["key"], { kind: "crypto", code: "unknown_option" })
      encoding = options["encoding"]
    if expected.class == String
      if encoding == nil
        encoding = "hex"
      if encoding == "hex"
        return hex.Hex(expected).decode()
      if encoding == "base64"
        return base64.Base64.decode(expected)
      raise error("hmac.verify: invalid encoding", { kind: "crypto", code: "invalid_encoding" })
    if encoding == nil or encoding == "raw"
      return expected
    raise error("hmac.verify: bytes expected value requires raw encoding", { kind: "crypto", code: "invalid_encoding" })

  # Hmac.hash_hex hashes value with algorithm and returns lowercase hex.
  # @param algorithm Any algorithm value.
  # @param value String value value.
  # @return Any the resulting value.
  hash_hex: algorithm, value ->
    if algorithm == "sha256"
      return digest.Digest.sha256(value)
    if algorithm == "sha384"
      return digest.Digest.sha384(value)
    if algorithm == "sha512"
      return digest.Digest.sha512(value)
    raise error("hmac.algorithm: unsupported algorithm", { kind: "crypto", code: "unsupported_algorithm" })

  # Hmac.hexdigest returns the HMAC digest as lowercase hex.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @return Any the resulting value.
  hexdigest: algorithm, key = nil, message = nil ->
    if self.algorithm != nil
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    size = self.block_size(algorithm)
    key_bytes = self.input_bytes("key", key)
    if bytes_array(key_bytes).len() > size
      key_bytes = hex.Hex(nil).decode(self.hash_hex(algorithm, key_bytes))
    key_block = self.pad(key_bytes, size)
    inner = self.xor(key_block, 54)
    outer = self.xor(key_block, 92)
    msg = self.input_bytes("message", message)
    inner_hash = hex.Hex(nil).decode(
      self.hash_hex(algorithm, bytes_concat(inner, msg))
    )
    self.hash_hex(algorithm, bytes_concat(outer, inner_hash))

  # Hmac.input_bytes normalizes string or bytes input.
  # @param name String name value.
  # @param value String value value.
  # @return Any the resulting value.
  input_bytes: name, value ->
    if value.class == String
      return bytes_of(value)
    if value.class.name == "Bytes"
      return value
    raise error("hmac." + name + ": value must be a string or bytes", { kind: "crypto", code: "invalid_input" })

  # Hmac.pad right-pads data with zeros to size.
  # @param data Array data value.
  # @param size Int size value.
  # @return Any the resulting value.
  pad: data, size ->
    arr = bytes_array(data)
    while arr.len() < size
      arr.push(0)
    bytes(arr)

  # Hmac.verify checks an expected digest.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @param expected Any expected value.
  # @param options Dict options value.
  # @return Any the resulting value.
  verify: algorithm, key = nil, message = nil, expected = nil, options = {} ->
    if self.algorithm != nil
      if message != nil
        options = message
      else
        options = expected
      expected = key
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    if options == nil
      options = {}
    actual = Hmac(algorithm, key).digest(message)
    wanted = self.expected_bytes(algorithm, expected, options)
    self.constant_time_equal(actual, wanted)

  # Hmac.xor applies a byte-wise xor mask.
  # @param data Array data value.
  # @param byte Int byte value.
  # @return Any the resulting value.
  xor: data, byte ->
    arr = bytes_array(data)
    out = []
    i = 0
    while i < arr.len()
      out.push(arr[i] ^ byte)
      i = i + 1
    bytes(out)

Instance Variables

algorithm

Hmac.algorithm

lib/hmac.tya:9

Hmac.algorithm stores instance state.

Source
  # Hmac.algorithm stores instance state.
  # @type Nil
  algorithm: nil

key

Hmac.key

lib/hmac.tya:13

Hmac.key stores instance state.

Source
  # Hmac.key stores instance state.
  # @type Nil
  key: nil

Methods

base64digest

Hmac.base64digest(algorithm, key = nil, message = nil)

lib/hmac.tya:28

Hmac.base64digest returns the HMAC digest as Base64.

Source
  # Hmac.base64digest returns the HMAC digest as Base64.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @return Any the resulting value.
  base64digest: algorithm, key = nil, message = nil ->
    if self.algorithm != nil
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    base64.Base64.encode(Hmac(algorithm, key).digest(message))

block_size

Hmac.block_size(algorithm)

lib/hmac.tya:38

Hmac.block_size returns the digest block size for algorithm.

Source
  # Hmac.block_size returns the digest block size for algorithm.
  # @param algorithm Any algorithm value.
  # @return Any the resulting value.
  block_size: algorithm ->
    if algorithm == "sha256"
      return 64
    if algorithm == "sha384" or algorithm == "sha512"
      return 128
    raise error("hmac.algorithm: unsupported algorithm", { kind: "crypto", code: "unsupported_algorithm" })

constant_time_equal

Hmac.constant_time_equal(left, right)

lib/hmac.tya:49

Hmac.constant_time_equal compares equal-length byte sequences.

Source
  # Hmac.constant_time_equal compares equal-length byte sequences.
  # @param left Any left value.
  # @param right Any right value.
  # @return Any the resulting value.
  constant_time_equal: left, right ->
    a = bytes_array(left)
    b = bytes_array(right)
    if a.len() != b.len()
      return false
    diff = 0
    i = 0
    while i < a.len()
      diff = diff | a[i] ^ b[i]
      i = i + 1
    diff == 0

digest

Hmac.digest(algorithm, key = nil, message = nil)

lib/hmac.tya:66

Hmac.digest returns the raw HMAC digest bytes.

Source
  # Hmac.digest returns the raw HMAC digest bytes.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @return Any the resulting value.
  digest: algorithm, key = nil, message = nil ->
    if self.algorithm != nil
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    hex.Hex(nil).decode(Hmac(algorithm, key).hexdigest(message))

digest_size

Hmac.digest_size(algorithm)

lib/hmac.tya:76

Hmac.digest_size returns the digest byte size for algorithm.

Source
  # Hmac.digest_size returns the digest byte size for algorithm.
  # @param algorithm Any algorithm value.
  # @return Any the resulting value.
  digest_size: algorithm ->
    if algorithm == "sha256"
      return 32
    if algorithm == "sha384"
      return 48
    if algorithm == "sha512"
      return 64
    raise error("hmac.algorithm: unsupported algorithm", { kind: "crypto", code: "unsupported_algorithm" })

expected_bytes

Hmac.expected_bytes(algorithm, expected, options)

lib/hmac.tya:90

Hmac.expected_bytes decodes a verification digest.

Source
  # Hmac.expected_bytes decodes a verification digest.
  # @param algorithm Any algorithm value.
  # @param expected Any expected value.
  # @param options Dict options value.
  # @return Any the resulting value.
  expected_bytes: algorithm, expected, options ->
    algorithm
    encoding = nil
    if options != nil
      if options.class != Dict
        raise error("hmac.verify: options must be a dictionary", { kind: "crypto", code: "invalid_options" })
      for entry in options
        if entry["key"] != "encoding"
          raise error("hmac.verify: unknown option " + entry["key"], { kind: "crypto", code: "unknown_option" })
      encoding = options["encoding"]
    if expected.class == String
      if encoding == nil
        encoding = "hex"
      if encoding == "hex"
        return hex.Hex(expected).decode()
      if encoding == "base64"
        return base64.Base64.decode(expected)
      raise error("hmac.verify: invalid encoding", { kind: "crypto", code: "invalid_encoding" })
    if encoding == nil or encoding == "raw"
      return expected
    raise error("hmac.verify: bytes expected value requires raw encoding", { kind: "crypto", code: "invalid_encoding" })

hash_hex

Hmac.hash_hex(algorithm, value)

lib/hmac.tya:116

Hmac.hash_hex hashes value with algorithm and returns lowercase hex.

Source
  # Hmac.hash_hex hashes value with algorithm and returns lowercase hex.
  # @param algorithm Any algorithm value.
  # @param value String value value.
  # @return Any the resulting value.
  hash_hex: algorithm, value ->
    if algorithm == "sha256"
      return digest.Digest.sha256(value)
    if algorithm == "sha384"
      return digest.Digest.sha384(value)
    if algorithm == "sha512"
      return digest.Digest.sha512(value)
    raise error("hmac.algorithm: unsupported algorithm", { kind: "crypto", code: "unsupported_algorithm" })

hexdigest

Hmac.hexdigest(algorithm, key = nil, message = nil)

lib/hmac.tya:130

Hmac.hexdigest returns the HMAC digest as lowercase hex.

Source
  # Hmac.hexdigest returns the HMAC digest as lowercase hex.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @return Any the resulting value.
  hexdigest: algorithm, key = nil, message = nil ->
    if self.algorithm != nil
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    size = self.block_size(algorithm)
    key_bytes = self.input_bytes("key", key)
    if bytes_array(key_bytes).len() > size
      key_bytes = hex.Hex(nil).decode(self.hash_hex(algorithm, key_bytes))
    key_block = self.pad(key_bytes, size)
    inner = self.xor(key_block, 54)
    outer = self.xor(key_block, 92)
    msg = self.input_bytes("message", message)
    inner_hash = hex.Hex(nil).decode(
      self.hash_hex(algorithm, bytes_concat(inner, msg))
    )
    self.hash_hex(algorithm, bytes_concat(outer, inner_hash))

initialize

Hmac.initialize(algorithm, key)

lib/hmac.tya:19

Hmac.initialize stores the algorithm and key.

Source
  # Hmac.initialize stores the algorithm and key.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @return Self the initialized object.
  initialize: algorithm, key ->
    self.algorithm = algorithm
    self.key = key

input_bytes

Hmac.input_bytes(name, value)

lib/hmac.tya:152

Hmac.input_bytes normalizes string or bytes input.

Source
  # Hmac.input_bytes normalizes string or bytes input.
  # @param name String name value.
  # @param value String value value.
  # @return Any the resulting value.
  input_bytes: name, value ->
    if value.class == String
      return bytes_of(value)
    if value.class.name == "Bytes"
      return value
    raise error("hmac." + name + ": value must be a string or bytes", { kind: "crypto", code: "invalid_input" })

pad

Hmac.pad(data, size)

lib/hmac.tya:163

Hmac.pad right-pads data with zeros to size.

Source
  # Hmac.pad right-pads data with zeros to size.
  # @param data Array data value.
  # @param size Int size value.
  # @return Any the resulting value.
  pad: data, size ->
    arr = bytes_array(data)
    while arr.len() < size
      arr.push(0)
    bytes(arr)

verify

Hmac.verify(algorithm, key = nil, message = nil, expected = nil, options = {})

lib/hmac.tya:176

Hmac.verify checks an expected digest.

Source
  # Hmac.verify checks an expected digest.
  # @param algorithm Any algorithm value.
  # @param key String key value.
  # @param message String message value.
  # @param expected Any expected value.
  # @param options Dict options value.
  # @return Any the resulting value.
  verify: algorithm, key = nil, message = nil, expected = nil, options = {} ->
    if self.algorithm != nil
      if message != nil
        options = message
      else
        options = expected
      expected = key
      message = algorithm
      algorithm = self.algorithm
      key = self.key
    if options == nil
      options = {}
    actual = Hmac(algorithm, key).digest(message)
    wanted = self.expected_bytes(algorithm, expected, options)
    self.constant_time_equal(actual, wanted)

xor

Hmac.xor(data, byte)

lib/hmac.tya:196

Hmac.xor applies a byte-wise xor mask.

Source
  # Hmac.xor applies a byte-wise xor mask.
  # @param data Array data value.
  # @param byte Int byte value.
  # @return Any the resulting value.
  xor: data, byte ->
    arr = bytes_array(data)
    out = []
    i = 0
    while i < arr.len()
      out.push(arr[i] ^ byte)
      i = i + 1
    bytes(out)