MultiMAC

February 28, 2022 · View on GitHub

License: MIT

MultiMAC

Authenticate multiple inputs easily using keyed BLAKE2b in libsodium.

Justification

The standard way of safely computing a MAC for multiple inputs requires worrying about concatenating arrays and converting the length of each array to a fixed number of bytes, such as 4 bytes to represent an integer, consistently in either big- or little-endian, regardless of the endianness of the machine. This is annoying to implement and possibly less efficient than the following approach discussed by Neil Madden, author of API Security in Action.

Installation

NuGet

You can find the NuGet package here.

The easiest way to install this is via the NuGet Package Manager in Visual Studio, as explained here. JetBrains Rider also has a package manager, and instructions can be found here.

Manual

  1. Install the Sodium.Core NuGet package in Visual Studio.
  2. Download the latest release.
  3. Move the downloaded .dll file into your Visual Studio project folder.
  4. Click on the Project tab and Add Project Reference... in Visual Studio.
  5. Go to Browse, click the Browse button, and select the downloaded .dll file.
  6. Add using MultiMAC; to the top of each code file that will use the library.

Requirements

Note that the libsodium library requires the Visual C++ Redistributable for Visual Studio 2015-2019 to work on Windows. If you want your program to be portable, then you must keep the relevant (x86 or x64) vcruntime140.dll file in the same folder as your executable on Windows.

Usage

⚠️WARNING: Never use the same key for key1 and key2.

const TagLength tagLength = TagLength.BLAKE2b256;

// Both keys should be derived using a KDF in practice (e.g. Argon2, HKDF, etc)
byte[] key1 = SodiumCore.GetRandomBytes((int)tagLength);

// The keys must be the same size as the tag length
byte[] key2 = SodiumCore.GetRandomBytes((int)tagLength);

// Gather up the byte arrays to authenticate
byte[] input1 = Encoding.UTF8.GetBytes("Po");

byte[] input2 = Encoding.UTF8.GetBytes("ta");

byte[] input3 = Encoding.UTF8.GetBytes("toes");

byte[] input4 = Encoding.UTF8.GetBytes("Boil 'em, mash 'em, stick 'em in a stew");

// Compute a 256-bit tag
byte[] tag = MultiMac.Compute(key1, key2, tagLength, input1, input2, input3, input4);

// Verify a tag
bool validTag = MultiMac.Verify(tag, key1, key2, input1, input2, input3, input4);

Benchmarks

The following benchmarks were done using BenchmarkDotNet in a .NET 6 console application. A plaintext file and 16 random bytes were used as inputs, and the key size was equal to the tag length.

  • BLAKE2b refers to concatenating multiple inputs via array copying (BLAKE2b(message: additionalData || fileBytes || additionalDataLength || fileBytesLength, key)).
  • MultiMAC-BLAKE2b refers to using this library, which employs a cascade construction (BLAKE2b(message: BLAKE2b(message: fileBytes, key: BLAKE2b(message: additionalData, key1)), key2)).

In sum, the MultiMAC cascade approach is noticeably faster for large inputs, slightly faster for medium inputs, and slower for very small inputs. It is also significantly easier and safer to use, which suggests it should be offered in cryptographic libraries.

512-byte file

MethodMeanErrorStdDev
BLAKE2b256646.3 ns0.77 ns0.68 ns
BLAKE2b384649.3 ns2.77 ns2.32 ns
BLAKE2b512660.8 ns0.67 ns0.53 ns
MultiMAC-BLAKE2b2561.030 us0.0041 us0.0032 us
MultiMAC-BLAKE2b3841.024 us0.0036 us0.0033 us
MultiMAC-BLAKE2b5121.031 us0.0062 us0.0058 us

16 KiB file

MethodMeanErrorStdDev
BLAKE2b25612.18 us0.055 us0.046 us
BLAKE2b38412.36 us0.242 us0.226 us
BLAKE2b51212.10 us0.155 us0.137 us
MultiMAC-BLAKE2b25611.42 us0.031 us0.029 us
MultiMAC-BLAKE2b38411.43 us0.018 us0.015 us
MultiMAC-BLAKE2b51211.71 us0.043 us0.033 us

32 KiB file

MethodMeanErrorStdDev
BLAKE2b25623.70 us0.120 us0.107 us
BLAKE2b38423.82 us0.205 us0.191 us
BLAKE2b51224.16 us0.197 us0.185 us
MultiMAC-BLAKE2b25622.30 us0.101 us0.095 us
MultiMAC-BLAKE2b38422.26 us0.116 us0.103 us
MultiMAC-BLAKE2b51222.26 us0.087 us0.073 us

64 KiB file

MethodMeanErrorStdDev
BLAKE2b25647.03 us0.056 us0.044 us
BLAKE2b38447.87 us0.207 us0.193 us
BLAKE2b51247.08 us0.112 us0.099 us
MultiMAC-BLAKE2b25643.73 us0.029 us0.025 us
MultiMAC-BLAKE2b38443.91 us0.174 us0.163 us
MultiMAC-BLAKE2b51243.92 us0.193 us0.181 us

128 KiB file

MethodMeanErrorStdDev
BLAKE2b256144.7 us1.20 us1.12 us
BLAKE2b384144.8 us1.48 us1.39 us
BLAKE2b512145.2 us1.09 us1.02 us
MultiMAC-BLAKE2b25687.41 us0.069 us0.061 us
MultiMAC-BLAKE2b38486.78 us0.101 us0.094 us
MultiMAC-BLAKE2b51286.90 us0.228 us0.202 us

32 MiB file

MethodMeanErrorStdDev
BLAKE2b25634.50 ms0.561 ms0.524 ms
BLAKE2b38435.56 ms0.264 ms0.247 ms
BLAKE2b51235.52 ms0.233 ms0.194 ms
MultiMAC-BLAKE2b25622.77 ms0.066 ms0.061 ms
MultiMAC-BLAKE2b38422.58 ms0.023 ms0.020 ms
MultiMAC-BLAKE2b51222.62 ms0.058 ms0.055 ms