aboutsummaryrefslogblamecommitdiff
path: root/modexp.c
blob: 37f5e0e9aaaf54d3da4fe76655c27ae6c43f12a5 (plain) (tree)
pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * ks_token.c
 * ----------
 * Keystore implementation in flash memory.
 *
 * Authors: Rob Austein, Fredrik Thulin
 * Copyright (c) 2015-2017, NORDUnet A/S All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This keystore driver operates over bare flash, versus over a flash file
 * system or flash translation layer. The block size is large enough to
 * hold an AES-keywrapped 4096-bit RSA key. Any remaining space in the key
 * block may be used to store attributes (opaque TLV blobs). If the
 * attributes overflow the key block, additional blocks may be added, but
 * no attribute may exceed the block size.
 */

#include <stddef.h>
#include <string.h>
#include <assert.h>

#include "hal.h"
#include "hal_internal.h"
#include "ks.h"

#include "last_gasp_pin_internal.h"

#define HAL_OK CMSIS_HAL_OK
#include "stm-keystore.h"
#undef HAL_OK

#ifndef KS_TOKEN_CACHE_SIZE
#define KS_TOKEN_CACHE_SIZE 4
#endif

#if HAL_KS_BLOCK_SIZE % KEYSTORE_SUBSECTOR_SIZE != 0
#error Keystore block size is not a multiple of flash subsector size
#endif

#define NUM_FLASH_BLOCKS ((KEYSTORE_NUM_SUBSECTORS * KEYSTORE_SUBSECTOR_SIZE) / HAL_KS_BLOCK_SIZE)
#define SUBSECTORS_PER_BLOCK (HAL_KS_BLOCK_SIZE / KEYSTORE_SUBSECTOR_SIZE)

/*
 * Keystore database.
 */

typedef struct {
  hal_ks_t              ks;                  /* Must be first (C "subclassing") */
  hal_ks_pin_t          wheel_pin;
  hal_ks_pin_t          so_pin;
  hal_ks_pin_t          user_pin;
} ks_token_db_t;

/*
 * This is a bit silly, but it's safe enough, and it lets us avoid a
 * nasty mess of forward references.
 */

#define db      ((ks_token_db_t * const) hal_ks_token)

/*
 * Calculate offset of the block in the flash address space.
 */

static inline uint32_t ks_token_offset(const unsigned blockno)
{
  return blockno * HAL_KS_BLOCK_SIZE;
}

/*
 * Read a flash block.
 *
 * Flash read on the Alpha is slow enough that it pays to check the
 * first page before reading the rest of the block.
 */

static hal_error_t ks_token_read(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block)
{
  if (ks != hal_ks_token || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != HAL_KS_BLOCK_SIZE)
    return HAL_ERROR_IMPOSSIBLE;

  if (keystore_read_data(ks_token_offset(blockno),
                         block->bytes,
                         KEYSTORE_PAGE_SIZE) != CMSIS_HAL_OK)
    return HAL_ERROR_KEYSTORE_ACCESS;

  switch (hal_ks_block_get_type(block)) {
  case HAL_KS_BLOCK_TYPE_ERASED:
  case HAL_KS_BLOCK_TYPE_ZEROED:
    return HAL_OK;
  case HAL_KS_BLOCK_TYPE_KEY:
  case HAL_KS_BLOCK_TYPE_PIN:
    break;
  default:
    return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE;
  }

  switch (hal_ks_block_get_status(block)) {
  case HAL_KS_BLOCK_STATUS_LIVE:
  case HAL_KS_BLOCK_STATUS_TOMBSTONE:
    break;
  default:
    return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE;
  }

  if (keystore_read_data(ks_token_offset(blockno) + KEYSTORE_PAGE_SIZE,
                         block->bytes + KEYSTORE_PAGE_SIZE,
                         sizeof(*block) - KEYSTORE_PAGE_SIZE) != CMSIS_HAL_OK)
    return HAL_ERROR_KEYSTORE_ACCESS;

  if (hal_ks_block_calculate_crc(block) != block->header.crc)
    return HAL_ERROR_KEYSTORE_BAD_CRC;

  return HAL_OK;
}

/*
 * Convert a live block into a tombstone.  Caller is responsible for
 * making sure that the block being converted is valid; since we don't
 * need to update the CRC for this, we just modify the first page.
 */

