I want to protect data from tampering

Stay organized with collections Save and categorize content based on your preferences.

If you want to ensure that nobody can tamper with your data, we recommend the Message Authentication Code (MAC) primitive. It uses a single key to generate message authentication codes and verify them. MAC does not encrypt data. In most cases, protecting data with AEAD, which includes encryption and MAC, is preferable to MAC alone.

We recommend the HMAC_SHA256 key type for most use cases. For all supported key types, see Supported Key Types.

The following examples get you started using the MAC primitive.

Python

python/examples/mac/mac.py
"""A command-line utility for checking file integrity with a Message Authentication Code (MAC).

It loads cleartext keys from disk - this is not recommended!
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import binascii

# Special imports
from absl import app
from absl import flags
from absl import logging
import tink
from tink import cleartext_keyset_handle
from tink import mac

FLAGS = flags.FLAGS

flags.DEFINE_enum('mode', None, ['compute', 'verify'],
                  'The operation to perform.')
flags.DEFINE_string('keyset_path', None,
                    'Path to the keyset used for the MAC operation.')
flags.DEFINE_string('data_path', None,
                    'Path to the file with the input data to be checked.')
flags.DEFINE_string('mac_path', None,
                    'Path to the file containing a hexadecimal MAC of the'
                    ' data.')


def main(argv):
  del argv  # Unused.

  # Initialise Tink.
  try:
    mac.register()
  except tink.TinkError as e:
    logging.error('Error initialising Tink: %s', e)
    return 1

  # Read the keyset into a keyset_handle.
  with open(FLAGS.keyset_path, 'rt') as keyset_file:
    try:
      text = keyset_file.read()
      keyset_handle = cleartext_keyset_handle.read(tink.JsonKeysetReader(text))
    except tink.TinkError as e:
      logging.error('Error reading key: %s', e)
      return 1

  # Get the primitive.
  try:
    cipher = keyset_handle.primitive(mac.Mac)
  except tink.TinkError as e:
    logging.error('Error creating primitive: %s', e)
    return 1

  with open(FLAGS.data_path, 'rb') as data_file:
    data = data_file.read()

  if FLAGS.mode == 'compute':
    # Compute the MAC.
    code = cipher.compute_mac(data)
    with open(FLAGS.mac_path, 'wb') as mac_file:
      mac_file.write(binascii.hexlify(code))
    return 0

  with open(FLAGS.mac_path, 'rb') as mac_file:
    try:
      expected_mac = binascii.unhexlify(mac_file.read().strip())
    except binascii.Error as e:
      logging.exception('Error reading expected code: %s', e)
      return 1

  try:
    cipher.verify_mac(expected_mac, data)
    logging.info('MAC verification succeeded.')
    return 0
  except tink.TinkError as e:
    logging.info('MAC verification failed.')
    return 1


if __name__ == '__main__':
  flags.mark_flags_as_required([
      'mode', 'keyset_path', 'data_path', 'mac_path'])
  app.run(main)

Java

java_src/examples/mac/MacExample.java
package mac;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.crypto.tink.CleartextKeysetHandle;
import com.google.crypto.tink.JsonKeysetReader;
import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.Mac;
import com.google.crypto.tink.mac.MacConfig;
import com.google.crypto.tink.subtle.Hex;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.List;

/**
 * A command-line utility for checking file integrity with a Message Authentication Code (MAC).
 *
 * <p>It loads cleartext keys from disk - this is not recommended!
 *
 * <p>It requires the following arguments:
 *
 * <ul>
 *   <li>mode: either 'compute' or 'verify'.
 *   <li>key-file: Read the key material from this file.
 *   <li>input-file: Read the input from this file.
 *   <li>mac-file: name of the file containing a hexadecimal MAC of the input data.
 */
public final class MacExample {
  public static void main(String[] args) throws Exception {
    if (args.length != 4) {
      System.err.printf("Expected 4 parameters, got %d\n", args.length);
      System.err.println("Usage: java MacExample compute/verify key-file input-file mac-file");
      System.exit(1);
    }
    String mode = args[0];
    if (!mode.equals("compute") && !mode.equals("verify")) {
      System.err.println("Incorrect mode. Please select compute or verify.");
      System.exit(1);
    }
    File keyFile = new File(args[1]);
    byte[] msg = Files.readAllBytes(Paths.get(args[2]));
    File macFile = new File(args[3]);

    // Register all MAC key types with the Tink runtime.
    MacConfig.register();

    // Read the keyset into a KeysetHandle.
    KeysetHandle handle = null;
    try {
      handle = CleartextKeysetHandle.read(JsonKeysetReader.withFile(keyFile));
    } catch (GeneralSecurityException | IOException ex) {
      System.err.println("Cannot read keyset, got error: " + ex);
      System.exit(1);
    }

    // Get the primitive.
    Mac macPrimitive = null;
    try {
      macPrimitive = handle.getPrimitive(Mac.class);
    } catch (GeneralSecurityException ex) {
      System.err.println("Cannot create primitive, got error: " + ex);
      System.exit(1);
    }

    if (mode.equals("compute")) {
      byte[] mac = macPrimitive.computeMac(msg);
      try (FileOutputStream stream = new FileOutputStream(macFile)) {
        stream.write(Hex.encode(mac).getBytes(UTF_8));
      }
      System.exit(0);
    }

    List<String> lines = Files.readAllLines(macFile.toPath());
    if (lines.size() != 1) {
      System.err.printf("The MAC file should contain only one line, got %d", lines.size());
      System.exit(1);
    }

    byte[] mac = Hex.decode(lines.get(0).trim());
    try {
      macPrimitive.verifyMac(mac, msg);
    } catch (GeneralSecurityException ex) {
      System.err.println("MAC verification failed.");
      System.exit(1);
    }

    System.exit(0);
  }

  private MacExample() {}
}

C++

cc/examples/mac/mac_cli.cc
// A command-line utility for testing Tink MAC.

#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <utility>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "tink/cleartext_keyset_handle.h"
#include "tink/json_keyset_reader.h"
#include "tink/keyset_handle.h"
#include "tink/keyset_reader.h"
#include "tink/mac.h"
#include "tink/mac/mac_config.h"
#include "tink/util/status.h"

ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format");
ABSL_FLAG(std::string, mode, "", "Mode of operation {compute|verify}");
ABSL_FLAG(std::string, data_filename, "", "Data file name");
ABSL_FLAG(std::string, tag_filename, "", "Authentication tag file name");

namespace {

using ::crypto::tink::CleartextKeysetHandle;
using ::crypto::tink::JsonKeysetReader;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::KeysetReader;
using ::crypto::tink::Mac;
using ::crypto::tink::MacConfig;
using ::crypto::tink::util::Status;
using ::crypto::tink::util::StatusOr;

constexpr absl::string_view kCompute = "compute";
constexpr absl::string_view kVerify = "verify";

// Creates a KeysetReader that reads a JSON-formatted keyset
// from the given file.
StatusOr<std::unique_ptr<KeysetReader>> GetJsonKeysetReader(
    const std::string& filename) {
  std::clog << "Creating a JsonKeysetReader...\n";
  auto key_input_stream = absl::make_unique<std::ifstream>();
  key_input_stream->open(filename, std::ifstream::in);
  return JsonKeysetReader::New(std::move(key_input_stream));
}

// Creates a KeysetHandle that for a keyset read from the given file,
// which is expected to contain a JSON-formatted keyset.
StatusOr<std::unique_ptr<KeysetHandle>> ReadKeyset(
    const std::string& filename) {
  StatusOr<std::unique_ptr<KeysetReader>> keyset_reader =
      GetJsonKeysetReader(filename);
  if (!keyset_reader.ok()) {
    return keyset_reader.status();
  }
  return CleartextKeysetHandle::Read(*std::move(keyset_reader));
}

// Reads `filename` and returns the read content as a string, or an error status
// if the file does not exist.
StatusOr<std::string> Read(const std::string& filename) {
  std::clog << "Reading the input...\n";
  std::ifstream input_stream;
  input_stream.open(filename, std::ifstream::in);
  if (!input_stream.is_open()) {
    return Status(absl::StatusCode::kInternal,
                  absl::StrCat("Error opening input file ", filename));
  }
  std::stringstream input;
  input << input_stream.rdbuf();
  return input.str();
}

// Writes the given `data_to_write` to the specified file `filename`.
Status Write(const std::string& data_to_write, const std::string& filename) {
  std::clog << "Writing the output...\n";
  std::ofstream output_stream(filename,
                              std::ofstream::out | std::ofstream::binary);
  if (!output_stream.is_open()) {
    return Status(absl::StatusCode::kInternal,
                  absl::StrCat("Error opening output file ", filename));
  }
  output_stream << data_to_write;
  return crypto::tink::util::OkStatus();
}

}  // namespace

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);

  std::string mode = absl::GetFlag(FLAGS_mode);
  std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename);
  std::string data_filename = absl::GetFlag(FLAGS_data_filename);
  std::string tag_filename = absl::GetFlag(FLAGS_tag_filename);

  if (mode.empty()) {
    std::cerr << "Mode must be specified with --mode=<" << kCompute << "|"
              << kVerify << ">." << std::endl;
    exit(1);
  }

  if (mode != kCompute && mode != kVerify) {
    std::cerr << "Unknown mode '" << mode << "'; "
              << "Expected either " << kCompute << " or " << kVerify << "."
              << std::endl;
    exit(1);
  }

  const std::string tag_file_action =
      (mode == kCompute) ? "written to" : "read from";
  std::clog << "Using keyset from file '" << keyset_filename << "' to " << mode
            << " authentication tag from file '" << tag_filename
            << "' for data file '" << data_filename << "'." << std::endl;
  std::clog << "The tag will be " << tag_file_action << " file '"
            << tag_filename << "'." << std::endl;

  Status result = MacConfig::Register();
  if (!result.ok()) {
    std::cerr << result.message() << std::endl;
    exit(1);
  }

  // Read the keyset from file.
  StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
      ReadKeyset(keyset_filename);
  if (!keyset_handle.ok()) {
    std::cerr << keyset_handle.status().message() << std::endl;
    exit(1);
  }

  // Get the primitive.
  StatusOr<std::unique_ptr<Mac>> mac_primitive =
      (*keyset_handle)->GetPrimitive<Mac>();
  if (!mac_primitive.ok()) {
    std::cerr << mac_primitive.status().message() << std::endl;
    exit(1);
  }

  // Read the input.
  StatusOr<std::string> data_file_content = Read(data_filename);
  if (!data_file_content.ok()) {
    std::cerr << data_file_content.status().message() << std::endl;
    exit(1);
  }

  std::string output;
  if (mode == kCompute) {
    // Compute authentication tag.
    std::clog << "Computing tag...\n";
    StatusOr<std::string> compute_result =
        (*mac_primitive)->ComputeMac(*data_file_content);
    if (!compute_result.ok()) {
      std::cerr << compute_result.status().message() << std::endl;
      exit(1);
    }
    // Write out the authentication tag to tag file.
    Status write_result = Write(*compute_result, tag_filename);
    if (!write_result.ok()) {
      std::cerr << write_result.message() << std::endl;
      exit(1);
    }
  } else {  // operation == kVerify.
    // Read the authentication tag from tag file.
    StatusOr<std::string> tag_result = Read(tag_filename);
    if (!tag_result.ok()) {
      std::cerr << tag_result.status().message() << std::endl;
      exit(1);
    }
    // Verify authentication tag.
    std::clog << "Verifying tag...\n";
    Status verify_result =
        (*mac_primitive)->VerifyMac(*tag_result, *data_file_content);
    if (!verify_result.ok()) {
      std::cerr << verify_result.message() << std::endl;
      exit(1);
    }
    std::clog << "verification succeeded" << std::endl;
  }

  std::clog << "All done." << std::endl;
  return 0;
}