libese: initial commit

libese is a low level library for enabling embedded secure
element use from Java or embedded usecases.

This commit defines the client API, the hardware abstraction API,
and supplies functional implementation of the T=1 protocol (frequently
used as the wire protocol for SPI attached smart cards/eSEs).

Included in this is one hardware reference implementation using
a NXP PN80T developer board wired to a HiKey 6220.

Test: baseline unittests; manual run of the examples/ese_nxp_sample; use
of relay with hardware
Bug: 34193615

Change-Id: I98037793bc29b3730c5301ee9cd5e6cd7465117d
diff --git a/libese-teq1/teq1.c b/libese-teq1/teq1.c
new file mode 100644
index 0000000..9d0b2a5
--- /dev/null
+++ b/libese-teq1/teq1.c
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include <ese/ese.h>
+#include <ese/teq1.h>
+
+#define LOG_TAG "ESE_T=1"
+#include <ese/log.h>
+
+#include "teq1_private.h"
+
+const char *teq1_rule_result_to_name(enum RuleResult result) {
+  switch (result) {
+  case kRuleResultComplete:
+    return "Complete";
+  case kRuleResultAbort:
+    return "Abort";
+  case kRuleResultContinue:
+    return "Continue";
+  case kRuleResultHardFail:
+    return "Hard failure";
+  case kRuleResultResetDevice:
+    return "Reset device";
+  case kRuleResultResetSession:
+    return "Reset session";
+  case kRuleResultRetransmit:
+    return "Retransmit";
+  case kRuleResultSingleShot:
+    return "Single shot";
+  };
+}
+
+const char *teq1_pcb_to_name(uint8_t pcb) {
+  switch (pcb) {
+  case I(0, 0):
+    return "I(0, 0)";
+  case I(0, 1):
+    return "I(0, 1)";
+  case I(1, 0):
+    return "I(1, 0)";
+  case I(1, 1):
+    return "I(1, 1)";
+  case R(0, 0, 0):
+    return "R(0, 0, 0)";
+  case R(0, 0, 1):
+    return "R(0, 0, 1)";
+  case R(0, 1, 0):
+    return "R(0, 1, 0)";
+  case R(0, 1, 1):
+    return "R(0, 1, 1)";
+  case R(1, 0, 0):
+    return "R(1, 0, 0)";
+  case R(1, 0, 1):
+    return "R(1, 0, 1)";
+  case R(1, 1, 0):
+    return "R(1, 1, 0)";
+  case R(1, 1, 1):
+    return "R(1, 1, 1)";
+  case S(RESYNC, REQUEST):
+    return "S(RESYNC, REQUEST)";
+  case S(RESYNC, RESPONSE):
+    return "S(RESYNC, RESPONSE)";
+  case S(IFS, REQUEST):
+    return "S(IFS, REQUEST)";
+  case S(IFS, RESPONSE):
+    return "S(IFS, RESPONSE)";
+  case S(ABORT, REQUEST):
+    return "S(ABORT, REQUEST)";
+  case S(ABORT, RESPONSE):
+    return "S(ABORT, RESPONSE)";
+  case S(WTX, REQUEST):
+    return "S(WTX, REQUEST)";
+  case S(WTX, RESPONSE):
+    return "S(WTX, RESPONSE)";
+  case 255:
+    return "INTERNAL-ERROR";
+  default:
+    return "???";
+  }
+}
+
+int teq1_transmit(struct EseInterface *ese, struct Teq1Frame *frame) {
+  /* Set during ese_open() by teq1_init. */
+  const struct Teq1ProtocolOptions *opts = ese->ops->opts;
+
+  /* Set correct node address. */
+  frame->header.NAD = opts->node_address;
+
+  /* Compute the LRC */
+  frame->INF[frame->header.LEN] = teq1_compute_LRC(frame);
+
+  /*
+   * If the card does something weird, like expect an CRC/LRC based on a
+   * different
+   * header value, the preprocessing can handle it.
+   */
+  if (opts->preprocess)
+    opts->preprocess(opts, frame, 1);
+
+  /*
+   * Begins transmission and ignore errors.
+   * Failed transmissions will result eventually in a resync then reset.
+   */
+  teq1_trace_transmit(frame->header.PCB.val, frame->header.LEN);
+  ese->ops->hw_transmit(ese, frame->val,
+                        sizeof(frame->header) + frame->header.LEN + 1, 1);
+  /* Even though in practice any WTX BWT extension starts when the above
+   * transmit ends.
+   * it is easier to implement it in the polling timeout of receive.
+   */
+  return 0;
+}
+
+int teq1_receive(struct EseInterface *ese, float timeout,
+                 struct Teq1Frame *frame) {
+  const struct Teq1ProtocolOptions *opts = ese->ops->opts;
+  /* Poll the bus until we see the start of frame indicator, the interface NAD.
+   */
+  if (ese->ops->poll(ese, opts->host_address, timeout, 0) < 0) {
+    /* Timed out or comm error. */
+    return -1;
+  }
+  /* We polled for the NAD above. */
+  frame->header.NAD = opts->host_address;
+  /* Get the remainder of the header, but keep the line &open. */
+  ese->ops->hw_receive(ese, (uint8_t *)(&frame->header.PCB),
+                       sizeof(frame->header) - 1, 0);
+  if (frame->header.LEN == 255) {
+    ALOGV("received invalid LEN of 255");
+    /* Close the receive window and return failure. */
+    ese->ops->hw_receive(ese, NULL, 0, 1);
+    return -1;
+  }
+  /*
+   * Get the data and the first byte of CRC data.
+   * Note, CRC support is not implemented. Only a single LRC byte is expected.
+   */
+  ese->ops->hw_receive(ese, (uint8_t *)(&(frame->INF[0])),
+                       frame->header.LEN + 1, 1);
+  teq1_trace_receive(frame->header.PCB.val, frame->header.LEN);
+
+  /*
+   * If the card does something weird, like expect an CRC/LRC based on a
+   * different
+   * header value, the preprocessing should fix up here prior to the LRC check.
+   */
+  if (opts->preprocess)
+    opts->preprocess(opts, frame, 0);
+
+  /* LRC and other protocol goodness checks are not done here. */
+  return frame->header.LEN; /* Return data bytes read. */
+}
+
+uint8_t teq1_fill_info_block(struct Teq1State *state, struct Teq1Frame *frame) {
+  uint32_t inf_len = INF_LEN;
+  if (state->ifs < inf_len)
+    inf_len = state->ifs;
+  switch (frame->header.PCB.type) {
+  case kPcbTypeInfo0:
+  case kPcbTypeInfo1: {
+    uint32_t len = state->app_data.tx_len;
+    if (len > inf_len)
+      len = inf_len;
+    memcpy(frame->INF, state->app_data.tx_buf, len);
+    frame->header.LEN = (len & 0xff);
+    ALOGV("Copying %x bytes of app data for transmission", frame->header.LEN);
+    /* Incrementing here means the caller MUST handle retransmit with prepared
+     * data. */
+    state->app_data.tx_len -= len;
+    state->app_data.tx_buf += len;
+    /* Perform chained transmission if needed. */
+    frame->header.PCB.I.more_data = 0;
+    if (state->app_data.tx_len > 0)
+      frame->header.PCB.I.more_data = 1;
+    return len;
+  }
+  case kPcbTypeSupervisory:
+  case kPcbTypeReceiveReady:
+  default:
+    break;
+  }
+  return 255; /* Invalid block type. */
+}
+
+void teq1_get_app_data(struct Teq1State *state, struct Teq1Frame *frame) {
+  switch (frame->header.PCB.type) {
+  case kPcbTypeInfo0:
+  case kPcbTypeInfo1: {
+    uint8_t len = frame->header.LEN;
+    /* TODO(wad): Some data will be left on the table. Should this error out? */
+    if (len > state->app_data.rx_len)
+      len = state->app_data.rx_len;
+    memcpy(state->app_data.rx_buf, frame->INF, len);
+    /* The original caller must retain the starting pointer to determine
+     * actual available data.
+     */
+    state->app_data.rx_len -= len;
+    state->app_data.rx_buf += len;
+    return;
+  }
+  case kPcbTypeReceiveReady:
+  case kPcbTypeSupervisory:
+  default:
+    break;
+  }
+  return;
+}
+
+/* Returns an R(0) frame with error bits set. */
+uint8_t teq1_frame_error_check(struct Teq1State *state,
+                               struct Teq1Frame *tx_frame,
+                               struct Teq1Frame *rx_frame) {
+  uint8_t lrc = 0;
+  int chained = 0;
+  if (rx_frame->header.PCB.val == 255)
+    return R(0, 1, 0); /* Other error */
+
+  lrc = teq1_compute_LRC(rx_frame);
+  if (rx_frame->INF[rx_frame->header.LEN] != lrc) {
+    ALOGV("Invalid LRC %x instead of %x", rx_frame->INF[rx_frame->header.LEN],
+          lrc);
+    return R(0, 0, 1); /* Parity error */
+  }
+
+  /* Check if we were chained and increment the last sent sequence. */
+  switch (tx_frame->header.PCB.type) {
+  case kPcbTypeInfo0:
+  case kPcbTypeInfo1:
+    chained = tx_frame->header.PCB.I.more_data;
+    state->card_state->seq.interface = tx_frame->header.PCB.I.send_seq;
+  }
+
+  /* Check if we've gone down an easy to catch error hole. The rest will turn up
+   * on the
+   * txrx switch.
+   */
+  switch (rx_frame->header.PCB.type) {
+  case kPcbTypeSupervisory:
+    if (rx_frame->header.PCB.val != S(RESYNC, RESPONSE) &&
+        rx_frame->header.LEN != 1)
+      return R(0, 1, 0);
+    break;
+  case kPcbTypeReceiveReady:
+    if (rx_frame->header.LEN != 0)
+      return R(0, 1, 0);
+#if 0
+/* Handled explicitly. */
+    /* R() blocks are always an error if we're not chaining. */
+    if (!chained)
+      return R(0, 1, 0);
+    /* If we are chaining, the R must be the next seq. */
+    if (rx_frame->header.PCB.R.next_seq == state->card_state->seq.interface)
+      return R(0, 1, 0);
+#endif
+    break;
+  case kPcbTypeInfo0:
+  case kPcbTypeInfo1:
+    /* I-blocks must always alternate for each endpoint. */
+    if (rx_frame->header.PCB.I.send_seq == state->card_state->seq.card) {
+      ALOGV("Got seq %d expected %d", rx_frame->header.PCB.I.send_seq,
+            state->card_state->seq.card);
+      return R(0, 1, 0);
+    }
+    /* Update the card's last I-block seq. */
+    state->card_state->seq.card = rx_frame->header.PCB.I.send_seq;
+  default:
+    break;
+  };
+  return 0;
+}
+
+enum RuleResult teq1_rules(struct Teq1State *state, struct Teq1Frame *tx_frame,
+                           struct Teq1Frame *rx_frame,
+                           struct Teq1Frame *next_tx) {
+  /* Rule 1 is enforced by first call∴ Start with I(0,M). */
+  /* 0 = TX, 1 = RX */
+  /* msb = tx pcb, lsb = rx pcb */
+  /* BUG_ON(!rx_frame && !tx_frame && !next_tx); */
+  uint16_t txrx = TEQ1_RULE(tx_frame->header.PCB.val, rx_frame->header.PCB.val);
+  struct PCB R_err;
+
+  while (1) {
+    /* Timeout errors come like invalid frames: 255. */
+    if ((R_err.val = teq1_frame_error_check(state, tx_frame, rx_frame)) != 0) {
+      ALOGV("incoming frame failed the error check");
+      state->last_error_message = "Invalid frame received";
+      /* Mark the frame as bad for our rule evaluation. */
+      txrx = TEQ1_RULE(tx_frame->header.PCB.val, 255);
+      state->errors++;
+      /* Rule 6.4 */
+      if (state->errors >= 6) { /* XXX: Review error count lifetime. */
+        return kRuleResultResetDevice;
+      }
+      /* Rule 7.4.2 */
+      if (state->errors >= 3) {
+        /* Rule 7.4.1: state should start with error count = 2 */
+        next_tx->header.PCB.val = S(RESYNC, REQUEST);
+        /* Resync result in a fresh session, so we should just continue here. */
+        return kRuleResultContinue;
+      }
+    }
+
+    /* Specific matches */
+    switch (txrx) {
+    /*** Rule 2.1: I() -> I() ***/
+    /* Error check will determine if the card seq is right. */
+    case TEQ1_RULE(I(0, 0), I(0, 0)):
+    case TEQ1_RULE(I(0, 0), I(1, 0)):
+    case TEQ1_RULE(I(1, 0), I(1, 0)):
+    case TEQ1_RULE(I(1, 0), I(0, 0)):
+      /* Read app data & return. */
+      teq1_get_app_data(state, rx_frame);
+      return kRuleResultComplete;
+
+      /* Read app data & return. */
+      teq1_get_app_data(state, rx_frame);
+      return kRuleResultComplete;
+
+    /* Card begins chained response. */
+    case TEQ1_RULE(I(0, 0), I(0, 1)):
+    case TEQ1_RULE(I(1, 0), I(1, 1)):
+      /* Prep R(N(S)) */
+      teq1_get_app_data(state, rx_frame);
+      next_tx->header.PCB.val = TEQ1_R(!rx_frame->header.PCB.I.send_seq, 0, 0);
+      next_tx->header.LEN = 0;
+      return kRuleResultContinue;
+
+    /*** Rule 2.2, Rule 5: Chained transmission ***/
+    case TEQ1_RULE(I(0, 1), R(1, 0, 0)):
+    case TEQ1_RULE(I(1, 1), R(0, 0, 0)):
+      /* Send next block */
+      next_tx->header.PCB.val = I(0, 0);
+      next_tx->header.PCB.I.send_seq = rx_frame->header.PCB.R.next_seq;
+      teq1_fill_info_block(state, next_tx); /* Sets M-bit and LEN. */
+      return kRuleResultContinue;
+
+    /*** Rule 3 ***/
+    case TEQ1_RULE(I(0, 0), S(WTX, REQUEST)):
+    case TEQ1_RULE(I(1, 0), S(WTX, REQUEST)):
+      /* Note: Spec is unclear on if WTX can occur during chaining so we make it
+      an error for now.
+      case TEQ1_RULE(I(0, 1), S(WTX, REQUEST)):
+      case TEQ1_RULE(I(1, 1), S(WTX, REQUEST)):
+      */
+      /* Send S(WTX, RESPONSE) with same INF */
+      next_tx->header.PCB.val = S(WTX, RESPONSE);
+      next_tx->header.LEN = 1;
+      next_tx->INF[0] = rx_frame->INF[0];
+      state->wait_mult = rx_frame->INF[0];
+      /* Then wait BWT*INF[0] after transmission. */
+      return kRuleResultSingleShot; /* Send then call back in with same tx_frame
+                                       and new rx_frame. */
+
+    /*** Rule 4 ***/
+    case TEQ1_RULE(S(IFS, REQUEST), S(IFS, RESPONSE)):
+      /* XXX: Check INFs match. */
+      return kRuleResultComplete; /* This is treated as an unique operation. */
+    case TEQ1_RULE(I(0, 0), S(IFS, REQUEST)):
+    case TEQ1_RULE(I(0, 1), S(IFS, REQUEST)):
+    case TEQ1_RULE(I(1, 0), S(IFS, REQUEST)):
+    case TEQ1_RULE(I(1, 1), S(IFS, REQUEST)):
+    /* Don't support a IFS_REQUEST if we sent an error R-block. */
+    case TEQ1_RULE(R(0, 0, 0), S(IFS, REQUEST)):
+    case TEQ1_RULE(R(1, 0, 0), S(IFS, REQUEST)):
+      next_tx->header.PCB.val = S(IFS, RESPONSE);
+      next_tx->header.LEN = 1;
+      next_tx->INF[0] = rx_frame->INF[0];
+      state->ifs = rx_frame->INF[0];
+      return kRuleResultSingleShot;
+
+    /*** Rule 5  (see Rule 2.2 for the chained-tx side. ) ***/
+    case TEQ1_RULE(R(0, 0, 0), I(0, 0)):
+    case TEQ1_RULE(R(1, 0, 0), I(1, 0)):
+      /* Chaining ended with terminal I-block. */
+      teq1_get_app_data(state, rx_frame);
+      return kRuleResultComplete;
+    case TEQ1_RULE(R(0, 0, 0), I(0, 1)):
+    case TEQ1_RULE(R(1, 0, 0), I(1, 1)):
+      /* Chaining continued; consume partial data and send R(N(S)) */
+      teq1_get_app_data(state, rx_frame);
+      next_tx->header.PCB.val = TEQ1_R(!rx_frame->header.PCB.I.send_seq, 0, 0);
+      return kRuleResultContinue;
+
+    /* Rule 6: Interface can send a RESYNC */
+    /* Rule 6.1: timeout BWT right. No case here. */
+    /* Rule 6.2, 6.3 */
+    case TEQ1_RULE(S(RESYNC, REQUEST), S(RESYNC, RESPONSE)):
+      /* Rule 6.5: indicates that the card should assume its prior
+       * block was lost _and_ the interface gets transmit privilege,
+       * so we just start fresh.
+       */
+      return kRuleResultResetSession; /* Start a new exchange (rule 6.3) */
+    case TEQ1_RULE(S(RESYNC, REQUEST), 255):
+      /* Retransmit the same frame up to 3 times. */
+      return kRuleResultRetransmit;
+
+    /* Rule 7.1, 7.5, 7.6 */
+    case TEQ1_RULE(I(0, 0), 255):
+    case TEQ1_RULE(I(1, 0), 255):
+    case TEQ1_RULE(I(0, 1), 255):
+    case TEQ1_RULE(I(1, 1), 255):
+      next_tx->header.PCB.val = R_err.val;
+      next_tx->header.PCB.R.next_seq = tx_frame->header.PCB.I.send_seq;
+      ALOGV("Rule 7.1,7.5,7.6: bad rx - sending error R: %x = %s",
+            next_tx->header.PCB.val, teq1_pcb_to_name(next_tx->header.PCB.val));
+      return kRuleResultSingleShot; /* So we still can retransmit the original.
+                                       */
+
+    /* Caught in the error check. */
+    case TEQ1_RULE(I(0, 0), R(1, 0, 0)):
+    case TEQ1_RULE(I(0, 0), R(1, 0, 1)):
+    case TEQ1_RULE(I(0, 0), R(1, 1, 0)):
+    case TEQ1_RULE(I(0, 0), R(1, 1, 1)):
+    case TEQ1_RULE(I(1, 0), R(0, 0, 0)):
+    case TEQ1_RULE(I(1, 0), R(0, 0, 1)):
+    case TEQ1_RULE(I(1, 0), R(0, 1, 0)):
+    case TEQ1_RULE(I(1, 0), R(0, 1, 1)):
+      next_tx->header.PCB.val = R(0, 0, 0);
+      next_tx->header.PCB.R.next_seq = tx_frame->header.PCB.I.send_seq;
+      ALOGV("Rule 7.1,7.5,7.6: weird rx - sending error R: %x = %s",
+            next_tx->header.PCB.val, teq1_pcb_to_name(next_tx->header.PCB.val));
+      return kRuleResultSingleShot;
+
+    /* Rule 7.2: Retransmit the _same_ R-block. */
+    /* The remainder of this rule is implemented in the next switch. */
+    case TEQ1_RULE(R(0, 0, 0), 255):
+    case TEQ1_RULE(R(0, 0, 1), 255):
+    case TEQ1_RULE(R(0, 1, 0), 255):
+    case TEQ1_RULE(R(0, 1, 1), 255):
+    case TEQ1_RULE(R(1, 0, 0), 255):
+    case TEQ1_RULE(R(1, 0, 1), 255):
+    case TEQ1_RULE(R(1, 1, 0), 255):
+    case TEQ1_RULE(R(1, 1, 1), 255):
+      return kRuleResultRetransmit;
+
+    /* Rule 7.3 request */
+    /* Note, 7.3 for transmission of S(*, RESPONSE) won't be seen because they
+     * are
+     * single shots.
+     * Instead, the invalid block will be handled as invalid for the prior TX.
+     * This should yield the correct R-block.
+     */
+    case TEQ1_RULE(I(0, 0), R(0, 0, 0)):
+    case TEQ1_RULE(I(0, 0), R(0, 0, 1)):
+    case TEQ1_RULE(I(0, 0), R(0, 1, 0)):
+    case TEQ1_RULE(I(0, 0), R(0, 1, 1)):
+    case TEQ1_RULE(I(1, 0), R(1, 0, 0)):
+    case TEQ1_RULE(I(1, 0), R(1, 1, 0)):
+    case TEQ1_RULE(I(1, 0), R(1, 0, 1)):
+    case TEQ1_RULE(I(1, 0), R(1, 1, 1)):
+    case TEQ1_RULE(I(0, 1), R(0, 0, 0)):
+    case TEQ1_RULE(I(0, 1), R(0, 1, 0)):
+    case TEQ1_RULE(I(0, 1), R(0, 0, 1)):
+    case TEQ1_RULE(I(0, 1), R(0, 1, 1)):
+    case TEQ1_RULE(I(1, 1), R(1, 0, 0)):
+    case TEQ1_RULE(I(1, 1), R(1, 1, 0)):
+    case TEQ1_RULE(I(1, 1), R(1, 0, 1)):
+    case TEQ1_RULE(I(1, 1), R(1, 1, 1)):
+      /* Retransmit I-block */
+      return kRuleResultRetransmit;
+
+    /* Rule 8 is card only. */
+    /* Rule 9: aborting a chain.
+     * If a S(ABORT) is injected into this engine, then we may have sent an
+     * abort.
+     */
+    case TEQ1_RULE(S(ABORT, REQUEST), S(ABORT, RESPONSE)):
+      /* No need to send back a R() because we want to keep transmit. */
+      return kRuleResultComplete; /* If we sent it, then we are complete. */
+    case TEQ1_RULE(S(ABORT, RESPONSE), R(0, 0, 0)):
+    case TEQ1_RULE(S(ABORT, RESPONSE), R(1, 0, 0)):
+      /* Card triggered abortion complete but we can resume sending. */
+      return kRuleResultAbort;
+    /* An abort request can interrupt a chain anywhere and could occur
+     * after a failure path too.
+     */
+    case TEQ1_RULE(I(0, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(I(1, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 0, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 0, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 1, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 1, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 0, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 0, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 1, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 1, 1), S(ABORT, REQUEST)):
+      next_tx->header.PCB.val = S(ABORT, REQUEST);
+      return kRuleResultContinue; /* Takes over prior flow. */
+    case TEQ1_RULE(S(ABORT, RESPONSE), 255):
+      return kRuleResultRetransmit;
+    /* Note, other blocks should be caught below. */
+    default:
+      break;
+    }
+
+    /* Note, only S(ABORT, REQUEST) and S(IFS, REQUEST) are supported
+     * for transmitting to the card. Others will result in error
+     * flows.
+     *
+     * For supported flows: If an operation was paused to
+     * send it, the caller may then switch to that state and resume.
+     */
+    if (rx_frame->header.PCB.val != 255) {
+      ALOGV("Unexpected frame. Marking error and re-evaluating.");
+      rx_frame->header.PCB.val = 255;
+      continue;
+    }
+
+    return kRuleResultHardFail;
+  }
+}
+
+/*
+ * TODO(wad): Consider splitting teq1_transcieve() into
+ *   teq1_transcieve_init() and teq1_transceive_process_one()
+ *   if testing becomes onerous given the loop below.
+ */
+
+API size_t teq1_transceive(struct EseInterface *ese,
+                           const uint8_t *const tx_buf, size_t tx_len,
+                           uint8_t *rx_buf, size_t rx_len) {
+  struct Teq1Frame tx_frame[2];
+  struct Teq1Frame rx_frame;
+  struct Teq1Frame *tx = &tx_frame[0];
+  int was_reset = 0;
+  int active = 0;
+  int done = 0;
+  enum RuleResult result = kRuleResultComplete;
+  const struct Teq1ProtocolOptions *opts = ese->ops->opts;
+  struct Teq1CardState *card_state = (struct Teq1CardState *)(&ese->pad[0]);
+  struct Teq1State init_state =
+      TEQ1_INIT_STATE(tx_buf, tx_len, rx_buf, rx_len, card_state);
+  struct Teq1State state =
+      TEQ1_INIT_STATE(tx_buf, tx_len, rx_buf, rx_len, card_state);
+
+  /* First I-block is always I(0, M). After that, modulo 2. */
+  tx->header.PCB.val = TEQ1_I(!card_state->seq.interface, 0);
+  teq1_fill_info_block(&state, tx);
+
+  teq1_trace_header();
+  while (!done) {
+    /* Populates the node address and LRC prior to attempting to transmit. */
+    teq1_transmit(ese, tx);
+
+    /* If tx was pointed to the inactive frame for a single shot, restore it
+     * now. */
+    tx = &tx_frame[active];
+
+    /* Clear the RX frame. */
+    memset(&rx_frame, 0xff, sizeof(rx_frame));
+
+    /* -1 indicates a timeout or failure from hardware. */
+    if (teq1_receive(ese, opts->bwt * (float)state.wait_mult, &rx_frame) < 0) {
+      /* TODO(wad): If the ese_error(ese) == 1, should this go ahead and fail?
+       */
+      /* Failures are considered invalid blocks in the rule engine below. */
+      rx_frame.header.PCB.val = 255;
+    }
+    /* Always reset |wait_mult| once we have calculated the timeout. */
+    state.wait_mult = 1;
+
+    /* Clear the inactive frame header for use as |next_tx|. */
+    memset(&tx_frame[!active].header, 0, sizeof(tx_frame[!active].header));
+
+    result = teq1_rules(&state, tx, &rx_frame, &tx_frame[!active]);
+    ALOGV("[ %s ]", teq1_rule_result_to_name(result));
+    switch (result) {
+    case kRuleResultComplete:
+      done = 1;
+      break;
+    case kRuleResultRetransmit:
+      /* TODO(wad) Find a clean way to move into teq1_rules(). */
+      if (state.retransmits++ < 3)
+        continue;
+      if (tx->header.PCB.val == S(RESYNC, REQUEST)) {
+        ese_set_error(ese, TEQ1_ERROR_HARD_FAIL);
+        return 0;
+      }
+      /* Fall through */
+      tx_frame[!active].header.PCB.val = S(RESYNC, REQUEST);
+    case kRuleResultContinue:
+      active = !active;
+      tx = &tx_frame[active];
+      state.retransmits = 0;
+      state.errors = 0;
+      continue;
+    case kRuleResultHardFail:
+      ese_set_error(ese, TEQ1_ERROR_HARD_FAIL);
+      return 0;
+    case kRuleResultAbort:
+      ese_set_error(ese, TEQ1_ERROR_ABORT);
+      return 0;
+    case kRuleResultSingleShot:
+      /*
+       * Send the next_tx on loop, but tell the rule engine that
+       * the last sent state hasn't changed. This allows for easy error
+       * and supervisory block paths without nesting state.
+       */
+      tx = &tx_frame[!active];
+      continue;
+    case kRuleResultResetDevice:
+      if (was_reset || !ese->ops->hw_reset || ese->ops->hw_reset(ese) == -1) {
+        ese_set_error(ese, TEQ1_ERROR_DEVICE_RESET);
+        return 0; /* Don't keep resetting -- hard fail. */
+      }
+      was_reset = 1;
+    /* Fall through to session reset. */
+    case kRuleResultResetSession:
+      /* Roll back state and reset. */
+      state = init_state;
+      TEQ1_INIT_CARD_STATE(state.card_state);
+      /* Reset the active frame. */
+      memset(tx, 0, sizeof(*tx));
+      /* Load initial I-block. */
+      tx->header.PCB.val = I(0, 0);
+      teq1_fill_info_block(&state, tx);
+      continue;
+    }
+  }
+  /* Return the number of bytes used in rx_buf. */
+  return rx_len - state.app_data.rx_len;
+}
+
+API uint8_t teq1_compute_LRC(const struct Teq1Frame *frame) {
+  uint8_t lrc = 0;
+  const uint8_t *buffer = frame->val;
+  const uint8_t *end = buffer + frame->header.LEN + sizeof(frame->header);
+  while (buffer < end) {
+    lrc ^= *buffer++;
+  }
+  return lrc;
+}