static hal_error_t ks_token_deprecate(hal_ks_t *ks, const unsigned blockno)
{
  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
    return HAL_ERROR_IMPOSSIBLE;

  uint8_t page[KEYSTORE_PAGE_SIZE];
  hal_ks_block_header_t *header = (void *) page;
  uint32_t offset = ks_token_offset(blockno);

  if (keystore_read_data(offset, page, sizeof(page)) != CMSIS_HAL_OK)
    return HAL_ERROR_KEYSTORE_ACCESS;

  header->block_status = HAL_KS_BLOCK_STATUS_TOMBSTONE;

  if (keystore_write_data(offset, page, sizeof(page)) != CMSIS_HAL_OK)
    return HAL_ERROR_KEYSTORE_ACCESS;

  return HAL_OK;
}

/*
 * Zero (not erase) a flash block.  Just need to zero the first page.
 */

static hal_error_t ks_token_zero(hal_ks_t *ks, const unsigned blockno)
{
  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
    return HAL_ERROR_IMPOSSIBLE;

  uint8_t page[KEYSTORE_PAGE_SIZE] = {0};

  if (keystore_write_data(ks_token_offset(blockno), page, sizeof(page)) != CMSIS_HAL_OK)
    return HAL_ERROR_KEYSTORE_ACCESS;

  return HAL_OK;
}

/*
 * Erase a flash block.  Also see ks_token_erase_maybe(), below.
 */

static hal_error_t ks_token_erase(hal_ks_t *ks, const unsigned blockno)
{
  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
    return HAL_ERROR_IMPOSSIBLE;

  unsigned subsector =  blockno      * SUBSECTORS_PER_BLOCK;
  const unsigned end = (blockno + 1) * SUBSECTORS_PER_BLOCK;

  do {
    if (keystore_erase_subsector(subsector) != CMSIS_HAL_OK)
      return HAL_ERROR_KEYSTORE_ACCESS;
  } while (++subsector < end);

  return HAL_OK;
}

/*
 * Erase a flash block if it hasn't already been erased.
 * May not be necessary, trying to avoid unnecessary wear.
 *
 * Unclear whether there's any sane reason why this needs to be
 * constant time, given how slow erasure is.  But side channel attacks
 * can be tricky things, and it's theoretically possible that we could
 * leak information about, eg, key length, so we do constant time.
 */

static hal_error_t ks_token_erase_maybe(hal_ks_t *ks, const unsigned blockno)
{
  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
    return HAL_ERROR_IMPOSSIBLE;

  uint8_t mask = 0xFF;

  for (uint32_t a = ks_token_offset(blockno); a < ks_token_offset(blockno + 1); a += KEYSTORE_PAGE_SIZE) {
    uint8_t page[KEYSTORE_PAGE_SIZE];
    if (keystore_read_data(a, page, sizeof(page)) != CMSIS_HAL_OK)
      return HAL_ERROR_KEYSTORE_ACCESS;
    for (int i = 0; i < KEYSTORE_PAGE_SIZE; i++)
      mask &= page[i];
  }

  return mask == 0xFF ? HAL_OK : ks_token_erase(ks, blockno);
}

/*
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * modexp.c
 * ----------
 * Wrapper around Cryptech ModExp core.
 *
 * This doesn't do full RSA, that's another module.  This module's job
 * is just the I/O to get bits in and out of the ModExp core, including
 * compensating for a few known bugs that haven't been resolved yet.
 *
 * If at some point the interface to the ModExp core becomes simple
 * enough that this module is no longer needed, it will go away.
 *
 * Authors: Rob Austein
 * Copyright (c) 2015-2017, NORDUnet A/S
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the NORDUnet nor the names of its contributors may
 *   be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdint.h>

#include "hal.h"
#include "hal_internal.h"

/*
 * Whether we want debug output.
 */

static int debug = 0;

void hal_modexp_set_debug(const int onoff)
{
  debug = onoff;
}

/*
 * Get value of an ordinary register.
 */

static inline hal_error_t get_register(const hal_core_t *core,
                                       const hal_addr_t addr,
                                       uint32_t *value)
{
  hal_error_t err;
  uint8_t w[4];

  if (value == NULL)
    return HAL_ERROR_IMPOSSIBLE;

  if ((err = hal_io_read(core, addr, w, sizeof(w))) != HAL_OK)
    return err;

  *value = (w[0] << 0) | (w[1] << 8) | (w[2] << 16) | (w[3] << 24);

  return HAL_OK;
}

/*
 * Set value of an ordinary register.
 */

static inline hal_error_t set_register(const hal_core_t *core,
                                       const hal_addr_t addr,
                                       const uint32_t value)
{
  const uint8_t w[4] = {
    ((value >> 24) & 0xFF),
    ((value >> 16) & 0xFF),
    ((value >>  8) & 0xFF),
    ((value >>  0) & 0xFF)
  };

  return hal_io_write(core, addr, w, sizeof(w));
}

/*
 * Get value of a data buffer.  We reverse the order of 32-bit words
 * in the buffer during the transfer to match what the modexpa7 core
 * expects.
 */

