Device interface library for multiple platforms including Mbed.

Dependents:   DeepCover Embedded Security in IoT MaximInterface MAXREFDES155#

Maxim Interface is a library framework focused on providing flexible and expressive hardware interfaces. Both communication interfaces such as I2C and 1-Wire and device interfaces such as DS18B20 are supported. Modern C++ concepts are used extensively while keeping compatibility with C++98/C++03 and requiring no external dependencies. The embedded-friendly design does not depend on exceptions or RTTI.

The full version of the project is hosted on GitLab: https://gitlab.com/iabenz/MaximInterface

Revision:
7:9cd16581b578
Child:
8:5ea891c7d1a1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MaximInterfaceDevices/DS28C40.cpp	Mon Jul 22 11:44:07 2019 -0500
@@ -0,0 +1,655 @@
+/*******************************************************************************
+* Copyright (C) 2019 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* Permission is hereby granted, free of charge, to any person obtaining a
+* copy of this software and associated documentation files (the "Software"),
+* to deal in the Software without restriction, including without limitation
+* the rights to use, copy, modify, merge, publish, distribute, sublicense,
+* and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
+* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************/
+
+#include <stddef.h>
+#include <algorithm>
+#include <MaximInterfaceCore/Error.hpp>
+#include "DS28C40.hpp"
+
+namespace MaximInterfaceDevices {
+
+using namespace Core;
+using std::copy;
+
+static const int readMemoryTimeMs = 2;
+static const int writeMemoryTimeMs = 150;
+static const int writeStateTimeMs = 15;
+static const int generateEccKeyPairTimeMs = 500;
+static const int generateEcdsaSignatureTimeMs = 50;
+static const int computeTimeMs = 4;
+static const int verifyEcdsaTimeMs = 160;
+static const int trngGenerationTimeMs = 50;
+static const int trngOnDemandCheckTimeMs = 50;
+
+static const uint_least8_t computeAndReadPageAuthenticationCmd = 0xA5;
+static const uint_least8_t readRngCmd = 0xD2;
+
+const int DS28C40::publicKeyAxPage;
+const int DS28C40::publicKeyAyPage;
+const int DS28C40::publicKeyBxPage;
+const int DS28C40::publicKeyByPage;
+const int DS28C40::authorityPublicKeyAxPage;
+const int DS28C40::authorityPublicKeyAyPage;
+const int DS28C40::authorityPublicKeyBxPage;
+const int DS28C40::authorityPublicKeyByPage;
+const int DS28C40::privateKeyAPage;
+const int DS28C40::privateKeyBPage;
+const int DS28C40::secretAPage;
+const int DS28C40::secretBPage;
+const int DS28C40::romOptionsPage;
+const int DS28C40::gpioControlPage;
+const int DS28C40::publicKeySxPage;
+const int DS28C40::publicKeySyPage;
+
+const int DS28C40::memoryPages;
+const int DS28C40::protectionBlocks;
+
+error_code DS28C40::writeMemory(int pageNum, Page::const_span page) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t request[2 + Page::size];
+  request[0] = 0x96;
+  request[1] = pageNum;
+  copy(page.begin(), page.end(), request + 2);
+  return runCommand(request, writeMemoryTimeMs);
+}
+
+error_code DS28C40::readMemory(int pageNum, Page::span page) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t buffer[1 + Page::size];
+  buffer[0] = 0x44;
+  buffer[1] = pageNum;
+  span<uint_least8_t> response(buffer);
+  const error_code result =
+      runCommand(make_span(buffer, 2), readMemoryTimeMs, response);
+  if (!result) {
+    copy(response.begin(), response.end(), page.begin());
+  }
+  return result;
+}
+
+error_code
+DS28C40::encryptedReadMemory(int pageNum, KeySecret secret,
+                             EncryptionChallenge::span encryptionChallenge,
+                             Page::span encryptedPage) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  const size_t requestSize = 3;
+  const size_t responseSize = 1 + EncryptionChallenge::size + Page::size;
+  uint_least8_t buffer[MaximInterfaceCore_MAX(requestSize, responseSize)];
+  buffer[0] = 0x4B;
+  buffer[1] = pageNum;
+  buffer[2] = secret;
+  span<uint_least8_t> response(buffer, responseSize);
+  const error_code result =
+      runCommand(make_span(buffer, requestSize),
+                 readMemoryTimeMs + computeTimeMs, response);
+  if (!result) {
+    span<uint_least8_t>::const_iterator begin = response.begin();
+    span<uint_least8_t>::const_iterator end =
+        begin + encryptionChallenge.size();
+    copy(begin, end, encryptionChallenge.begin());
+    begin = end;
+    end = begin + encryptedPage.size();
+    copy(begin, end, encryptedPage.begin());
+  }
+  return result;
+}
+
+error_code DS28C40::readBlockProtection(int blockNumber,
+                                        Optional<KeySecret> & keySecret,
+                                        BlockProtection & protection) {
+  if (blockNumber < 0 || blockNumber >= protectionBlocks) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  const size_t requestSize = 2;
+  const size_t responseSize = 3;
+  uint_least8_t buffer[MaximInterfaceCore_MAX(requestSize, responseSize)];
+  buffer[0] = 0xAA;
+  buffer[1] = blockNumber;
+  span<uint_least8_t> response(buffer, responseSize);
+  const error_code result =
+      runCommand(make_span(buffer, requestSize), readMemoryTimeMs, response);
+  if (result) {
+    return result;
+  }
+  if ((response[0] & 0x3F) != blockNumber) {
+    return make_error_code(InvalidResponseError);
+  }
+  switch (response[0] >> 6) {
+  case 0:
+    keySecret = none;
+    break;
+  case 1:
+    keySecret = KeySecretA;
+    break;
+  case 2:
+    keySecret = KeySecretB;
+    break;
+  default:
+    return make_error_code(InvalidResponseError);
+  }
+  if ((response[1] & 0x20) != 0) {
+    return make_error_code(InvalidResponseError);
+  }
+  protection = response[1];
+  return error_code();
+}
+
+error_code DS28C40::setBlockProtection(int blockNum, KeySecret keySecret,
+                                       const BlockProtection & protection) {
+  if (blockNum < 0 || blockNum >= protectionBlocks || keySecret == KeySecretS) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  const uint_least8_t request[] = {
+      0xC3,
+      static_cast<uint_least8_t>((keySecret == KeySecretB ? 0x80 : 0x40) |
+                                 blockNum),
+      static_cast<uint_least8_t>(protection.to_ulong())};
+  return runCommand(request, writeStateTimeMs);
+}
+
+error_code
+DS28C40::computeAndReadPageAuthentication(int pageNum, KeySecret key,
+                                          Page::const_span challenge,
+                                          Ecc256::Signature::span signature) {
+  if (pageNum < 0 || pageNum >= memoryPages || key == KeySecretS) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  const size_t requestSize = 3 + Page::size;
+  const size_t responseSize = 1 + 2 * Ecc256::Scalar::size;
+  uint_least8_t buffer[MaximInterfaceCore_MAX(requestSize, responseSize)];
+  buffer[0] = computeAndReadPageAuthenticationCmd;
+  buffer[1] = pageNum;
+  buffer[2] = key + 3;
+  copy(challenge.begin(), challenge.end(), buffer + 3);
+  span<uint_least8_t> response(buffer, responseSize);
+  const error_code result = runCommand(make_span(buffer, requestSize),
+                                       generateEcdsaSignatureTimeMs, response);
+  if (!result) {
+    span<uint_least8_t>::const_iterator begin = response.begin();
+    span<uint_least8_t>::const_iterator end = begin + signature.s.size();
+    copy(begin, end, signature.s.begin());
+    begin = end;
+    end = begin + signature.r.size();
+    copy(begin, end, signature.r.begin());
+  }
+  return result;
+}
+
+error_code DS28C40::computeAndReadPageAuthentication(int pageNum,
+                                                     KeySecret secret,
+                                                     Page::const_span challenge,
+                                                     Page::span hmac) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  const size_t requestSize = 3 + Page::size;
+  const size_t responseSize = 1 + Page::size;
+  uint_least8_t buffer[MaximInterfaceCore_MAX(requestSize, responseSize)];
+  buffer[0] = computeAndReadPageAuthenticationCmd;
+  buffer[1] = pageNum;
+  buffer[2] = secret;
+  copy(challenge.begin(), challenge.end(), buffer + 3);
+  span<uint_least8_t> response(buffer, responseSize);
+  const error_code result =
+      runCommand(make_span(buffer, requestSize), computeTimeMs, response);
+  if (!result) {
+    copy(response.begin(), response.end(), hmac.begin());
+  }
+  return result;
+}
+
+error_code DS28C40::computeMultiblockHash(bool firstBlock, bool lastBlock,
+                                          span<const uint_least8_t> data) {
+  const span<const uint_least8_t>::index_type maxDataSize = 64;
+
+  if (data.size() < 1 || data.size() > maxDataSize) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t buffer[2 + maxDataSize];
+  buffer[0] = 0x33;
+  buffer[1] = 0;
+  if (firstBlock) {
+    buffer[1] |= 0x40;
+  }
+  if (lastBlock) {
+    buffer[1] |= 0x80;
+  }
+  copy(data.begin(), data.end(), buffer + 2);
+  return runCommand(make_span(buffer, 2 + data.size()), computeTimeMs);
+}
+
+error_code DS28C40::verifyEcdsaSignature(
+    KeySecret key, bool authorityKey, GpioState gpioState,
+    Ecc256::Signature::const_span signature, span<const uint_least8_t> data) {
+  return verifyEcdsaSignature(key, authorityKey, DataInput, gpioState,
+                              signature, data);
+}
+
+error_code DS28C40::verifyEcdsaSignature(
+    KeySecret key, bool authorityKey, GpioState gpioState,
+    Ecc256::Signature::const_span signature, Page::const_span hash) {
+  return verifyEcdsaSignature(key, authorityKey, HashInput, gpioState,
+                              signature, hash);
+}
+
+error_code
+DS28C40::verifyEcdsaSignature(KeySecret key, bool authorityKey,
+                              GpioState gpioState,
+                              Ecc256::Signature::const_span signature) {
+  return verifyEcdsaSignature(key, authorityKey, THASH, gpioState, signature,
+                              span<const uint_least8_t>());
+}
+
+error_code DS28C40::verifyEcdsaSignature(
+    KeySecret key, bool authorityKey, HashType hashType, GpioState gpioState,
+    Ecc256::Signature::const_span signature, span<const uint_least8_t> buffer) {
+  const span<const uint_least8_t>::index_type maxBufferSize = 61;
+
+  if (buffer.size() > maxBufferSize) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t request[2 + 2 * Ecc256::Scalar::size + maxBufferSize];
+  uint_least8_t * requestIt = request;
+  *requestIt++ = 0x59;
+  switch (key) {
+  case KeySecretA:
+    if (authorityKey) {
+      *requestIt = 2;
+    } else {
+      *requestIt = 0;
+    }
+    break;
+  case KeySecretB:
+    if (authorityKey) {
+      *requestIt = 3;
+    } else {
+      *requestIt = 1;
+    }
+    break;
+  case KeySecretS:
+    if (!authorityKey) {
+      *requestIt = 4;
+      break;
+    }
+    // else: Go to default case.
+  default:
+    return make_error_code(InvalidParameterError);
+  }
+  *requestIt |= hashType << 3;
+  if (gpioState != Unchanged) {
+    *requestIt |= 0x40;
+  }
+  if (gpioState == Conducting) {
+    *requestIt |= 0x20;
+  }
+  requestIt = copy(signature.r.begin(), signature.r.end(), ++requestIt);
+  requestIt = copy(signature.s.begin(), signature.s.end(), requestIt);
+  requestIt = copy(buffer.begin(), buffer.end(), requestIt);
+  return runCommand(make_span(request, requestIt),
+                    verifyEcdsaTimeMs +
+                        (hashType == DataInput ? computeTimeMs : 0));
+}
+
+error_code DS28C40::authenticateEcdsaPublicKey(
+    KeySecret key, Ecc256::Signature::const_span cert,
+    span<const uint_least8_t> certCustomization) {
+  return authenticateEcdsaPublicKey(key, true, cert, certCustomization, NULL);
+}
+
+error_code DS28C40::authenticateEcdsaPublicKey(
+    KeySecret key, bool authWrites, Ecc256::Signature::const_span cert,
+    span<const uint_least8_t> certCustomization,
+    span<const uint_least8_t> ecdhCustomization) {
+  return authenticateEcdsaPublicKey(key, authWrites, cert, certCustomization,
+                                    &ecdhCustomization);
+}
+
+error_code DS28C40::authenticateEcdsaPublicKey(
+    KeySecret key, bool authWrites, Ecc256::Signature::const_span cert,
+    span<const uint_least8_t> certCustomization,
+    const span<const uint_least8_t> * ecdhCustomization) {
+  const span<const uint_least8_t>::index_type minCustomizationSize = 1;
+  const span<const uint_least8_t>::index_type maxCertCustomizationSize = 32;
+  const span<const uint_least8_t>::index_type maxEcdhCustomizationSize = 48;
+  const span<const uint_least8_t>::index_type maxTotalCustomizationSize = 60;
+
+  if (!(certCustomization.size() >= minCustomizationSize &&
+        certCustomization.size() <= maxCertCustomizationSize &&
+        (!ecdhCustomization ||
+         (ecdhCustomization->size() >= minCustomizationSize &&
+          ecdhCustomization->size() <= maxEcdhCustomizationSize &&
+          certCustomization.size() + ecdhCustomization->size() <=
+              maxTotalCustomizationSize)))) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  if (key == KeySecretS) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t
+      request[2 + 2 * Ecc256::Scalar::size + maxTotalCustomizationSize];
+  uint_least8_t * requestIt = request;
+  *requestIt++ = 0xA8;
+  *requestIt++ = static_cast<uint_least8_t>(
+      ((certCustomization.size() - 1) << 3) | (key << 2) |
+      ((ecdhCustomization != NULL) << 1) | (authWrites << 0));
+  requestIt = copy(cert.r.begin(), cert.r.end(), requestIt);
+  requestIt = copy(cert.s.begin(), cert.s.end(), requestIt);
+  requestIt =
+      copy(certCustomization.begin(), certCustomization.end(), requestIt);
+  int delay = verifyEcdsaTimeMs;
+  if (ecdhCustomization) {
+    const span<const uint_least8_t>::index_type certCustomizationPaddingSize =
+        maxCertCustomizationSize - certCustomization.size();
+    std::fill_n(requestIt, certCustomizationPaddingSize, 0);
+    requestIt += certCustomizationPaddingSize;
+    requestIt =
+        copy(ecdhCustomization->begin(), ecdhCustomization->end(), requestIt);
+    delay += verifyEcdsaTimeMs;
+  }
+  return runCommand(make_span(request, requestIt), delay);
+}
+
+error_code DS28C40::authenticatedEcdsaWriteMemory(
+    int pageNum, bool useKeyS, Page::const_span newPageData,
+    Ecc256::Signature::const_span signature) {
+  return authenticatedEcdsaWriteMemory(pageNum, useKeyS, newPageData, signature,
+                                       NULL);
+}
+
+error_code DS28C40::authenticatedEcdsaWriteMemory(
+    int pageNum, bool useKeyS, Page::const_span newPageData,
+    Ecc256::Signature::const_span signature,
+    EncryptionChallenge::const_span challenge) {
+  return authenticatedEcdsaWriteMemory(pageNum, useKeyS, newPageData, signature,
+                                       &challenge);
+}
+
+error_code DS28C40::authenticatedEcdsaWriteMemory(
+    int pageNum, bool useKeyS, Page::const_span newPageData,
+    Ecc256::Signature::const_span signature,
+    const EncryptionChallenge::const_span * challenge) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t request[2 + Page::size + 2 * Ecc256::Scalar::size +
+                        EncryptionChallenge::size];
+  uint_least8_t * requestIt = request;
+  *requestIt++ = 0x89;
+  *requestIt = pageNum;
+  if (useKeyS) {
+    *requestIt |= 0x80;
+  }
+  requestIt = copy(newPageData.begin(), newPageData.end(), ++requestIt);
+  requestIt = copy(signature.r.begin(), signature.r.end(), requestIt);
+  requestIt = copy(signature.s.begin(), signature.s.end(), requestIt);
+  int delay = verifyEcdsaTimeMs + writeMemoryTimeMs;
+  if (challenge) {
+    requestIt = copy(challenge->begin(), challenge->end(), requestIt);
+    delay += computeTimeMs;
+  }
+  return runCommand(make_span(request, requestIt), delay);
+}
+
+error_code DS28C40::authenticatedSha256WriteMemory(int pageNum, bool useSecretS,
+                                                   Page::const_span newPageData,
+                                                   Page::const_span hmac) {
+  return authenticatedSha256WriteMemory(pageNum, useSecretS, newPageData, hmac,
+                                        NULL);
+}
+
+error_code DS28C40::authenticatedSha256WriteMemory(
+    int pageNum, bool useSecretS, Page::const_span newPageData,
+    Page::const_span hmac, EncryptionChallenge::const_span challenge) {
+  return authenticatedSha256WriteMemory(pageNum, useSecretS, newPageData, hmac,
+                                        &challenge);
+}
+
+error_code DS28C40::authenticatedSha256WriteMemory(
+    int pageNum, bool useSecretS, Page::const_span newPageData,
+    Page::const_span hmac, const EncryptionChallenge::const_span * challenge) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t request[3 + 2 * Page::size + EncryptionChallenge::size];
+  uint_least8_t * requestIt = request;
+  *requestIt++ = 0x99;
+  *requestIt++ = pageNum;
+  *requestIt++ = useSecretS ? 2 : 0;
+  requestIt = copy(newPageData.begin(), newPageData.end(), requestIt);
+  requestIt = copy(hmac.begin(), hmac.end(), requestIt);
+  int delay = writeMemoryTimeMs + computeTimeMs;
+  if (challenge) {
+    requestIt = copy(challenge->begin(), challenge->end(), requestIt);
+    delay += computeTimeMs;
+  }
+  return runCommand(make_span(request, requestIt), delay);
+}
+
+error_code
+DS28C40::computeAndWriteSha256Secret(int pageNum, KeySecret masterSecret,
+                                     KeySecret destinationSecret,
+                                     Page::const_span partialSecret) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t request[3 + Page::size];
+  request[0] = 0x3C;
+  request[1] = pageNum;
+  request[2] = (destinationSecret << 2) | masterSecret;
+  copy(partialSecret.begin(), partialSecret.end(), request + 3);
+  return runCommand(request, writeMemoryTimeMs + computeTimeMs);
+}
+
+error_code DS28C40::generateEcc256KeyPair(KeySecret key) {
+  if (key == KeySecretS) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  const uint_least8_t request[] = {0xCB, key == KeySecretB};
+  return runCommand(request, generateEccKeyPairTimeMs);
+}
+
+error_code DS28C40::readRng(span<uint_least8_t> data) {
+  const span<uint_least8_t>::index_type maxDataSize = 64;
+  if ((data.size() < 1) || (data.size() > maxDataSize)) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t buffer[1 + maxDataSize];
+  buffer[0] = readRngCmd;
+  buffer[1] = static_cast<uint_least8_t>(data.size() - 1);
+  span<uint_least8_t> response(buffer, 1 + data.size());
+  const error_code result =
+      runCommand(make_span(buffer, 2), trngGenerationTimeMs, response);
+  if (!result) {
+    copy(response.begin(), response.end(), data.begin());
+  }
+  return result;
+}
+
+error_code DS28C40::entropyHealthTest() {
+  const uint_least8_t request[] = {readRngCmd, 0x80};
+  return runCommand(request, trngOnDemandCheckTimeMs);
+}
+
+error_code DS28C40::runCommand(span<const uint_least8_t> request, int delayTime,
+                               span<uint_least8_t> & response) {
+  const span<const uint_least8_t>::index_type responseInputSize =
+      response.size();
+  error_code result = doRunCommand(request, delayTime, response);
+  if (result) {
+    return result;
+  }
+  if (response.empty()) {
+    return make_error_code(InvalidResponseError);
+  }
+  // Parse command result byte.
+  switch (response[0]) {
+  case 0xAA:
+    // Success response.
+    if (response.size() != responseInputSize) {
+      result = make_error_code(InvalidResponseError);
+    }
+    break;
+
+  case 0x00:
+    result = make_error_code(AuthenticationError);
+    break;
+
+  default:
+    result.assign(response[0], errorCategory());
+    break;
+  }
+  response = response.subspan(1);
+  return result;
+}
+
+error_code DS28C40::runCommand(span<const uint_least8_t> request,
+                               int delayTime) {
+  uint_least8_t buffer;
+  span<uint_least8_t> response(&buffer, 1);
+  return runCommand(request, delayTime, response);
+}
+
+const error_category & DS28C40::errorCategory() {
+  static class : public error_category {
+  public:
+    virtual const char * name() const { return "DS28C40"; }
+
+    virtual std::string message(int condition) const {
+      switch (condition) {
+      case InvalidOperationError:
+        return "Invalid Operation Error";
+
+      case InvalidParameterError:
+        return "Invalid Parameter Error";
+
+      case InvalidSequenceError:
+        return "Invalid Sequence Error";
+
+      case AuthenticationError:
+        return "Authentication Error";
+
+      case InternalError:
+        return "Internal Error";
+
+      case DeviceDisabledError:
+        return "Device Disabled Error";
+
+      case InvalidResponseError:
+        return "Invalid Response Error";
+      }
+      return defaultErrorMessage(condition);
+    }
+  } instance;
+  return instance;
+}
+
+error_code computeMultiblockHash(DS28C40 & device,
+                                 span<const uint_least8_t> data) {
+  error_code result;
+  span<const uint_least8_t>::index_type dataIdx = 0;
+  while (dataIdx < data.size() && !result) {
+    const span<const uint_least8_t>::index_type remainingSize =
+        data.size() - dataIdx;
+    const span<const uint_least8_t>::index_type chunkSize =
+        std::min<span<const uint_least8_t>::index_type>(remainingSize, 64);
+    result =
+        device.computeMultiblockHash(dataIdx == 0, remainingSize == chunkSize,
+                                     data.subspan(dataIdx, chunkSize));
+    dataIdx += chunkSize;
+  }
+  return result;
+}
+
+error_code readRomIdAndManId(DS28C40 & device, RomId::span romId,
+                             ManId::span manId) {
+  DS28C40::Page::array page;
+  error_code result = device.readMemory(DS28C40::romOptionsPage, page);
+  if (!result) {
+    const DS28C40::RomOptions romOptions(page);
+    copy(romOptions.romId(), romId);
+    copy(romOptions.manId(), manId);
+  }
+  return result;
+}
+
+static void setAnonymous(RomId::span romId) {
+  std::fill(romId.begin(), romId.end(), 0xFF);
+}
+
+DS28C40::PageAuthenticationData &
+DS28C40::PageAuthenticationData::setAnonymousRomId() {
+  setAnonymous(romId());
+  return *this;
+}
+
+DS28C40::ComputeSecretData::ComputeSecretData() : data() {
+  setPageNum(0);
+  setManId(ManId::array());
+}
+
+DS28C40::ComputeSecretData &
+DS28C40::ComputeSecretData::setManId(ManId::const_span manId) {
+  ManId::array validatedManId;
+  copy(manId, make_span(validatedManId));
+  validatedManId[1] |= 0x80;
+  data.setManId(validatedManId);
+  return *this;
+}
+
+DS28C40::DecryptionHmacData & DS28C40::DecryptionHmacData::setAnonymousRomId() {
+  setAnonymous(romId());
+  return *this;
+}
+
+} // namespace MaximInterfaceDevices