Maxim Integrated / MaximInterface

Dependents:   DeepCover Embedded Security in IoT MaximInterface MAXREFDES155#

Revision:
0:f77ad7f72d04
Child:
3:f818ea5172ed
diff -r 000000000000 -r f77ad7f72d04 Devices/DS28C36_DS2476.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Devices/DS28C36_DS2476.cpp	Mon Nov 06 14:39:18 2017 -0600
@@ -0,0 +1,805 @@
+/*******************************************************************************
+* Copyright (C) 2017 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 <MaximInterface/Links/I2CMaster.hpp>
+#include <MaximInterface/Utilities/Error.hpp>
+#include <MaximInterface/Utilities/RomId.hpp>
+#include "DS28C36_DS2476.hpp"
+
+using std::copy;
+using std::vector;
+
+namespace MaximInterface {
+
+using namespace Ecc256;
+
+// DS28C36 commands.
+enum Command {
+  WriteMemory = 0x96,
+  ReadMemory = 0x69,
+  WriteBuffer = 0x87,
+  ReadBuffer = 0x5A,
+  ReadPageProtection = 0xAA,
+  SetPageProtection = 0xC3,
+  DecrementCounter = 0xC9,
+  ReadRng = 0xD2,
+  EncryptedReadMemory = 0x4B,
+  ComputeAndReadPathAuthentication = 0xA5,
+  AuthenticatedSha2WriteMemory = 0x99,
+  ComputeAndLockSha2Secret = 0x3C,
+  GenerateEcc256KeyPair = 0xCB,
+  ComputeMultiblockHash = 0x33,
+  VerifyEcdsaSignature = 0x59,
+  AuthenticateEcdsaPublicKey = 0xA8,
+  AuthenticatedEcdsaWriteMemory = 0x89
+};
+
+static error_code convertResultByte(uint_least8_t resultByte) {
+  error_code errorCode;
+  if (resultByte != 0xAA) {
+    errorCode.assign((resultByte == 0x00)
+                         ? static_cast<int>(DS28C36::AuthenticationError)
+                         : resultByte,
+                     DS28C36::errorCategory());
+  }
+  return errorCode;
+}
+
+error_code DS28C36::writeMemory(int pageNum, const Page & page) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.reserve(1 + page.size());
+  buffer.push_back(pageNum);
+  buffer.insert(buffer.end(), page.begin(), page.end());
+  error_code result = writeCommand(WriteMemory, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(writeMemoryTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::readMemory(int pageNum, Page & page) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.push_back(pageNum);
+  error_code result = writeCommand(ReadMemory, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(readMemoryTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == (1 + page.size())) {
+        result = convertResultByte(buffer[0]);
+        copy(buffer.begin() + 1, buffer.end(), page.begin());
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::writeBuffer(const uint_least8_t * data, size_t dataSize) {
+  return writeCommand(WriteBuffer, data, dataSize);
+}
+
+error_code DS28C36::readBuffer(vector<uint_least8_t> & data) {
+  error_code result = writeCommand(ReadBuffer, NULL, 0);
+  if (!result) {
+    result = readResponse(data);
+  }
+  return result;
+}
+
+error_code DS28C36::readPageProtection(int pageNum,
+                                       PageProtection & protection) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.push_back(pageNum);
+  error_code result =
+      writeCommand(ReadPageProtection, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(readMemoryTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        protection = buffer[0];
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::setPageProtection(int pageNum,
+                                      const PageProtection & protection) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.push_back(pageNum);
+  buffer.push_back(static_cast<uint_least8_t>(protection.to_ulong()));
+  error_code result =
+      writeCommand(SetPageProtection, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(writeMemoryTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::decrementCounter() {
+  error_code result = writeCommand(DecrementCounter, NULL, 0);
+  if (!result) {
+    sleep(writeMemoryTimeMs);
+    vector<uint_least8_t> buffer;
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::readRng(int numBytes, vector<uint_least8_t> & data) {
+  if ((numBytes < 1) || (numBytes > 64)) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  data.clear();
+  data.push_back(numBytes - 1);
+  error_code result = writeCommand(ReadRng, &data[0], data.size());
+  if (!result) {
+    sleep(sha256ComputationTimeMs);
+    result = readResponse(data);
+    if (!result) {
+      if (data.size() !=
+          static_cast<vector<uint_least8_t>::size_type>(numBytes)) {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::encryptedReadMemory(int pageNum, SecretNum secretNum,
+                                        EncryptedPage & page) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.push_back((secretNum << 6) | pageNum);
+  error_code result =
+      writeCommand(EncryptedReadMemory, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(readMemoryTimeMs + sha256ComputationTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == (1 + page.challenge.size() + page.data.size())) {
+        result = convertResultByte(buffer[0]);
+        vector<uint_least8_t>::const_iterator begin = buffer.begin() + 1;
+        vector<uint_least8_t>::const_iterator end =
+            begin + page.challenge.size();
+        copy(begin, end, page.challenge.begin());
+        begin = end;
+        end = begin + page.data.size();
+        copy(begin, end, page.data.begin());
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code
+DS28C36::computeAndReadPageAuthentication(int pageNum, AuthType authType,
+                                          vector<uint_least8_t> & data) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  data.clear();
+  data.push_back((authType << 5) | pageNum);
+  error_code result =
+      writeCommand(ComputeAndReadPathAuthentication, &data[0], data.size());
+  if (!result) {
+    sleep(readMemoryTimeMs + ((authType < EcdsaWithKeyA)
+                                  ? sha256ComputationTimeMs
+                                  : generateEcdsaSignatureTimeMs));
+    result = readResponse(data);
+    if (!result) {
+      if (data.size() > 1) {
+        result = convertResultByte(data[0]);
+        data.erase(data.begin());
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code
+DS28C36::computeAndReadEcdsaPageAuthentication(int pageNum, KeyNum keyNum,
+                                               Signature & signature) {
+  AuthType authType;
+  switch (keyNum) {
+  case KeyNumA:
+    authType = EcdsaWithKeyA;
+    break;
+  case KeyNumB:
+    authType = EcdsaWithKeyB;
+    break;
+  case KeyNumC:
+    authType = EcdsaWithKeyC;
+    break;
+  default:
+    return make_error_code(InvalidParameterError);
+  }
+  vector<uint_least8_t> data;
+  error_code result = computeAndReadPageAuthentication(pageNum, authType, data);
+  if (!result) {
+    vector<uint_least8_t>::const_iterator begin = data.begin();
+    vector<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 DS28C36::computeAndReadHmacPageAuthentication(int pageNum,
+                                                         SecretNum secretNum,
+                                                         Sha256::Hash & hmac) {
+  AuthType authType;
+  switch (secretNum) {
+  case SecretNumA:
+    authType = HmacWithSecretA;
+    break;
+  case SecretNumB:
+    authType = HmacWithSecretB;
+    break;
+  case SecretNumS:
+    authType = HmacWithSecretS;
+    break;
+  default:
+    return make_error_code(InvalidParameterError);
+  }
+  vector<uint_least8_t> data;
+  error_code result = computeAndReadPageAuthentication(pageNum, authType, data);
+  if (!result) {
+    copy(data.begin(), data.end(), hmac.begin());
+  }
+  return result;
+}
+
+error_code DS28C36::authenticatedSha2WriteMemory(int pageNum,
+                                                 SecretNum secretNum,
+                                                 const Page & page) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.reserve(1 + page.size());
+  buffer.push_back((secretNum << 6) | pageNum);
+  buffer.insert(buffer.end(), page.begin(), page.end());
+  error_code result =
+      writeCommand(AuthenticatedSha2WriteMemory, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(writeMemoryTimeMs + (2 * sha256ComputationTimeMs));
+    result = readResponse(buffer);
+    if (buffer.size() == 1) {
+      result = convertResultByte(buffer[0]);
+    } else {
+      result = make_error_code(InvalidResponseError);
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::computeAndLockSha2Secret(int pageNum, SecretNum msecretNum,
+                                             SecretNum dsecretNum,
+                                             bool writeProtectEnable) {
+  // User pages only
+  if (pageNum < UserData0 || pageNum > UserData15) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.push_back((dsecretNum << 6) | (msecretNum << 4) | pageNum);
+  buffer.push_back(writeProtectEnable ? 0x80 : 0x00);
+  error_code result =
+      writeCommand(ComputeAndLockSha2Secret, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(sha256ComputationTimeMs +
+          ((writeProtectEnable ? 2 : 1) * writeMemoryTimeMs));
+    result = readResponse(buffer);
+    if (buffer.size() == 1) {
+      result = convertResultByte(buffer[0]);
+    } else {
+      result = make_error_code(InvalidResponseError);
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::generateEcc256KeyPair(KeyNum keyNum,
+                                          bool writeProtectEnable) {
+  if (keyNum == KeyNumS) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  uint_least8_t parameter = keyNum;
+  if (writeProtectEnable) {
+    parameter |= 0x80;
+  }
+  buffer.push_back(parameter);
+  error_code result =
+      writeCommand(GenerateEcc256KeyPair, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(generateEccKeyPairTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::computeMultiblockHash(bool firstBlock, bool lastBlock,
+                                          const uint_least8_t * data,
+                                          size_t dataSize) {
+  uint_least8_t parameter = 0;
+  if (firstBlock) {
+    parameter |= 0x40;
+  }
+  if (lastBlock) {
+    parameter |= 0x80;
+  }
+  vector<uint_least8_t> buffer;
+  buffer.reserve(1 + dataSize);
+  buffer.push_back(parameter);
+  buffer.insert(buffer.end(), data, data + dataSize);
+  error_code result =
+      writeCommand(ComputeMultiblockHash, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(sha256ComputationTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::verifyEcdsaSignature(KeyNum keyNum, HashType hashType,
+                                         const Signature & signature,
+                                         PioState pioa, PioState piob) {
+  uint_least8_t parameter = keyNum | (hashType << 2);
+  if (pioa != Unchanged) {
+    parameter |= 0x20;
+  }
+  if (pioa == Conducting) {
+    parameter |= 0x10;
+  }
+  if (piob != Unchanged) {
+    parameter |= 0x80;
+  }
+  if (piob == Conducting) {
+    parameter |= 0x40;
+  }
+  vector<uint_least8_t> buffer;
+  buffer.reserve(1 + signature.r.size() + signature.s.size());
+  buffer.push_back(parameter);
+  buffer.insert(buffer.end(), signature.r.begin(), signature.r.end());
+  buffer.insert(buffer.end(), signature.s.begin(), signature.s.end());
+  error_code result =
+      writeCommand(VerifyEcdsaSignature, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(verifyEsdsaSignatureOrComputeEcdhTimeMs +
+          ((hashType == DataInBuffer) ? sha256ComputationTimeMs : 0));
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::authenticateEcdsaPublicKey(bool authWrites, bool ecdh,
+                                               KeyNum keyNum, int csOffset,
+                                               const Signature & signature) {
+  if (((keyNum != KeyNumA) && (keyNum != KeyNumB)) || (csOffset < 0) ||
+      (csOffset > 31)) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  uint_least8_t parameter = (csOffset << 3) | (keyNum << 2);
+  if (ecdh) {
+    parameter |= 0x02;
+  }
+  if (authWrites) {
+    parameter |= 0x01;
+  }
+  vector<uint_least8_t> buffer;
+  buffer.reserve(1 + signature.r.size() + signature.s.size());
+  buffer.push_back(parameter);
+  buffer.insert(buffer.end(), signature.r.begin(), signature.r.end());
+  buffer.insert(buffer.end(), signature.s.begin(), signature.s.end());
+  error_code result =
+      writeCommand(AuthenticateEcdsaPublicKey, &buffer[0], buffer.size());
+  if (!result) {
+    sleep((ecdh ? 2 : 1) * verifyEsdsaSignatureOrComputeEcdhTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::authenticatedEcdsaWriteMemory(int pageNum,
+                                                  const Page & page) {
+  if (pageNum < 0 || pageNum >= memoryPages) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.reserve(1 + page.size());
+  buffer.push_back(pageNum);
+  buffer.insert(buffer.end(), page.begin(), page.end());
+  error_code result =
+      writeCommand(AuthenticatedEcdsaWriteMemory, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(verifyEsdsaSignatureOrComputeEcdhTimeMs + writeMemoryTimeMs +
+          sha256ComputationTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        result = convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS28C36::writeCommand(uint_least8_t command,
+                                 const uint_least8_t * parameters,
+                                 size_t parametersSize) {
+  error_code result = master->start(address_);
+  if (result) {
+    master->stop();
+    return result;
+  }
+  result = master->writeByte(command);
+  if (result) {
+    master->stop();
+    return result;
+  }
+  if (parameters) {
+    result = master->writeByte(static_cast<uint_least8_t>(parametersSize));
+    if (result) {
+      master->stop();
+      return result;
+    }
+    result = master->writeBlock(parameters, parametersSize);
+    if (result) {
+      master->stop();
+      return result;
+    }
+  }
+  result = master->stop();
+  return result;
+}
+
+error_code DS28C36::readResponse(vector<uint_least8_t> & response) {
+  error_code result = master->start(address_ | 1);
+  if (result) {
+    master->stop();
+    return result;
+  }
+  uint_least8_t length;
+  result = master->readByte(I2CMaster::Ack, length);
+  if (result) {
+    master->stop();
+    return result;
+  }
+  response.resize(length);
+  result = master->readBlock(I2CMaster::Nack, &response[0], response.size());
+  if (result) {
+    master->stop();
+    return result;
+  }
+  result = master->stop();
+  return result;
+}
+
+DS28C36::PageAuthenticationData
+DS28C36::createPageAuthenticationData(const RomId & romId, const Page & page,
+                                      const Page & challenge, int pageNum,
+                                      const ManId & manId) {
+  PageAuthenticationData data;
+  PageAuthenticationData::iterator dataIt = data.begin();
+  dataIt = copy(romId.begin(), romId.end(), dataIt);
+  dataIt = copy(page.begin(), page.end(), dataIt);
+  dataIt = copy(challenge.begin(), challenge.end(), dataIt);
+  *dataIt = static_cast<uint_least8_t>(pageNum);
+  ++dataIt;
+  copy(manId.begin(), manId.end(), dataIt);
+  return data;
+}
+
+const error_category & DS28C36::errorCategory() {
+  static class : public error_category {
+  public:
+    virtual const char * name() const { return "DS28C36"; }
+
+    virtual std::string message(int condition) const {
+      switch (condition) {
+      case ProtectionError:
+        return "Protection Error";
+
+      case InvalidParameterError:
+        return "Invalid Parameter Error";
+
+      case InvalidSequenceError:
+        return "Invalid Sequence Error";
+
+      case InvalidEcdsaInputOrResultError:
+        return "Invalid ECDSA Input or Result Error";
+
+      case AuthenticationError:
+        return "Authentication Error";
+
+      case InvalidResponseError:
+        return "Invalid Response Error";
+
+      default:
+        return defaultErrorMessage(condition);
+      }
+    }
+  } instance;
+  return instance;
+}
+
+error_code readRomIdAndManId(DS28C36 & ds28c36, RomId * romId, ManId * manId) {
+  DS28C36::Page page;
+  const error_code result = ds28c36.readMemory(DS28C36::RomOptions, page);
+  if (!result) {
+    DS28C36::Page::const_iterator pageIt = page.begin() + 22;
+    if (manId) {
+      copy(pageIt, pageIt + ManId::size(), manId->begin());
+    }
+    pageIt += ManId::size();
+    if (romId) {
+      copy(pageIt, pageIt + RomId::size(), romId->begin());
+    }
+  }
+  return result;
+}
+
+error_code readRng(DS28C36 & ds28c36, uint_least8_t * data, size_t dataSize) {
+  error_code result;
+  vector<uint_least8_t> buffer;
+  while (dataSize > 0) {
+    result = ds28c36.readRng(static_cast<int>(std::min<size_t>(dataSize, 64)),
+                             buffer);
+    if (result) {
+      break;
+    }
+    copy(buffer.begin(), buffer.end(), data);
+    data += buffer.size();
+    dataSize -= buffer.size();
+  }
+  return result;
+}
+
+error_code computeMultiblockHash(DS28C36 & ds28c36, const uint_least8_t * data,
+                                 const size_t dataSize) {
+  error_code result;
+  size_t remainingSize = dataSize;
+  while ((remainingSize > 0) && !result) {
+    const size_t chunkSize = std::min<size_t>(remainingSize, 64);
+    result = ds28c36.computeMultiblockHash(
+        remainingSize == dataSize, remainingSize == chunkSize, data, chunkSize);
+    data += chunkSize;
+    remainingSize -= chunkSize;
+  }
+  return result;
+}
+
+error_code verifyEcdsaSignature(DS28C36 & ds28c36, DS28C36::KeyNum publicKey,
+                                const uint_least8_t * data, size_t dataSize,
+                                const Signature & signature,
+                                DS28C36::PioState pioa,
+                                DS28C36::PioState piob) {
+  error_code result = computeMultiblockHash(ds28c36, data, dataSize);
+  if (!result) {
+    result = ds28c36.verifyEcdsaSignature(publicKey, DS28C36::THASH, signature,
+                                          pioa, piob);
+  }
+  return result;
+}
+
+error_code verifyEcdsaSignature(DS28C36 & ds28c36, const PublicKey & publicKey,
+                                const uint_least8_t * data, size_t dataSize,
+                                const Signature & signature,
+                                DS28C36::PioState pioa,
+                                DS28C36::PioState piob) {
+  error_code result = ds28c36.writeMemory(DS28C36::PublicKeySX, publicKey.x);
+  if (!result) {
+    result = ds28c36.writeMemory(DS28C36::PublicKeySY, publicKey.y);
+  }
+  if (!result) {
+    result = verifyEcdsaSignature(ds28c36, DS28C36::KeyNumS, data, dataSize,
+                                  signature, pioa, piob);
+  }
+  return result;
+}
+
+// DS2476 additional commands.
+enum DS2476_Command {
+  GenerateEcdsaSignature = 0x1E,
+  ComputeSha2UniqueSecret = 0x55,
+  ComputeSha2Hmac = 0x2D
+};
+
+error_code DS2476::generateEcdsaSignature(KeyNum keyNum,
+                                          Ecc256::Signature & signature) {
+  if (keyNum == KeyNumS) {
+    return make_error_code(InvalidParameterError);
+  }
+
+  vector<uint_least8_t> buffer;
+  buffer.push_back(keyNum);
+  error_code result =
+      writeCommand(GenerateEcdsaSignature, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(generateEcdsaSignatureTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == (1 + signature.r.size() + signature.s.size())) {
+        result = convertResultByte(buffer[0]);
+        vector<uint_least8_t>::const_iterator begin = buffer.begin() + 1;
+        vector<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());
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS2476::computeSha2UniqueSecret(SecretNum msecretNum) {
+  vector<uint_least8_t> buffer;
+  buffer.push_back(msecretNum << 4);
+  error_code result =
+      writeCommand(ComputeSha2UniqueSecret, &buffer[0], buffer.size());
+  if (!result) {
+    sleep(sha256ComputationTimeMs);
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == 1) {
+        convertResultByte(buffer[0]);
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code DS2476::computeSha2Hmac(Sha256::Hash & hmac) {
+  error_code result = writeCommand(ComputeSha2Hmac, NULL, 0);
+  if (!result) {
+    sleep(sha256ComputationTimeMs);
+    vector<uint_least8_t> buffer;
+    result = readResponse(buffer);
+    if (!result) {
+      if (buffer.size() == (1 + hmac.size())) {
+        result = convertResultByte(buffer[0]);
+        copy(buffer.begin() + 1, buffer.end(), hmac.begin());
+      } else {
+        result = make_error_code(InvalidResponseError);
+      }
+    }
+  }
+  return result;
+}
+
+error_code enableCoprocessor(DS2476 & ds2476) {
+  DS2476::Page gpioControl;
+  error_code result = ds2476.readMemory(DS2476::GpioControl, gpioControl);
+  if (!result && gpioControl[0] != 0xAA) {
+    gpioControl[0] = 0xAA;
+    result = ds2476.writeMemory(DS2476::GpioControl, gpioControl);
+  }
+  return result;
+}
+
+} // namespace MaximInterface