static inline hal_error_t get_buffer(const hal_core_t *core,
                                     const hal_addr_t data_addr,
                                     uint8_t *value,
                                     const size_t length)
{
  hal_error_t err;
  size_t i;

  if (value == NULL || length % 4 != 0)
    return HAL_ERROR_IMPOSSIBLE;

  for (i = 0; i < length; i += 4)
    if ((err = hal_io_read(core, data_addr + i/4, &value[length - 4 - i], 4)) != HAL_OK)
      return err;

  return HAL_OK;
}

/*
 * Set value of a data buffer.  We reverse the order of 32-bit words
 * in the buffer during the transfer to match what the modexpa7 core
 * expects.
 *
 * Do we need to zero the portion of the buffer we're not using
 * explictly (that is, the portion between `length` and the value of
 * the core's MODEXPA7_ADDR_BUFFER_BITS register)?  We've gotten away
 * without doing this so far, but the core doesn't take an explicit
 * length parameter for the message itself, instead it assumes that
 * the message is either as long as or twice as long as the exponent,
 * depending on the setting of the CRT mode bit.  Maybe initializing
 * the core clears the excess bits so there's no issue?  Dunno.  Have
 * never seen a problem with this yet, just dont' know why not.
 */

static inline hal_error_t set_buffer(const hal_core_t *core,
                                     const hal_addr_t data_addr,
                                     const uint8_t * const value,
                                     const size_t length)
{
  hal_error_t err;
  size_t i;

  if (value == NULL || length % 4 != 0)
    return HAL_ERROR_IMPOSSIBLE;

  for (i = 0; i < length; i += 4)
    if ((err = hal_io_write(core, data_addr + i/4, &value[length - 4 - i], 4)) != HAL_OK)
      return err;

  return HAL_OK;
}

/*
 * Stuff moved out of modexp so we can run two cores in parallel more
 * easily.  We have to return to the jacket routine every time we kick
 * a core into doing something, since only the jacket routines know
 * how many cores we're running for any particular calculation.
 *
 * In theory we could do something clever where we don't wait for both
 * cores to finish precalc before starting either of them on the main
 * computation, but that way probably lies madness.
 */

static inline hal_error_t check_args(hal_modexp_arg_t *a)
{
  /*
   * All data pointers must be set, exponent may not be longer than
   * modulus, message may not be longer than twice the modulus (CRT
   * mode), result buffer must not be shorter than modulus, and all
   * input lengths must be a multiple of four bytes (the core is all
   * about 32-bit words).
   */

  if (a         == NULL ||
      a->msg    == NULL || a->msg_len    > MODEXPA7_OPERAND_BYTES || a->msg_len    >  a->mod_len * 2 ||
      a->exp    == NULL || a->exp_len    > MODEXPA7_OPERAND_BYTES || a->exp_len    >  a->mod_len     ||
      a->mod    == NULL || a->mod_len    > MODEXPA7_OPERAND_BYTES ||
      a->result == NULL || a->result_len > MODEXPA7_OPERAND_BYTES || a->result_len <  a->mod_len     ||
      a->coeff  == NULL || a->coeff_len  > MODEXPA7_OPERAND_BYTES ||
      a->mont   == NULL || a->mont_len   > MODEXPA7_OPERAND_BYTES ||
      ((a->msg_len | a->exp_len | a->mod_len) & 3) != 0)
    return HAL_ERROR_BAD_ARGUMENTS;

  return HAL_OK;
}

static inline hal_error_t setup_precalc(const int precalc, hal_modexp_arg_t *a)
{
  hal_error_t err;

  /*
   * Check that operand size is compatabible with the core.
   */

  uint32_t operand_max = 0;

  if ((err = get_register(a->core, MODEXPA7_ADDR_BUFFER_BITS, &operand_max)) != HAL_OK)
    return err;

  operand_max /= 8;

  if (a->msg_len   > operand_max ||
      a->exp_len   > operand_max ||
      a->mod_len   > operand_max ||
      a->coeff_len > operand_max ||
      a->mont_len  > operand_max)
    return HAL_ERROR_BAD_ARGUMENTS;

  /*
   * Set the modulus, then initiate calculation of modulus-dependent
   * speedup factors if necessary, by edge-triggering the "init" bit,
   * then return to caller so it can wait for precalc.
   */

  if ((err = set_register(a->core, MODEXPA7_ADDR_MODULUS_BITS, a->mod_len * 8)) != HAL_OK  ||
      (err = set_buffer(a->core, MODEXPA7_ADDR_MODULUS, a->mod, a->mod_len))    != HAL_OK  ||
      (precalc && (err = hal_io_zero(a->core))                                  != HAL_OK) ||
      (precalc && (err = hal_io_init(a->core))                                  != HAL_OK))
    return err;

  return HAL_OK;
}

