Merge "Enable Weaver throttling." am: 3c1268556e am: e539bdd74b am: dc453a0255
am: a0528642b0
Change-Id: Ibd803d0c789c0ad73d2bb9db6559bad193303ee3
diff --git a/apps/boot/README.md b/apps/boot/README.md
index 1fbce35..65744d6 100644
--- a/apps/boot/README.md
+++ b/apps/boot/README.md
@@ -261,7 +261,7 @@
stay powered on for a period of time -- either at the end of use or before the
requested command can be serviced. As the behavior is implementation specific,
the only effective option is to keep the secure element powered for the number of
-minutes specified by the response uint32\_t.
+seconds specified by the response uint32\_t.
For chips that support it, like the one this applet is being tested on, the
cooldown time can be requested with a special APDU to ese\_transceive():
@@ -269,7 +269,7 @@
FFE10000
In response, a 6 byte response will contain a uint32\_t and a successful status
-code (90 00). The unsigned little endian integer indicates how many minutes
+code (90 00). The unsigned little-endian integer indicates how many seconds
the chip needs to stay powered and unused to cooldown. If this happens before
the locks or rollback storage can be read, the bootloader will need to
determine a safe delay or recovery path until boot can proceed securely.
diff --git a/apps/weaver/card/build.xml b/apps/weaver/card/build.xml
index fdb4ee5..0805fc2 100644
--- a/apps/weaver/card/build.xml
+++ b/apps/weaver/card/build.xml
@@ -37,14 +37,14 @@
output="weaver_comm.cap" sources="src/com/android/weaver" export="export/comm">
<applet class="com.android.weaver.Weaver" aid="A00000006203010C0101"/>
<import exps="export/comm" />
+ <import exps="jcopx-4.0-R2" jar="jcopx-4.0-R2/api-jcopx-4.0-R2.jar" />
</cap>
<!-- Core applet -->
<cap aid="A00000006203010C02" package="com.android.weaver.core" version="0.1"
output="weaver_core.cap" sources="src/com/android/weaver/core" export="export/core">
- <!-- TODO: reintroduce jcopx dependency for DSTimer -->
- <!-- Grab the other interfaces from export/ -->
- <import exps="export/comm" jar="export/comm/weaver.jar" />
<applet class="com.android.weaver.core.WeaverCore" aid="A00000006203010C0201"/>
+ <import exps="export/comm" jar="export/comm/weaver.jar" />
+ <import exps="jcopx-4.0-R2" jar="jcopx-4.0-R2/api-jcopx-4.0-R2.jar" />
</cap>
</javacard>
</target>
diff --git a/apps/weaver/card/src/com/android/weaver/core/CoreSlots.java b/apps/weaver/card/src/com/android/weaver/core/CoreSlots.java
index e744f02..29e4841 100644
--- a/apps/weaver/card/src/com/android/weaver/core/CoreSlots.java
+++ b/apps/weaver/card/src/com/android/weaver/core/CoreSlots.java
@@ -24,7 +24,7 @@
import com.android.weaver.Consts;
import com.android.weaver.Slots;
-//import com.nxp.id.jcopx.util.DSTimer;
+import com.nxp.id.jcopx.util.DSTimer;
class CoreSlots implements Slots {
static final byte NUM_SLOTS = 64;
@@ -87,7 +87,7 @@
private byte[] mKey = new byte[Consts.SLOT_KEY_BYTES];
private byte[] mValue = new byte[Consts.SLOT_VALUE_BYTES];
private short mFailureCount;
- //private DSTimer mBackoffTimer;
+ private DSTimer mBackoffTimer;
/**
* Transactionally reset the slot with a new key and value.
@@ -103,7 +103,7 @@
Util.arrayCopy(keyBuffer, keyOffset, mKey, (short) 0, Consts.SLOT_KEY_BYTES);
Util.arrayCopy(valueBuffer, valueOffset, mValue, (short) 0, Consts.SLOT_VALUE_BYTES);
mFailureCount = 0;
- //mBackoffTimer = DSTimer.getInstance();
+ mBackoffTimer = DSTimer.getInstance();
JCSystem.commitTransaction();
}
@@ -115,7 +115,7 @@
arrayFill(mKey, (short) 0, Consts.SLOT_KEY_BYTES, (byte) 0);
arrayFill(mValue, (short) 0, Consts.SLOT_VALUE_BYTES, (byte) 0);
mFailureCount = 0;
- //mBackoffTimer.stopTimer();
+ mBackoffTimer.stopTimer();
JCSystem.commitTransaction();
}
@@ -130,10 +130,7 @@
*/
public byte read(byte[] keyBuffer, short keyOffset, byte[] outBuffer, short outOffset) {
// Check timeout has expired or hasn't been started
- // TODO: bring back the timer
- //mBackoffTimer.getRemainingTime(sRemainingBackoff, (short) 0);
- Util.setShort(sRemainingBackoff, (short) 0, (short) 0);
- Util.setShort(sRemainingBackoff, (short) 2, (short) 0);
+ mBackoffTimer.getRemainingTime(sRemainingBackoff, (short) 0);
if (sRemainingBackoff[0] != 0 || sRemainingBackoff[1] != 0 ||
sRemainingBackoff[2] != 0 || sRemainingBackoff[3] != 0) {
Util.arrayCopyNonAtomic(
@@ -162,10 +159,10 @@
// Start the timer on a failure
if (throttle(sRemainingBackoff, (short) 0, mFailureCount)) {
- //mBackoffTimer.startTimer(
- // sRemainingBackoff, (short) 0, DSTimer.DST_POWEROFFMODE_FALLBACK);
+ mBackoffTimer.startTimer(
+ sRemainingBackoff, (short) 0, DSTimer.DST_POWEROFFMODE_FALLBACK);
} else {
- //mBackoffTimer.stopTimer();
+ mBackoffTimer.stopTimer();
}
final byte[] data = (result == Consts.READ_SUCCESS) ? mValue : sRemainingBackoff;
@@ -184,7 +181,7 @@
}
/**
- * Calculates the timeout in milliseconds as a function of the failure
+ * Calculates the timeout in seconds as a function of the failure
* counter 'x' as follows:
*
* [0, 5) -> 0
@@ -194,7 +191,7 @@
* [30, 140) -> 30 * (2^((x - 30)/10))
* [140, inf) -> 1 day
*
- * The 32-bit millisecond timeout is written to the array.
+ * The 32-bit timeout in seconds is written to the array.
*
* @return Whether there is any throttle time.
*/
@@ -202,28 +199,27 @@
short highWord = 0;
short lowWord = 0;
- final short thirtySecondsInMilliseconds = 0x7530; // = 1000 * 30
+ final short thirtySeconds = 30;
if (failureCount == 0) {
// 0s
} else if (failureCount > 0 && failureCount <= 10) {
if (failureCount % 5 == 0) {
// 30s
- lowWord = thirtySecondsInMilliseconds;
+ lowWord = thirtySeconds;
} else {
// 0s
}
} else if (failureCount < 30) {
// 30s
- lowWord = thirtySecondsInMilliseconds;
+ lowWord = thirtySeconds;
} else if (failureCount < 140) {
// 30 * (2^((x - 30)/10))
final short shift = (short) ((short) (failureCount - 30) / 10);
- highWord = (short) (thirtySecondsInMilliseconds >> (16 - shift));
- lowWord = (short) (thirtySecondsInMilliseconds << shift);
+ lowWord = (short) (thirtySeconds << shift);
} else {
- // 1 day in ms = 1000 * 60 * 60 * 24 = 0x526 5C00
- highWord = 0x0526;
- lowWord = 0x5c00;
+ // 1 day in seconds = 24 * 60 * 60 = 0x1 5180
+ highWord = 0x1;
+ lowWord = 0x5180;
}
// Write the value to the buffer
diff --git a/apps/weaver/weaver.c b/apps/weaver/weaver.c
index b6fed66..d0fb0e9 100644
--- a/apps/weaver/weaver.c
+++ b/apps/weaver/weaver.c
@@ -340,16 +340,17 @@
const uint8_t READ_SUCCESS = 0x00;
const uint8_t READ_WRONG_KEY = 0x7f;
const uint8_t READ_BACK_OFF = 0x76;
+ const uint32_t millisInSecond = 1000;
// wrong key
if (appletStatus == READ_WRONG_KEY) {
ALOGI("ese_weaver_read wrong key provided");
- *timeout = get_uint32(value);
+ *timeout = get_uint32(value) * millisInSecond;
return ESE_WEAVER_READ_WRONG_KEY;
}
// backoff
if (appletStatus == READ_BACK_OFF) {
ALOGI("ese_weaver_read wrong key provided");
- *timeout = get_uint32(value);
+ *timeout = get_uint32(value) * millisInSecond;
return ESE_WEAVER_READ_TIMEOUT;
}
if (rx_len != 19) {
diff --git a/libese-hw/nxp/Android.bp b/libese-hw/nxp/Android.bp
index 46150c0..1950052 100644
--- a/libese-hw/nxp/Android.bp
+++ b/libese-hw/nxp/Android.bp
@@ -19,7 +19,12 @@
proprietary: true,
defaults: ["libese-defaults"],
srcs: ["pn80t/common.c"],
- shared_libs: ["liblog", "libese", "libese-teq1"],
+ shared_libs: [
+ "liblog",
+ "libese",
+ "libese-teq1",
+ "libese-sysdeps",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
target: {
@@ -38,7 +43,12 @@
enabled: false,
},
},
- shared_libs: ["liblog", "libese", "libese-teq1"],
+ shared_libs: [
+ "liblog",
+ "libese",
+ "libese-teq1",
+ "libese-sysdeps",
+ ],
static_libs: ["libese-hw-nxp-pn80t-common"],
}
diff --git a/libese-hw/nxp/pn80t/common.c b/libese-hw/nxp/pn80t/common.c
index 81dd89d..12d1972 100644
--- a/libese-hw/nxp/pn80t/common.c
+++ b/libese-hw/nxp/pn80t/common.c
@@ -18,10 +18,6 @@
#include "include/ese/hw/nxp/pn80t/common.h"
-#ifndef INT_MAX
-#define INT_MAX 2147483647
-#endif
-
int nxp_pn80t_preprocess(const struct Teq1ProtocolOptions *const opts,
struct Teq1Frame *frame, int tx) {
if (tx) {
@@ -149,57 +145,65 @@
return -1;
}
-/* Returns the minutes the chip has requested to stay powered for internal
+/* Returns the seconds the chip has requested to stay powered for internal
* maintenance. This is not expected during normal operation, but it is still
* a possible operating response.
*
* There are three timers reserved for internal state usage which are
* not reliable API. As such, this function returns the maximum time
- * in minutes that the chip would like to stay powered-on.
+ * in seconds that the chip would like to stay powered-on.
*/
-#define TIMER_COUNT 3
-#define TIMER_BASE 0xF0
+#define SECURE_TIMER 0xF1
+#define ATTACK_COUNTER 0xF2
+#define RESTRICTED_MODE_PENALTY 0xF3
uint32_t nxp_pn80t_send_cooldown(struct EseInterface *ese, bool end) {
- const struct Pn80tPlatform *platform = ese->ops->opts;
- const static uint8_t kEndofApduSession[] = {0x5a, 0xc5, 0x00, 0xc5};
- const static uint8_t kResetSession[] = {0x5a, 0xc4, 0x00, 0xc4};
- uint8_t rx_buf[32];
- uint32_t bytes_read = 0;
- int timer = 0;
- uint32_t *timers[TIMER_COUNT];
- uint32_t max_wait = 0;
- const uint8_t *message = kResetSession;
if (ese->error.is_err) {
return 0;
}
- if (end) {
- message = kEndofApduSession;
- }
- ese->ops->hw_transmit(ese, kEndofApduSession, sizeof(kEndofApduSession), 1);
+
+ const static uint8_t kEndofApduSession[] = {0x5a, 0xc5, 0x00, 0xc5};
+ const static uint8_t kResetSession[] = {0x5a, 0xc4, 0x00, 0xc4};
+ const uint8_t *const message = end ? kEndofApduSession : kResetSession;
+ const uint32_t message_len =
+ end ? sizeof(kEndofApduSession) : sizeof(kResetSession);
+ ese->ops->hw_transmit(ese, message, message_len, 1);
+
nxp_pn80t_poll(ese, kTeq1Options.host_address, 5.0f, 0);
- bytes_read = ese->ops->hw_receive(ese, rx_buf, sizeof(rx_buf), 1);
- ALOGI("Requested power-down delay times (min):");
+ uint8_t rx_buf[32];
+ const uint32_t bytes_read =
+ ese->ops->hw_receive(ese, rx_buf, sizeof(rx_buf), 1);
+ ALOGI("Requested power-down delay times (sec):");
+
/* For each tag type, walk the response to extract the value. */
+ uint32_t max_wait = 0;
if (bytes_read >= 0x8 && rx_buf[0] == 0xe5 && rx_buf[1] == 0x12) {
- for (; timer < TIMER_COUNT; ++timer) {
- timers[timer] = NULL;
- uint8_t *tag = &rx_buf[2];
- while (tag < (rx_buf + bytes_read)) {
- uint8_t *tag_len = tag + 1;
- if (*tag == (TIMER_BASE | (timer + 1))) {
- if (*tag_len == 4) {
- timers[timer] = (uint32_t *)(tag + 2);
- ALOGI("- Timer 0x%.2X: %d", (TIMER_BASE | (timer + 1)),
- *timers[timer]);
- if (*timers[timer] > max_wait) {
- max_wait = *timers[timer];
- }
+ uint8_t *tag_ptr = &rx_buf[2];
+ while (tag_ptr < (rx_buf + bytes_read)) {
+ const uint8_t tag = *tag_ptr;
+ const uint8_t length = *(tag_ptr + 1);
+
+ // The cooldown timers are 32-bit values
+ if (length == 4) {
+ const uint32_t *const value_ptr = (uint32_t *)(tag_ptr + 2);
+ uint32_t cooldown = ese_be32toh(*value_ptr);
+ switch (tag) {
+ case ATTACK_COUNTER:
+ // This cooldown timer is in minutes, so convert it to seconds
+ cooldown *= 60;
+ // Fallthrough
+ case SECURE_TIMER:
+ case RESTRICTED_MODE_PENALTY:
+ ALOGI("- Timer 0x%.2X: %d", tag, cooldown);
+ if (cooldown > max_wait) {
+ max_wait = cooldown;
}
break;
- } else {
- tag += 1;
+ default:
+ // Ignore -- not a cooldown timer
+ break;
}
}
+ tag_ptr += 2 + length;
}
}
return max_wait;
@@ -231,6 +235,7 @@
case kResetCommand:
ALOGI("interface command received: reset");
if (nxp_pn80t_reset(ese) < 0) {
+ /* TODO(wad): wire up a call to hw_reset and teq1_reset */
/* Warning, state unchanged error. */
ok[0] = 0x62;
}
@@ -253,8 +258,8 @@
case kCooldownCommand:
ALOGI("interface command received: cooldown");
uint8_t reply[6] = {0, 0, 0, 0, 0x90, 0x00};
- uint32_t cooldownMin = nxp_pn80t_send_cooldown(ese, false);
- *(uint32_t *)(&reply[0]) = cooldownMin;
+ const uint32_t cooldownSec = nxp_pn80t_send_cooldown(ese, false);
+ *(uint32_t *)(&reply[0]) = ese_htole32(cooldownSec);
return ese_sg_from_buf(rx_buf, rx_len, 0, sizeof(reply), reply);
}
return 0;
@@ -264,7 +269,7 @@
const struct EseSgBuffer *tx_buf, uint32_t tx_len,
struct EseSgBuffer *rx_buf, uint32_t rx_len) {
- uint32_t recvd =
+ const uint32_t recvd =
nxp_pn80t_handle_interface_call(ese, tx_buf, tx_len, rx_buf, rx_len);
if (recvd > 0) {
return recvd;
@@ -273,19 +278,17 @@
}
void nxp_pn80t_close(struct EseInterface *ese) {
- struct NxpState *ns;
- const struct Pn80tPlatform *platform = ese->ops->opts;
- uint32_t wait_min = 0;
ALOGV("%s: called", __func__);
- ns = NXP_PN80T_STATE(ese);
- if (!ese->error.is_err) {
- wait_min = nxp_pn80t_send_cooldown(ese, true);
- }
+ struct NxpState *ns = NXP_PN80T_STATE(ese);
+ const struct Pn80tPlatform *platform = ese->ops->opts;
+ const uint32_t wait_sec =
+ ese->error.is_err ? 0 : nxp_pn80t_send_cooldown(ese, true);
+
/* In practice, this should probably migrate into the kernel
* and into the spidev code such that each platform can handle it
* as needed.
*/
- if (!wait_min) {
+ if (wait_sec == 0) {
platform->toggle_reset(ns->handle, 0);
if (platform->toggle_power_req) {
platform->toggle_power_req(ns->handle, 0);
@@ -294,6 +297,7 @@
platform->toggle_ven(ns->handle, 0);
}
}
+
platform->release(ns->handle);
ns->handle = NULL;
}
diff --git a/libese-sysdeps/android-sysdeps.c b/libese-sysdeps/android-sysdeps.c
index 5cfb447..7f78985 100644
--- a/libese-sysdeps/android-sysdeps.c
+++ b/libese-sysdeps/android-sysdeps.c
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <endian.h>
#include <stdint.h>
#include <string.h>
@@ -24,3 +25,15 @@
void *ese_memset(void *__s, int __c, uint64_t __n) {
return memset(__s, __c, __n);
}
+
+uint32_t ese_be32toh(uint32_t big_endian_32bits) {
+ return be32toh(big_endian_32bits);
+}
+
+uint32_t ese_htobe32(uint32_t host_32bits) { return htobe32(host_32bits); }
+
+uint32_t ese_le32toh(uint32_t little_endian_32bits) {
+ return le32toh(little_endian_32bits);
+}
+
+uint32_t ese_htole32(uint32_t host_32bits) { return htole32(host_32bits); }
diff --git a/libese-sysdeps/darwin-sysdeps.c b/libese-sysdeps/darwin-sysdeps.c
index 5cfb447..ab15d95 100644
--- a/libese-sysdeps/darwin-sysdeps.c
+++ b/libese-sysdeps/darwin-sysdeps.c
@@ -17,6 +17,8 @@
#include <stdint.h>
#include <string.h>
+#include <libkern/OSByteOrder.h>
+
void *ese_memcpy(void *__dest, const void *__src, uint64_t __n) {
return memcpy(__dest, __src, __n);
}
@@ -24,3 +26,19 @@
void *ese_memset(void *__s, int __c, uint64_t __n) {
return memset(__s, __c, __n);
}
+
+uint32_t ese_be32toh(uint32_t big_endian_32bits) {
+ return OSSwapBigToHostInt32(big_endian_32bits);
+}
+
+uint32_t ese_htobe32(uint32_t host_32bits) {
+ return OSSwapHostToBigInt32(host_32bits);
+}
+
+uint32_t ese_le32toh(uint32_t little_endian_32bits) {
+ return OSSwapLittleToHostInt32(little_endian_32bits);
+}
+
+uint32_t ese_htole32(uint32_t host_32bits) {
+ return OSSwapHostToLittleInt32(host_32bits);
+}
diff --git a/libese-sysdeps/include/ese/sysdeps.h b/libese-sysdeps/include/ese/sysdeps.h
index c829ea8..7d20e01 100644
--- a/libese-sysdeps/include/ese/sysdeps.h
+++ b/libese-sysdeps/include/ese/sysdeps.h
@@ -38,4 +38,9 @@
extern void *ese_memcpy(void *__dest, const void *__src, uint64_t __n);
extern void *ese_memset(void *__s, int __c, uint64_t __n);
+extern uint32_t ese_be32toh(uint32_t big_endian_32bits);
+extern uint32_t ese_htobe32(uint32_t host_32bits);
+extern uint32_t ese_le32toh(uint32_t little_endian_32bits);
+extern uint32_t ese_htole32(uint32_t host_32bits);
+
#endif /* ESE_SYSDEPS_H__ */