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

MaximInterfaceDevices/DS28E16.cpp

Committer:
IanBenzMaxim
Date:
2019-07-22
Revision:
7:9cd16581b578
Child:
8:5ea891c7d1a1

File content as of revision 7:9cd16581b578:

/*******************************************************************************
* Copyright (C) 2018 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 "DS28E16.hpp"

namespace MaximInterfaceDevices {

using namespace Core;
using std::copy;
using std::fill;

static const int readMemoryTimeMs = 5;
static const int writeMemoryTimeMs = 60;
static const int shortWriteMemoryTimeMs = 15;
static const int computationTimeMs = 15;

const int DS28E16::decrementCounterPage;
const int DS28E16::masterSecretPage;
const int DS28E16::memoryPages;

error_code DS28E16::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 DS28E16::readMemory(int pageNum, Page::span page) {
  if (pageNum < 0 || pageNum >= memoryPages) {
    return make_error_code(InvalidParameterError);
  }

  uint_least8_t buffer[1 + Page::size * 2];
  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) {
    response = response.first(Page::size);
    copy(response.begin(), response.end(), page.begin());
  }
  return result;
}

error_code DS28E16::readStatus(Status & status) {
  uint_least8_t buffer[1 + Status::PageProtectionList::csize + 2];
  buffer[0] = 0xAA;
  span<uint_least8_t> response(buffer);
  const error_code result =
      runCommand(make_span(buffer, 1), readMemoryTimeMs, response);
  if (!result) {
    span<uint_least8_t>::const_iterator responseIt = response.begin();
    for (Status::PageProtectionList::iterator it =
             status.pageProtection.begin();
         it != status.pageProtection.end(); ++it) {
      *it = *responseIt;
      ++responseIt;
    }
    status.manId = *responseIt;
    ++responseIt;
    status.deviceVersion = *responseIt;
  }
  return result;
}

error_code DS28E16::setPageProtection(int pageNum,
                                      const PageProtection & protection) {
  if (pageNum < 0 || pageNum >= memoryPages) {
    return make_error_code(InvalidParameterError);
  }

  const uint_least8_t request[] = {
      0xC3, static_cast<uint_least8_t>(pageNum),
      static_cast<uint_least8_t>(protection.to_ulong())};
  return runCommand(request, shortWriteMemoryTimeMs);
}

error_code
DS28E16::computeAndReadPageAuthentication(int pageNum, bool anonymous,
                                          DoublePage::const_span challenge,
                                          DoublePage::span hmac) {
  if (pageNum < 0 || pageNum >= memoryPages) {
    return make_error_code(InvalidParameterError);
  }

  const size_t requestSize = 3 + DoublePage::size;
  const size_t responseSize = 1 + DoublePage::size;
  uint_least8_t buffer[MaximInterfaceCore_MAX(requestSize, responseSize)];
  buffer[0] = 0xA5;
  buffer[1] = pageNum;
  if (anonymous) {
    buffer[1] |= 0xE0;
  }
  buffer[2] = 0x02;
  copy(challenge.begin(), challenge.end(), buffer + 3);
  span<uint_least8_t> response(buffer, responseSize);
  const error_code result =
      runCommand(make_span(buffer, requestSize), computationTimeMs, response);
  if (!result) {
    copy(response.begin(), response.end(), hmac.begin());
  }
  return result;
}

error_code DS28E16::computeSecret(int bindingDataPageNum,
                                  bool constantBindingData, bool anonymous,
                                  DoublePage::const_span partialSecret) {
  if (bindingDataPageNum < 0 || bindingDataPageNum >= memoryPages) {
    return make_error_code(InvalidParameterError);
  }

  uint_least8_t request[3 + DoublePage::size];
  request[0] = 0x3C;
  request[1] = bindingDataPageNum;
  if (constantBindingData) {
    request[1] |= 0x04;
  }
  if (anonymous) {
    request[1] |= 0xE0;
  }
  request[2] = 0x08;
  copy(partialSecret.begin(), partialSecret.end(), request + 3);
  return runCommand(request, computationTimeMs);
}

error_code DS28E16::decrementCounter() {
  const uint_least8_t request = 0xC9;
  return runCommand(make_span(&request, 1), writeMemoryTimeMs);
}

error_code DS28E16::lockOutDisableDevice() {
  const DisableDevicePassword::array password = {};
  return disableDevice(LockOutDisableDevice, password);
}

error_code
DS28E16::setDisableDevicePassword(DisableDevicePassword::const_span password) {
  return disableDevice(SetDisableDevicePassword, password);
}

error_code DS28E16::disableDevice(DisableDevicePassword::const_span password) {
  return disableDevice(DisableDevice, password);
}

error_code DS28E16::disableDevice(DisableDeviceOperation operation,
                                  DisableDevicePassword::const_span password) {
  const uint_least8_t request[] = {
      0x33,        static_cast<uint_least8_t>(operation),
      password[0], password[1],
      0x71,        0x35,
      0x0E,        0xAC,
      0x95,        0xF8};
  return runCommand(request, shortWriteMemoryTimeMs);
}

error_code DS28E16::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 DS28E16::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 & DS28E16::errorCategory() {
  static class : public error_category {
  public:
    virtual const char * name() const { return "DS28E16"; }

    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 InternalError:
        return "Internal Error";

      case DeviceDisabledError:
        return "Device Disabled Error";

      case AuthenticationError:
        return "Authentication Error";

      case InvalidResponseError:
        return "Invalid Response Error";
      }
      return defaultErrorMessage(condition);
    }
  } instance;
  return instance;
}

error_code readManId(DS28E16 & device, uint_least8_t & manId) {
  DS28E16::Status status;
  const error_code result = device.readStatus(status);
  if (!result) {
    manId = status.manId;
  }
  return result;
}

DS28E16::PageAuthenticationData &
DS28E16::PageAuthenticationData::setAnonymousRomId() {
  fill(romId().begin(), romId().end(), 0xFF);
  return *this;
}

DS28E16::ComputeSecretData &
DS28E16::ComputeSecretData::setConstantBindingData(bool constantBindingData) {
  if (constantBindingData) {
    data.setPageNum(data.pageNum() | constantBindingDataMask);
    fill(bindingData().begin(), bindingData().end(), 0);
  } else {
    data.setPageNum(data.pageNum() & ~constantBindingDataMask);
  }
  return *this;
}

} // namespace MaximInterfaceDevices