static inline hal_error_t setup_calc(const int precalc, hal_modexp_arg_t *a)
{
  hal_error_t err;

  /*
   * Select CRT mode if and only if message is longer than exponent.
   */

  const uint32_t mode = a->msg_len > a->mod_len ? MODEXPA7_MODE_CRT : MODEXPA7_MODE_PLAIN;

  /*
   * Copy out precalc results if necessary, then load everything and
   * start the calculation by edge-triggering the "next" bit.  If
   * everything works, return to caller so it can wait for the
   * calculation to complete.
   */

  if ((precalc &&
       (err = get_buffer(a->core, MODEXPA7_ADDR_MODULUS_COEFF_OUT,     a->coeff, a->coeff_len)) != HAL_OK) ||
      (precalc &&
        (err = get_buffer(a->core, MODEXPA7_ADDR_MONTGOMERY_FACTOR_OUT, a->mont,  a->mont_len)) != HAL_OK) ||
      (err = set_buffer(a->core, MODEXPA7_ADDR_MODULUS_COEFF_IN,     a->coeff, a->coeff_len))   != HAL_OK  ||
      (err = set_buffer(a->core, MODEXPA7_ADDR_MONTGOMERY_FACTOR_IN, a->mont,  a->mont_len))    != HAL_OK  ||
      (err = set_register(a->core, MODEXPA7_ADDR_MODE, mode))                                   != HAL_OK  ||
      (err = set_buffer(a->core, MODEXPA7_ADDR_MESSAGE, a->msg, a->msg_len))                    != HAL_OK  ||
      (err = set_buffer(a->core, MODEXPA7_ADDR_EXPONENT, a->exp, a->exp_len))                   != HAL_OK  ||
      (err = set_register(a->core, MODEXPA7_ADDR_EXPONENT_BITS, a->exp_len * 8))                != HAL_OK  ||
      (err = hal_io_zero(a->core))                                                              != HAL_OK  ||
      (err = hal_io_next(a->core)) != HAL_OK)
    return err;

  return HAL_OK;
}

static inline hal_error_t extract_result(hal_modexp_arg_t *a)
{
  /*
   * Extract results from the main calculation and we're done.
   * Hardly seems worth making this a separate function.
   */

  return get_buffer(a->core, MODEXPA7_ADDR_RESULT, a->result, a->mod_len);
}

/*
 * Run one modexp operation.
 */

hal_error_t hal_modexp(const int precalc, hal_modexp_arg_t *a)
{
  hal_error_t err;

  if ((err = check_args(a)) != HAL_OK)
    return err;

  if ((err = hal_core_alloc(MODEXPA7_NAME, &a->core)) == HAL_OK  &&
      (err = setup_precalc(precalc, a))               == HAL_OK  &&
      (!precalc ||
       (err = hal_io_wait_ready(a->core))             == HAL_OK) &&
      (err = setup_calc(precalc, a))                  == HAL_OK  &&
      (err = hal_io_wait_valid(a->core))              == HAL_OK  &&
      (err = extract_result(a))                       == HAL_OK)
    err = HAL_OK;

  hal_core_free(a->core);
  return err;
}

/*
 * Run two modexp operations in parallel.
 */

hal_error_t hal_modexp2(const int precalc, hal_modexp_arg_t *a1, hal_modexp_arg_t *a2)
{
  hal_error_t err;

  if ((err = check_args(a1)) != HAL_OK ||
      (err = check_args(a2)) != HAL_OK)
    return err;

  if ((err = hal_core_alloc2(MODEXPA7_NAME, &a1->core,
                             MODEXPA7_NAME, &a2->core)) == HAL_OK  &&
      (err = setup_precalc(precalc, a1))               == HAL_OK  &&
      (err = setup_precalc(precalc, a2))               == HAL_OK  &&
      (!precalc ||
       (err = hal_io_wait_ready2(a1->core, a2->core))  == HAL_OK) &&
      (err = setup_calc(precalc, a1))                  == HAL_OK  &&
      (err = setup_calc(precalc, a2))                  == HAL_OK  &&
      (err = hal_io_wait_valid2(a1->core, a2->core))   == HAL_OK  &&
      (err = extract_result(a1))                       == HAL_OK  &&
      (err = extract_result(a2))                       == HAL_OK)
    err = HAL_OK;

  hal_core_free(a1->core);
  hal_core_free(a2->core);
  return err;
}

/*
 * Local variables:
 * indent-tabs-mode: nil
 * End:
 */
ss="cm">/* * Local variables: * indent-tabs-mode: nil * End: */