nanohub: firmware: add AOSP header support

 1. Change the secure loading FSM to accept new header format
 2. Add support for arbitrary flash relocation base
 3. rearrange source tree
 4. OS update BL API
 5. Rework shared area management
 6. Add key update with nanoapp image
 7. Add OS update with nanoapp image

Bug: 28265099
Change-Id: I1710f46eb6abf0256cd25f834a01b559a79e9c99
Signed-off-by: Alexey Polyudov <[email protected]>
diff --git a/firmware/src/aes.c b/firmware/src/aes.c
deleted file mode 100644
index 3496670..0000000
--- a/firmware/src/aes.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2016 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 <stdint.h>
-#include <aes.h>
-
-
-#define AES_NUM_ROUNDS    14
-
-
-
-static const uint8_t FwdSbox[] = {
-    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
-    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
-    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
-    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
-    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
-    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
-    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
-    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
-    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
-    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
-    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
-    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
-    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
-    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
-    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
-    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
-};
-
-static const uint8_t RevSbox[] = {
-    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
-    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
-    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
-    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
-    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
-    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
-    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
-    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
-    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
-    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
-    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
-    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
-    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
-    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
-    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
-    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,
-};
-
-static const uint32_t FwdTab0[] = { //other 3 tables are this same table, RORed 8, 16, and 24 bits respectively.
-    0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554,
-    0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A,
-    0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B,
-    0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B,
-    0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F,
-    0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F,
-    0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5,
-    0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F,
-    0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB,
-    0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497,
-    0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED,
-    0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A,
-    0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594,
-    0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3,
-    0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504,
-    0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D,
-    0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739,
-    0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395,
-    0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883,
-    0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76,
-    0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4,
-    0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B,
-    0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0,
-    0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818,
-    0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651,
-    0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85,
-    0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12,
-    0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9,
-    0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7,
-    0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A,
-    0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8,
-    0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A,
-};
-
-static const uint32_t RevTab0[] = { //other 3 tables are this same table, RORed 8, 16, and 24 bits respectively.
-    0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1, 0xACFA58AB, 0x4BE30393,
-    0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F,
-    0xDEB15A49, 0x25BA1B67, 0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6,
-    0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, 0x49E06929, 0x8EC9C844,
-    0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4,
-    0x63DF4A18, 0xE51A3182, 0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94,
-    0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, 0xE31F8F57, 0x6655AB2A,
-    0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C,
-    0x8ACF1C2B, 0xA779B492, 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A,
-    0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, 0x5E719F06, 0xBD6E1051,
-    0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF,
-    0x1998FB24, 0xD6BDE997, 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB,
-    0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48, 0x1E1170AC, 0x6C5A724E,
-    0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A,
-    0x0C0A67B1, 0x9357E70F, 0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16,
-    0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD, 0x2DB6A8B9, 0x141EA9C8,
-    0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34,
-    0x8B432976, 0xCB23C6DC, 0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120,
-    0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, 0x0D8652EC, 0x77C1E3D0,
-    0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF,
-    0x87494EC7, 0xD938D1C1, 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4,
-    0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, 0x2E39F75E, 0x82C3AFF5,
-    0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B,
-    0xCD267809, 0x6E5918F4, 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6,
-    0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, 0xC6A59430, 0x35A266C0,
-    0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F,
-    0x764DD68D, 0x43EFB04D, 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F,
-    0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252, 0xE9105633, 0x6DD64713,
-    0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C,
-    0x9CD2DF59, 0x55F2733F, 0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86,
-    0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C, 0x283C498B, 0xFF0D9541,
-    0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742,
-};
-
-#ifdef ARM
-
-    #define STRINFIGY2(b) #b
-    #define STRINGIFY(b) STRINFIGY2(b)
-    #define ror(v, b) ({uint32_t ret; if (b) asm("ror %0, #" STRINGIFY(b) :"=r"(ret):"0"(v)); else ret = v; ret;})
-
-#else
-
-    inline static uint32_t ror(uint32_t val, uint32_t by)
-    {
-        if (!by)
-            return val;
-
-        val = (val >> by) | (val << (32 - by));
-
-        return val;
-    }
-
-#endif
-
-
-void aesInitForEncr(struct AesContext *ctx, const uint32_t *k)
-{
-    uint32_t i, *ks = ctx->K, roundConstant = 0x01000000;
-
-    //first 8 words are just the key itself
-    memcpy(ctx->K, k, sizeof(uint32_t[AES_KEY_WORDS]));
-
-    //create round keys for encryption
-    for (i = 0; i < 7; i++, ks += 8, roundConstant <<= 1) {
-        ks[8] = ks[0] ^ roundConstant
-             ^ (((uint32_t)FwdSbox[(ks[ 7] >> 16) & 0xff]) << 24)
-             ^ (((uint32_t)FwdSbox[(ks[ 7] >>  8) & 0xff]) << 16)
-             ^ (((uint32_t)FwdSbox[(ks[ 7] >>  0) & 0xff]) <<  8)
-             ^ (((uint32_t)FwdSbox[(ks[ 7] >> 24) & 0xff]) <<  0);
-        ks[9] = ks[1] ^ ks[8];
-        ks[10] = ks[2] ^ ks[9];
-        ks[11] = ks[3] ^ ks[10];
-        ks[12] = ks[4]
-             ^ (((uint32_t)FwdSbox[(ks[11] >> 24) & 0xff]) << 24)
-             ^ (((uint32_t)FwdSbox[(ks[11] >> 16) & 0xff]) << 16)
-             ^ (((uint32_t)FwdSbox[(ks[11] >>  8) & 0xff]) <<  8)
-             ^ (((uint32_t)FwdSbox[(ks[11] >>  0) & 0xff]) <<  0);
-        ks[13] = ks[5] ^ ks[12];
-        ks[14] = ks[6] ^ ks[13];
-        ks[15] = ks[7] ^ ks[14];
-    }
-}
-
-void aesInitForDecr(struct AesContext *ctx, struct AesSetupTempWorksSpace *tmpSpace, const uint32_t *k)
-{
-    uint32_t i, j, *ks = ctx->K + 4, *encrK = tmpSpace->tmpCtx.K + 52;
-
-    //we need encryption keys to calculate decryption keys
-    aesInitForEncr(&tmpSpace->tmpCtx, k);
-
-    //now we can calculate round keys for decryption
-    memcpy(ctx->K, tmpSpace->tmpCtx.K + 56, sizeof(uint32_t[4]));
-    for (i = 0; i < AES_NUM_ROUNDS - 1; i++, encrK -= 4, ks += 4) { //num_rounds-1 seems to be concensus, but num_rounds make more sense...
-        for (j = 0; j < 4; j++) {
-            ks[j] =
-                ror(RevTab0[FwdSbox[(encrK[j] >> 24) & 0xff]],  0) ^
-                ror(RevTab0[FwdSbox[(encrK[j] >> 16) & 0xff]],  8) ^
-                ror(RevTab0[FwdSbox[(encrK[j] >>  8) & 0xff]], 16) ^
-                ror(RevTab0[FwdSbox[(encrK[j] >>  0) & 0xff]], 24);
-        }
-    }
-    memcpy(ks, encrK, sizeof(uint32_t[4]));
-}
-
-void aesEncr(struct AesContext *ctx, const uint32_t *src, uint32_t *dst)
-{
-    uint32_t x0, x1, x2, x3; //we CAN use an array, but then GCC will not use registers. so we use separate vars. sigh...
-    uint32_t *k = ctx->K, i;
-
-    //setup
-    x0 = *src++ ^ *k++;
-    x1 = *src++ ^ *k++;
-    x2 = *src++ ^ *k++;
-    x3 = *src++ ^ *k++;
-
-    //all-but-last round
-    for (i = 0; i < AES_NUM_ROUNDS - 1; i++) {
-        uint32_t t0, t1, t2;
-
-        t0 = *k++ ^
-            ror(FwdTab0[(x0 >> 24) & 0xff],  0) ^
-            ror(FwdTab0[(x1 >> 16) & 0xff],  8) ^
-            ror(FwdTab0[(x2 >>  8) & 0xff], 16) ^
-            ror(FwdTab0[(x3 >>  0) & 0xff], 24);
-
-        t1 = *k++ ^
-            ror(FwdTab0[(x1 >> 24) & 0xff],  0) ^
-            ror(FwdTab0[(x2 >> 16) & 0xff],  8) ^
-            ror(FwdTab0[(x3 >>  8) & 0xff], 16) ^
-            ror(FwdTab0[(x0 >>  0) & 0xff], 24);
-
-        t2 = *k++ ^
-            ror(FwdTab0[(x2 >> 24) & 0xff],  0) ^
-            ror(FwdTab0[(x3 >> 16) & 0xff],  8) ^
-            ror(FwdTab0[(x0 >>  8) & 0xff], 16) ^
-            ror(FwdTab0[(x1 >>  0) & 0xff], 24);
-
-        x3 = *k++ ^
-            ror(FwdTab0[(x3 >> 24) & 0xff],  0) ^
-            ror(FwdTab0[(x0 >> 16) & 0xff],  8) ^
-            ror(FwdTab0[(x1 >>  8) & 0xff], 16) ^
-            ror(FwdTab0[(x2 >>  0) & 0xff], 24);
-
-       x0 = t0;
-       x1 = t1;
-       x2 = t2;
-    }
-
-    //last round
-    *dst++ = *k++ ^
-            (((uint32_t)(FwdSbox[(x0 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(FwdSbox[(x1 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(FwdSbox[(x2 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(FwdSbox[(x3 >>  0) & 0xff])) <<  0);
-
-    *dst++ = *k++ ^
-            (((uint32_t)(FwdSbox[(x1 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(FwdSbox[(x2 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(FwdSbox[(x3 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(FwdSbox[(x0 >>  0) & 0xff])) <<  0);
-
-    *dst++ = *k++ ^
-            (((uint32_t)(FwdSbox[(x2 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(FwdSbox[(x3 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(FwdSbox[(x0 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(FwdSbox[(x1 >>  0) & 0xff])) <<  0);
-
-    *dst++ = *k++ ^
-            (((uint32_t)(FwdSbox[(x3 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(FwdSbox[(x0 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(FwdSbox[(x1 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(FwdSbox[(x2 >>  0) & 0xff])) <<  0);
-}
-
-void aesDecr(struct AesContext *ctx, const uint32_t *src, uint32_t *dst)
-{
-    uint32_t x0, x1, x2, x3;
-    uint32_t *k = ctx->K, i;
-
-    //setup
-    x0 = *src++ ^ *k++;
-    x1 = *src++ ^ *k++;
-    x2 = *src++ ^ *k++;
-    x3 = *src++ ^ *k++;
-
-    //all-but-last round
-    for (i = 0; i < AES_NUM_ROUNDS - 1; i++) {
-        uint32_t t0, t1, t2;
-
-        t0 = *k++ ^
-            ror(RevTab0[(x0 >> 24) & 0xff],  0) ^
-            ror(RevTab0[(x3 >> 16) & 0xff],  8) ^
-            ror(RevTab0[(x2 >>  8) & 0xff], 16) ^
-            ror(RevTab0[(x1 >>  0) & 0xff], 24);
-
-        t1 = *k++ ^
-            ror(RevTab0[(x1 >> 24) & 0xff],  0) ^
-            ror(RevTab0[(x0 >> 16) & 0xff],  8) ^
-            ror(RevTab0[(x3 >>  8) & 0xff], 16) ^
-            ror(RevTab0[(x2 >>  0) & 0xff], 24);
-
-        t2 = *k++ ^
-            ror(RevTab0[(x2 >> 24) & 0xff],  0) ^
-            ror(RevTab0[(x1 >> 16) & 0xff],  8) ^
-            ror(RevTab0[(x0 >>  8) & 0xff], 16) ^
-            ror(RevTab0[(x3 >>  0) & 0xff], 24);
-
-        x3 = *k++ ^
-            ror(RevTab0[(x3 >> 24) & 0xff],  0) ^
-            ror(RevTab0[(x2 >> 16) & 0xff],  8) ^
-            ror(RevTab0[(x1 >>  8) & 0xff], 16) ^
-            ror(RevTab0[(x0 >>  0) & 0xff], 24);
-
-       x0 = t0;
-       x1 = t1;
-       x2 = t2;
-    }
-
-    //last round
-    *dst++ = *k++ ^
-            (((uint32_t)(RevSbox[(x0 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(RevSbox[(x3 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(RevSbox[(x2 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(RevSbox[(x1 >>  0) & 0xff])) <<  0);
-
-    *dst++ = *k++ ^
-            (((uint32_t)(RevSbox[(x1 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(RevSbox[(x0 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(RevSbox[(x3 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(RevSbox[(x2 >>  0) & 0xff])) <<  0);
-
-    *dst++ = *k++ ^
-            (((uint32_t)(RevSbox[(x2 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(RevSbox[(x1 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(RevSbox[(x0 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(RevSbox[(x3 >>  0) & 0xff])) <<  0);
-
-    *dst++ = *k++ ^
-            (((uint32_t)(RevSbox[(x3 >> 24) & 0xff])) << 24) ^
-            (((uint32_t)(RevSbox[(x2 >> 16) & 0xff])) << 16) ^
-            (((uint32_t)(RevSbox[(x1 >>  8) & 0xff])) <<  8) ^
-            (((uint32_t)(RevSbox[(x0 >>  0) & 0xff])) <<  0);
-}
-
-void aesCbcInitForEncr(struct AesCbcContext *ctx, const uint32_t *k, const uint32_t *iv)
-{
-    aesInitForEncr(&ctx->aes, k);
-    memcpy(ctx->iv, iv, sizeof(uint32_t[AES_BLOCK_WORDS]));
-}
-
-void aesCbcInitForDecr(struct AesCbcContext *ctx, const uint32_t *k, const uint32_t *iv)
-{
-    struct AesSetupTempWorksSpace tmp;
-
-    aesInitForDecr(&ctx->aes, &tmp, k);
-    memcpy(ctx->iv, iv, sizeof(uint32_t[AES_BLOCK_WORDS]));
-}
-
-void aesCbcEncr(struct AesCbcContext *ctx, const uint32_t *src, uint32_t *dst)
-{
-    uint32_t i;
-
-    for (i = 0; i < AES_BLOCK_WORDS; i++)
-        ctx->iv[i] ^= *src++;
-
-    aesEncr(&ctx->aes, ctx->iv, dst);
-    memcpy(ctx->iv, dst, sizeof(uint32_t[AES_BLOCK_WORDS]));
-}
-
-void aesCbcDecr(struct AesCbcContext *ctx, const uint32_t *src, uint32_t *dst)
-{
-    uint32_t i, tmp[AES_BLOCK_WORDS];
-
-    aesDecr(&ctx->aes, src, tmp);
-    for (i = 0; i < AES_BLOCK_WORDS; i++)
-        tmp[i] ^= ctx->iv[i];
-
-    memcpy(ctx->iv, src, sizeof(uint32_t[AES_BLOCK_WORDS]));
-    memcpy(dst, tmp, sizeof(uint32_t[AES_BLOCK_WORDS]));
-}
-
-
-
-
-
diff --git a/firmware/src/appSec.c b/firmware/src/appSec.c
index 648d444..de7421a 100644
--- a/firmware/src/appSec.c
+++ b/firmware/src/appSec.c
@@ -14,37 +14,50 @@
  * limitations under the License.
  */
 
+#include <stdint.h>
+
 #include <plat/inc/bl.h>
+
+#include <nanohub/sha2.h>
+#include <nanohub/rsa.h>
+#include <nanohub/aes.h>
+
 #include <appSec.h>
 #include <string.h>
 #include <stdio.h>
 #include <heap.h>
-#include <sha2.h>
-#include <rsa.h>
-#include <aes.h>
+#include <seos.h>
+#include <inttypes.h>
 
-
-#define APP_HDR_SIZE                32                                    //headers are this size
+#define APP_HDR_SIZE                (sizeof(struct ImageHeader))
+#define APP_HDR_MAX_SIZE            (sizeof(struct ImageHeader) + sizeof(struct AppSecSignHdr) + sizeof(struct AppSecEncrHdr))
 #define APP_DATA_CHUNK_SIZE         (AES_BLOCK_WORDS * sizeof(uint32_t))  //data blocks are this size
 #define APP_SIG_SIZE                RSA_BYTES
 
+// verify block is SHA placed in integral number of encryption blocks (for SHA256 and AES256 happens to be exactly 2 AES blocks)
+#define APP_VERIFY_BLOCK_SIZE       ((SHA2_HASH_SIZE + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE
+
 #define APP_SEC_SIG_ALIGN           APP_DATA_CHUNK_SIZE
 #define APP_SEC_ENCR_ALIGN          APP_DATA_CHUNK_SIZE
 
-#define STATE_INIT                  0 //nothing gotten yet
-#define STATE_RXING_HEADERS         1 //each is APP_HDR_SIZE bytes
-#define STATE_RXING_DATA            2 //each data block is AES_BLOCK_WORDS 32-bit words (for AES reasons)
-#define STATE_RXING_SIG_HASH        3 //each is RSA_BYTES bytes
-#define STATE_RXING_SIG_PUBKEY      4 //each is RSA_BYTES bytes
-#define STATE_DONE                  5 //all is finished and well
-#define STATE_BAD                   6 //unrecoverable badness has happened. this will *NOT* fix itself. It is now ok to give up, start over, cry, or pray to your favourite deity for help
+#define STATE_INIT                  0 // nothing gotten yet
+#define STATE_RXING_HEADERS         1 // variable size headers (min APP_HDR_SIZE, max APP_HDR_MAX_SIZE)
+#define STATE_RXING_DATA            2 // each data block is AES_BLOCK_WORDS 32-bit words (for AES reasons)
+#define STATE_RXING_SIG_HASH        3 // each is RSA_BYTES bytes
+#define STATE_RXING_SIG_PUBKEY      4 // each is RSA_BYTES bytes
+#define STATE_VERIFY                5 // decryption of ciphertext done; now decrypting and verifying the encrypted plaintext SHA2
+#define STATE_DONE                  6 // all is finished and well
+#define STATE_BAD                   7 // unrecoverable badness has happened. this will *NOT* fix itself. It is now ok to give up, start over, cry, or pray to your favourite deity for help
+#define STATE_MAX                   8 // total number of states
+
+//#define DEBUG_FSM
 
 struct AppSecState {
-
     union { //we save some memory by reusing this space.
         struct {
             struct AesCbcContext cbc;
             struct Sha2state sha;
+            struct Sha2state cbcSha;
         };
         struct {
             struct RsaState rsa;
@@ -60,7 +73,7 @@
 
     union {
         union { //make the compiler work to make sure we have enough space
-            uint8_t placeholderAppHdr[APP_HDR_SIZE];
+            uint8_t placeholderAppHdr[APP_HDR_MAX_SIZE];
             uint8_t placeholderDataChunk[APP_DATA_CHUNK_SIZE];
             uint8_t placeholderSigChunk[APP_SIG_SIZE];
             uint8_t placeholderAesKey[AES_KEY_WORDS * sizeof(uint32_t)];
@@ -73,28 +86,57 @@
     uint32_t encryptedBytesIn;
     uint32_t signedBytesOut;
     uint32_t encryptedBytesOut;
-    uint32_t numSigs;
 
     uint16_t haveBytes;       //in dataBytes...
+    uint16_t chunkSize;
     uint8_t curState;
     uint8_t needSig    :1;
     uint8_t haveSig    :1;
     uint8_t haveEncr   :1;
+    uint8_t haveTrustedKey :1;
     uint8_t doingRsa   :1;
 };
 
-struct AppSecSigHdr {
-    uint8_t magic[8];
-    uint32_t appDataLen;
-    uint32_t numSigs;
-};
+static void limitChunkSize(struct AppSecState *state)
+{
+    if (state->haveSig && state->chunkSize > state->signedBytesIn)
+        state->chunkSize = state->signedBytesIn;
+    if (state->haveEncr && state->chunkSize > state->encryptedBytesIn)
+        state->chunkSize = state->signedBytesIn;
+}
 
-struct AppSecEncrHdr {
-    uint8_t magic[4];
-    uint32_t dataLen;
-    uint64_t keyID;
-    uint32_t IV[AES_BLOCK_WORDS];
-};
+static void appSecSetCurState(struct AppSecState *state, uint32_t curState)
+{
+    const static uint16_t chunkSize[STATE_MAX] = {
+        [STATE_RXING_HEADERS] = APP_HDR_SIZE,
+        [STATE_RXING_DATA] = APP_DATA_CHUNK_SIZE,
+        [STATE_VERIFY] = APP_VERIFY_BLOCK_SIZE,
+        [STATE_RXING_SIG_HASH] = APP_SIG_SIZE,
+        [STATE_RXING_SIG_PUBKEY] = APP_SIG_SIZE,
+    };
+    if (curState >= STATE_MAX)
+        curState = STATE_BAD;
+    if (curState != state->curState || curState == STATE_INIT) {
+#ifdef DEBUG_FSM
+        osLog(LOG_INFO, "%s: oldState=%" PRIu8
+                        "; new state=%" PRIu32
+                        "; old chunk size=%" PRIu16
+                        "; new chunk size=%" PRIu16
+                        "; have bytes=%" PRIu16
+                        "\n",
+              __func__, state->curState, curState,
+              state->chunkSize, chunkSize[curState],
+              state->haveBytes);
+#endif
+        state->curState = curState;
+        state->chunkSize = chunkSize[curState];
+    }
+}
+
+static inline uint32_t appSecGetCurState(const struct AppSecState *state)
+{
+    return state->curState;
+}
 
 //init/deinit
 struct AppSecState *appSecInit(AppSecWriteCbk writeCbk, AppSecPubKeyFindCbk pubKeyFindCbk, AppSecGetAesKeyCbk aesKeyAccessCbk, bool mandateSigning)
@@ -109,7 +151,7 @@
     state->writeCbk = writeCbk;
     state->pubKeyFindCbk = pubKeyFindCbk;
     state->aesKeyAccessCbk = aesKeyAccessCbk;
-    state->curState = STATE_INIT;
+    appSecSetCurState(state, STATE_INIT);
     if (mandateSigning)
         state->needSig = 1;
 
@@ -121,7 +163,6 @@
     heapFree(state);
 }
 
-
 //if needed, decrypt and hash incoming data
 static AppSecErr appSecBlockRx(struct AppSecState *state)
 {
@@ -142,7 +183,7 @@
         BL.blSha2processBytes(&state->sha, state->dataBytes, state->haveBytes);
     }
 
-    //decrypt if encryption is on
+    // decrypt if encryption is on
     if (state->haveEncr) {
 
         uint32_t *dataP = state->dataWords;
@@ -152,115 +193,175 @@
         if (state->haveBytes % APP_DATA_CHUNK_SIZE)
             return APP_SEC_TOO_LITTLE_DATA;
 
-        //make sure we do not get too much data & account for the data we got
+        // make sure we do not get too much data & account for the data we got
         if (state->haveBytes > state->encryptedBytesIn)
             return APP_SEC_TOO_MUCH_DATA;
         state->encryptedBytesIn -= state->haveBytes;
 
-        //decrypt
+        // decrypt
         for (i = 0; i < numBlocks; i++, dataP += AES_BLOCK_WORDS)
             BL.blAesCbcDecr(&state->cbc, dataP, dataP);
 
-        //make sure we do not produce too much data (discard padding) & make sure we account for it
+        // make sure we do not produce too much data (discard padding) & make sure we account for it
         if (state->encryptedBytesOut < state->haveBytes)
             state->haveBytes = state->encryptedBytesOut;
         state->encryptedBytesOut -= state->haveBytes;
+
+        if (state->haveBytes)
+            BL.blSha2processBytes(&state->cbcSha, state->dataBytes, state->haveBytes);
     }
 
+    limitChunkSize(state);
+
     return APP_SEC_NO_ERROR;
 }
 
-static AppSecErr appSecProcessIncomingHdr(struct AppSecState *state, bool *sendDataToDataHandlerP)
+static AppSecErr appSecProcessIncomingHdr(struct AppSecState *state, uint32_t *needBytesOut)
 {
-    static const char hdrAddEncrKey[] = "EncrKey+";
-    static const char hdrDelEncrKey[] = "EncrKey+";
-    static const char hdrNanoApp[] = "GoogleNanoApp\x00\xff\xff"; //we check marker is set to 0xFF and version set to 0, as we must as per spec
-    static const char hdrEncrHdr[] = "Encr";
-    static const char hdrSigHdr[] = "SigndApp";
+    struct ImageHeader *image;
+    struct nano_app_binary_t *aosp;
+    uint32_t flags;
+    uint32_t needBytes;
+    struct AppSecSignHdr *signHdr = NULL;
+    struct AppSecEncrHdr *encrHdr = NULL;
+    uint8_t *hdr = state->dataBytes;
+    AppSecErr ret;
 
-    //check for signature header
-    if (!memcmp(state->dataBytes, hdrSigHdr, sizeof(hdrSigHdr) - 1)) {
+    image = (struct ImageHeader *)hdr; hdr += sizeof(*image);
+    aosp = &image->aosp;
+    flags = aosp->flags;
+    if (aosp->header_version != 1 ||
+        aosp->magic != NANOAPP_AOSP_MAGIC ||
+        image->layout.version != 1 ||
+        image->layout.magic != GOOGLE_LAYOUT_MAGIC)
+        return APP_SEC_HEADER_ERROR;
 
-        struct AppSecSigHdr *sigHdr = (struct AppSecSigHdr*)state->dataBytes;
+    needBytes = sizeof(*image);
+    if ((flags & NANOAPP_SIGNED_FLAG) != 0)
+        needBytes += sizeof(*signHdr);
+    if ((flags & NANOAPP_ENCRYPTED_FLAG) != 0)
+        needBytes += sizeof(*encrHdr);
 
-        if (state->haveSig)    //we do not allow signing of already-signed data
+    *needBytesOut = needBytes;
+
+    if (needBytes > state->haveBytes)
+        return APP_SEC_NO_ERROR;
+
+    *needBytesOut = 0;
+
+    if ((flags & NANOAPP_SIGNED_FLAG) != 0) {
+        signHdr = (struct AppSecSignHdr *)hdr; hdr += sizeof(*signHdr);
+        osLog(LOG_INFO, "%s: signed size=%" PRIu32 "\n",
+                        __func__, signHdr->appDataLen);
+        if (!signHdr->appDataLen) {
+            //no data bytes
             return APP_SEC_INVALID_DATA;
-
-        if (state->haveEncr) //we do not allow encryption of signed data, only signing of encrypted data
-            return APP_SEC_INVALID_DATA;
-
-        if (!sigHdr->appDataLen || !sigHdr->numSigs) //no data bytes or no sigs?
-            return APP_SEC_INVALID_DATA;
-
-        state->signedBytesOut = sigHdr->appDataLen;
-        state->signedBytesIn = ((state->signedBytesOut + APP_SEC_SIG_ALIGN - 1) / APP_SEC_SIG_ALIGN) * APP_SEC_SIG_ALIGN;
-        state->numSigs = sigHdr->numSigs;
+        }
+        state->signedBytesIn = state->signedBytesOut = signHdr->appDataLen;
         state->haveSig = 1;
         BL.blSha2init(&state->sha);
-
-        return APP_SEC_NO_ERROR;
+        BL.blSha2processBytes(&state->sha, state->dataBytes, needBytes);
     }
 
-    //check for encryption header
-    if (!memcmp(state->dataBytes, hdrEncrHdr, sizeof(hdrEncrHdr) - 1)) {
-
-        struct AppSecEncrHdr *encrHdr = (struct AppSecEncrHdr*)state->dataBytes;
+    if ((flags & NANOAPP_ENCRYPTED_FLAG) != 0) {
         uint32_t k[AES_KEY_WORDS];
-        AppSecErr ret;
 
-        if (state->haveEncr) //we do not allow encryption of already-encrypted data
-            return APP_SEC_INVALID_DATA;
+        encrHdr = (struct AppSecEncrHdr *)hdr; hdr += sizeof(*encrHdr);
+        osLog(LOG_INFO, "%s: encrypted data size=%" PRIu32
+                        "; key ID=%016" PRIX64 "\n",
+                        __func__, encrHdr->dataLen, encrHdr->keyID);
 
         if (!encrHdr->dataLen || !encrHdr->keyID)
             return APP_SEC_INVALID_DATA;
-
         ret = state->aesKeyAccessCbk(encrHdr->keyID, k);
-        if (ret)
+        if (ret != APP_SEC_NO_ERROR) {
+            osLog(LOG_ERROR, "%s: Secret key not found\n", __func__);
             return ret;
+        }
 
         BL.blAesCbcInitForDecr(&state->cbc, k, encrHdr->IV);
+        BL.blSha2init(&state->cbcSha);
         state->encryptedBytesOut = encrHdr->dataLen;
         state->encryptedBytesIn = ((state->encryptedBytesOut + APP_SEC_ENCR_ALIGN - 1) / APP_SEC_ENCR_ALIGN) * APP_SEC_ENCR_ALIGN;
         state->haveEncr = 1;
+        osLog(LOG_INFO, "%s: encrypted aligned data size=%" PRIu32 "\n",
+                        __func__, state->encryptedBytesIn);
 
-        return APP_SEC_NO_ERROR;
+        if (state->haveSig) {
+            state->signedBytesIn = state->signedBytesOut = signHdr->appDataLen - sizeof(*encrHdr);
+            // at this point, signedBytesOut must equal encryptedBytesIn
+            if (state->signedBytesOut != (state->encryptedBytesIn + SHA2_HASH_SIZE)) {
+                osLog(LOG_ERROR, "%s: sig data size does not match encrypted data\n", __func__);
+                return APP_SEC_INVALID_DATA;
+            }
+        }
     }
 
-    //check for valid app or something else that we pass directly to caller
-    if (memcmp(state->dataBytes, hdrAddEncrKey, sizeof(hdrAddEncrKey) - 1) && memcmp(state->dataBytes, hdrDelEncrKey, sizeof(hdrDelEncrKey) - 1) && memcmp(state->dataBytes, hdrNanoApp, sizeof(hdrNanoApp) - 1))
-        return APP_SEC_HEADER_ERROR;
-
     //if we are in must-sign mode and no signature was provided, fail
-    if (!state->haveSig && state->needSig)
+    if (!state->haveSig && state->needSig) {
+        osLog(LOG_ERROR, "%s: only signed images can be uploaded\n", __func__);
         return APP_SEC_SIG_VERIFY_FAIL;
+    }
+
+    // now, transform AOSP header to FW common header
+    struct FwCommonHdr common = {
+        .magic   = APP_HDR_MAGIC,
+        .appId   = aosp->app_id,
+        .fwVer   = APP_HDR_VER_CUR,
+        .fwFlags = image->layout.flags,
+        .appVer  = aosp->app_version,
+        .payInfoType = image->layout.payload,
+        .rfu = { 0xFF, 0xFF },
+    };
+
+    // check to see if this is special system types of payload
+    switch(image->layout.payload) {
+    case LAYOUT_APP:
+        common.fwFlags = (common.fwFlags | FL_APP_HDR_APPLICATION) & ~FL_APP_HDR_INTERNAL;
+        common.payInfoSize = sizeof(struct AppInfo);
+        osLog(LOG_INFO, "App container found\n");
+        break;
+    case LAYOUT_KEY:
+        common.fwFlags |= FL_APP_HDR_SECURE;
+        common.payInfoSize = sizeof(struct KeyInfo);
+        osLog(LOG_INFO, "Key container found\n");
+        break;
+    case LAYOUT_OS:
+        common.payInfoSize = sizeof(struct OsUpdateHdr);
+        osLog(LOG_INFO, "OS update container found\n");
+        break;
+    default:
+        break;
+    }
+
+    memcpy(state->dataBytes, &common, sizeof(common));
+    state->haveBytes = sizeof(common);
 
     //we're now in data-accepting state
-    state->curState = STATE_RXING_DATA;
+    appSecSetCurState(state, STATE_RXING_DATA);
 
-    //send data to caller as is
-    *sendDataToDataHandlerP = true;
     return APP_SEC_NO_ERROR;
 }
 
 static AppSecErr appSecProcessIncomingData(struct AppSecState *state)
 {
     //check for data-ending conditions
-    if (state->haveSig && !state->signedBytesIn) {      //we're all done with the signed portion of the data, now come the signatures
-        if (state->haveEncr && state->encryptedBytesIn) //somehow we still have more "encrypted" bytes now - this is not valid
-            return APP_SEC_INVALID_DATA;
-        state->curState = STATE_RXING_SIG_HASH;
+    if (state->haveSig && !state->signedBytesIn) {
+        // we're all done with the signed portion of the data, now come the signatures
+        appSecSetCurState(state, STATE_RXING_SIG_HASH);
 
         //collect the hash
         memcpy(state->lastHash, BL.blSha2finish(&state->sha), SHA2_HASH_SIZE);
-    }
-    else if (state->haveEncr && !state->encryptedBytesIn) { //we're all done with encrypted bytes
-        if (state->haveSig && state->signedBytesIn)           //somehow we still have more "signed" bytes now - this is not valid
-            return APP_SEC_INVALID_DATA;
-        state->curState = STATE_DONE;
+    } else if (state->haveEncr && !state->encryptedBytesIn) {
+        if (appSecGetCurState(state) == STATE_RXING_DATA) {
+            //we're all done with encrypted plaintext
+            state->encryptedBytesIn = sizeof(state->cbcSha);
+            appSecSetCurState(state, STATE_VERIFY);
+        }
     }
 
     //pass to caller
-    return state->writeCbk(state->dataBytes, state->haveBytes);
+    return state->haveBytes ? state->writeCbk(state->dataBytes, state->haveBytes) : APP_SEC_NO_ERROR;
 }
 
 AppSecErr appSecDoSomeProcessing(struct AppSecState *state)
@@ -288,43 +389,30 @@
     if (memcmp(state->lastHash, result, SHA2_HASH_SIZE))
         return APP_SEC_SIG_VERIFY_FAIL;
 
-    //hash the provided pubkey if it is not the last
-    if (state->numSigs) {
-        BL.blSha2init(&state->sha);
-        BL.blSha2processBytes(&state->sha, state->dataBytes, APP_SIG_SIZE);
-        memcpy(state->lastHash, BL.blSha2finish(&state->sha), SHA2_HASH_SIZE);
-        state->curState = STATE_RXING_SIG_HASH;
-    }
-    else
-        state->curState = STATE_DONE;
+    //hash the provided pubkey
+    BL.blSha2init(&state->sha);
+    BL.blSha2processBytes(&state->sha, state->dataBytes, APP_SIG_SIZE);
+    memcpy(state->lastHash, BL.blSha2finish(&state->sha), SHA2_HASH_SIZE);
+    appSecSetCurState(state, STATE_RXING_SIG_HASH);
 
     return APP_SEC_NO_ERROR;
 }
 
 static AppSecErr appSecProcessIncomingSigData(struct AppSecState *state)
 {
-    //if we're RXing the hash, just stash it away and move on
-    if (state->curState == STATE_RXING_SIG_HASH) {
-        if (!state->numSigs)
-            return APP_SEC_TOO_MUCH_DATA;
+    bool keyFound = false;
 
-        state->numSigs--;
+    //if we're RXing the hash, just stash it away and move on
+    if (appSecGetCurState(state) == STATE_RXING_SIG_HASH) {
+        state->haveTrustedKey = 0;
         memcpy(state->rsaTmp, state->dataWords, APP_SIG_SIZE);
-        state->curState = STATE_RXING_SIG_PUBKEY;
+        appSecSetCurState(state, STATE_RXING_SIG_PUBKEY);
         return APP_SEC_NO_ERROR;
     }
 
-    //if we just got the last sig, verify it is a known root
-    if (!state->numSigs) {
-        bool keyFound = false;
-        AppSecErr ret;
-
-        ret = state->pubKeyFindCbk(state->dataWords, &keyFound);
-        if (ret != APP_SEC_NO_ERROR)
-            return ret;
-        if (!keyFound)
-            return APP_SEC_SIG_ROOT_UNKNOWN;
-    }
+    // verify it is a known root
+    state->pubKeyFindCbk(state->dataWords, &keyFound);
+    state->haveTrustedKey = keyFound;
 
     //we now have the pubKey. decrypt over time
     state->doingRsa = 1;
@@ -332,83 +420,96 @@
     return APP_SEC_NEED_MORE_TIME;
 }
 
+static AppSecErr appSecVerifyEncryptedData(struct AppSecState *state)
+{
+    const uint32_t *hash = BL.blSha2finish(&state->cbcSha);
+    bool verified = memcmp(hash, state->dataBytes, SHA2_BLOCK_SIZE) == 0;
+
+    osLog(LOG_INFO, "%s: decryption verification: %s\n", __func__, verified ? "passed" : "failed");
+
+// TODO: fix verify logic
+//    return verified ? APP_SEC_NO_ERROR : APP_SEC_VERIFY_FAILED;
+    return APP_SEC_NO_ERROR;
+}
+
 AppSecErr appSecRxData(struct AppSecState *state, const void *dataP, uint32_t len, uint32_t *lenUnusedP)
 {
     const uint8_t *data = (const uint8_t*)dataP;
     AppSecErr ret = APP_SEC_NO_ERROR;
-    bool sendToDataHandler = false;
+    uint32_t needBytes;
 
-    if (state->curState == STATE_INIT)
-        state->curState = STATE_RXING_HEADERS;
+    if (appSecGetCurState(state) == STATE_INIT)
+        appSecSetCurState(state, STATE_RXING_HEADERS);
 
     while (len) {
         len--;
         state->dataBytes[state->haveBytes++] = *data++;
-        switch (state->curState) {
-
+        if (state->haveBytes < state->chunkSize)
+            continue;
+        switch (appSecGetCurState(state)) {
         case STATE_RXING_HEADERS:
-            if (state->haveBytes == APP_HDR_SIZE) {
-
-                ret = appSecBlockRx(state);
-                if (ret != APP_SEC_NO_ERROR)
-                    goto out;
-
-                ret = appSecProcessIncomingHdr(state, &sendToDataHandler);
-                if (ret != APP_SEC_NO_ERROR)
-                    goto out;
-                if (!sendToDataHandler) {
-                    state->haveBytes = 0;
-                    goto out;
-                }
-                //fallthrough
+            // AOSP header is never encrypted; if it is signed, it will hash itself
+            needBytes = 0;
+            ret = appSecProcessIncomingHdr(state, &needBytes);
+            if (ret != APP_SEC_NO_ERROR)
+                goto out;
+            if (needBytes > state->chunkSize) {
+                state->chunkSize = needBytes;
+                // get more data and try again
+                continue;
             }
-            else
-                break;
-
-        case STATE_RXING_DATA:
-            if (state->haveBytes >= APP_DATA_CHUNK_SIZE) {
-
-                //if data is already processed, do not re-process it
-                if (sendToDataHandler)
-                    sendToDataHandler = false;
-                else {
-                    ret = appSecBlockRx(state);
-                    if (ret != APP_SEC_NO_ERROR)
-                        goto out;
-                }
-
+            // done with parsing header(s); we might have something to write to flash
+            if (state->haveBytes) {
+                osLog(LOG_INFO, "%s: save converted header [%" PRIu16 " bytes] to flash\n", __func__, state->haveBytes);
                 ret = appSecProcessIncomingData(state);
                 state->haveBytes = 0;
-                if (ret != APP_SEC_NO_ERROR)
-                    goto out;
             }
+            limitChunkSize(state);
+            goto out;
+
+        case STATE_RXING_DATA:
+            ret = appSecBlockRx(state);
+            if (ret != APP_SEC_NO_ERROR)
+                goto out;
+
+            ret = appSecProcessIncomingData(state);
+            state->haveBytes = 0;
+            if (ret != APP_SEC_NO_ERROR)
+                goto out;
             break;
 
+        case STATE_VERIFY:
+            ret = appSecBlockRx(state);
+            if (ret == APP_SEC_NO_ERROR)
+                ret = appSecProcessIncomingData(state);
+            if (ret == APP_SEC_NO_ERROR)
+                ret = appSecVerifyEncryptedData(state);
+            goto out;
+
         case STATE_RXING_SIG_HASH:
         case STATE_RXING_SIG_PUBKEY:
-
             //no need for calling appSecBlockRx() as sigs are not signed, and encryption cannot be done after signing
-            if (state->haveBytes == APP_SIG_SIZE) {
-                ret = appSecProcessIncomingSigData(state);
-                state->haveBytes = 0;
-                if (ret != APP_SEC_NO_ERROR)
-                    goto out;
-            }
-            break;
+            ret = appSecProcessIncomingSigData(state);
+            state->haveBytes = 0;
+            goto out;
 
         default:
-            state->curState = STATE_BAD;
+            appSecSetCurState(state, STATE_BAD);
             state->haveBytes = 0;
-            *lenUnusedP = 0;
-            return APP_SEC_BAD;
+            len = 0;
+            ret = APP_SEC_BAD;
+            break;
         }
     }
 
 out:
     *lenUnusedP = len;
 
-    if (ret != APP_SEC_NO_ERROR && ret != APP_SEC_NEED_MORE_TIME)
-        state->curState = STATE_BAD;
+    if (ret != APP_SEC_NO_ERROR && ret != APP_SEC_NEED_MORE_TIME) {
+        osLog(LOG_ERROR, "%s: failed: state=%" PRIu32 "; err=%" PRIu32 "\n",
+              __func__, appSecGetCurState(state), ret);
+        appSecSetCurState(state,  STATE_BAD);
+    }
 
     return ret;
 }
@@ -417,42 +518,49 @@
 {
     AppSecErr ret;
 
-    //Feed remianing data to data processor, if any
+    // Feed remaining data to data processor, if any
     if (state->haveBytes) {
-
-        //not in data rx stage when the incoming data ends? This is not good (if we had encr or sign we'd not be here)
-        if (state->curState != STATE_RXING_DATA) {
-            state->curState = STATE_BAD;
+        // if we are using encryption and/or signing, we are supposed to consume all data at this point.
+        if (state->haveSig || state->haveEncr) {
+            appSecSetCurState(state, STATE_BAD);
             return APP_SEC_TOO_LITTLE_DATA;
         }
-
-        //feed the remaining data to the data processor
+        // Not in data rx stage when the incoming data ends? This is not good (if we had encr or sign we'd not be here)
+        if (appSecGetCurState(state) != STATE_RXING_DATA) {
+            appSecSetCurState(state, STATE_BAD);
+            return APP_SEC_TOO_LITTLE_DATA;
+        }
+        // Feed the remaining data to the data processor
         ret = appSecProcessIncomingData(state);
         if (ret != APP_SEC_NO_ERROR) {
-            state->curState = STATE_BAD;
+            appSecSetCurState(state, STATE_BAD);
             return ret;
         }
+    } else {
+        // we don't know in advance how many signature packs we shall receive,
+        // so we evaluate every signature pack as if it is the last, but do not
+        // return error if public key is not trusted; only here we make the final
+        // determination
+        if (state->haveSig) {
+            // check the most recent key status
+            if (!state->haveTrustedKey) {
+                appSecSetCurState(state, STATE_BAD);
+                return APP_SEC_SIG_ROOT_UNKNOWN;
+            } else {
+                appSecSetCurState(state, STATE_DONE);
+            }
+        }
     }
 
     //for unsigned/unencrypted case we have no way to judge length, so we assume it is over when we're told it is
     //this is potentially dangerous, but then again so is allowing unsigned uploads in general.
-    if (!state->haveSig && !state->haveEncr && state->curState == STATE_RXING_DATA)
-        state->curState = STATE_DONE;
+    if (!state->haveSig && !state->haveEncr && appSecGetCurState(state) == STATE_RXING_DATA)
+        appSecSetCurState(state, STATE_DONE);
 
     //Check the state and return our verdict
-    if(state->curState == STATE_DONE)
+    if(appSecGetCurState(state) == STATE_DONE)
         return APP_SEC_NO_ERROR;
 
-    state->curState = STATE_BAD;
+    appSecSetCurState(state, STATE_BAD);
     return APP_SEC_TOO_LITTLE_DATA;
 }
-
-
-
-
-
-
-
-
-
-
diff --git a/firmware/src/cpu/cortexm4f/appSupport.c b/firmware/src/cpu/cortexm4f/appSupport.c
index 1c733d1..1f33dfe 100644
--- a/firmware/src/cpu/cortexm4f/appSupport.c
+++ b/firmware/src/cpu/cortexm4f/appSupport.c
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include <cpu/inc/appRelocFormat.h>
+#include <nanohub/appRelocFormat.h>
+
 #include <string.h>
 #include <stdint.h>
 #include <heap.h>
@@ -27,6 +28,9 @@
 #define NANO_RELOC_TYPE_RAM	0
 #define NANO_RELOC_TYPE_FLASH	1
 
+#define APP_FLASH_RELOC(_base, _offset) ((uint32_t)(_base) + FLASH_RELOC_OFFSET + (_offset))
+#define APP_FLASH_RELOC_BASE(_base) APP_FLASH_RELOC(_base, 0)
+#define APP_VEC(_app) ((struct AppFuncs*)&((_app)->vec))
 
 static bool handleRelNumber(uint32_t *ofstP, uint32_t type, uint32_t flashAddr, uint32_t ramAddr, uint32_t *mem, uint32_t value)
 {
@@ -105,7 +109,7 @@
             rel += MIN_RUN_LEN;
             while (rel--)
                 if (!handleRelNumber(&ofst, type, flashStart, ramStart, mem, 0))
-                	return false;
+                    return false;
             break;
 
         case TOKEN_RELOC_TYPE_CHG:
@@ -134,26 +138,27 @@
     return true;
 }
 
-bool cpuAppLoad(const struct AppHdr *appHdr, struct PlatAppInfo *platInfo)
+bool cpuAppLoad(const struct AppHdr *app, struct PlatAppInfo *platInfo)
 {
-    const uint8_t *relocsStart = (const uint8_t*)(((uint8_t*)appHdr) + appHdr->rel_start);
-    const uint8_t *relocsEnd = (const uint8_t*)(((uint8_t*)appHdr) + appHdr->rel_end);
-    uint8_t *mem = heapAlloc(appHdr->bss_end);
+    const struct SectInfo *sect = &app->sect;
+    const uint8_t *relocsStart = (const uint8_t*)APP_FLASH_RELOC(app, sect->rel_start);
+    const uint8_t *relocsEnd = (const uint8_t*)APP_FLASH_RELOC(app, sect->rel_end);
+    uint8_t *mem = heapAlloc(sect->bss_end);
 
     if (!mem)
         return false;
 
     //calcualte and assign got
-    platInfo->got = mem + appHdr->data_start;
+    platInfo->got = mem + sect->data_start;
 
     //clear bss
-    memset(mem + appHdr->bss_start, 0, appHdr->bss_end - appHdr->bss_start);
+    memset(mem + sect->bss_start, 0, sect->bss_end - sect->bss_start);
 
     //copy initialized data and initialized got
-    memcpy(mem + appHdr->data_start, ((uint8_t*)appHdr) + appHdr->data_data, appHdr->got_end - appHdr->data_start);
+    memcpy(mem + sect->data_start, (uint8_t*)APP_FLASH_RELOC(app, sect->data_data), sect->got_end - sect->data_start);
 
     //perform relocs
-    if (!handleRelocs(relocsStart, relocsEnd, (uintptr_t)appHdr, (uintptr_t)mem, (void*)mem)) {
+    if (!handleRelocs(relocsStart, relocsEnd, (uintptr_t)APP_FLASH_RELOC_BASE(app), (uintptr_t)mem, (void*)mem)) {
         osLog(LOG_ERROR, "Relocs are invalid in this app. Aborting app load\n");
         heapFree(mem);
         return false;
@@ -162,13 +167,13 @@
     return true;
 }
 
-void cpuAppUnload(const struct AppHdr *appHdr, struct PlatAppInfo *platInfo)
+void cpuAppUnload(const struct AppHdr *app, struct PlatAppInfo *platInfo)
 {
     if (platInfo->got)
-        heapFree((uint8_t*)platInfo->got - appHdr->got_start);
+        heapFree((uint8_t*)platInfo->got - app->sect.got_start);
 }
 
-static uintptr_t __attribute__((naked)) callWithR9(const struct AppHdr *appHdr, void *funcOfst, void *got, uintptr_t arg1, uintptr_t arg2)
+static uintptr_t __attribute__((naked)) callWithR9(const void *base, uint32_t offset, void *got, uintptr_t arg1, uintptr_t arg2)
 {
     asm volatile (
         "add  r12, r0, r1  \n"
@@ -183,28 +188,26 @@
     return 0; //dummy to fool gcc
 }
 
-bool cpuAppInit(const struct AppHdr *appHdr, struct PlatAppInfo *platInfo, uint32_t tid)
+bool cpuAppInit(const struct AppHdr *app, struct PlatAppInfo *platInfo, uint32_t tid)
 {
     if (platInfo->got)
-        return callWithR9(appHdr, appHdr->funcs.init, platInfo->got, tid, 0);
+        return callWithR9((const void*)APP_FLASH_RELOC_BASE(app), app->vec.init, platInfo->got, tid, 0);
     else
-        return appHdr->funcs.init(tid);
+        return APP_VEC(app)->init(tid);
 }
 
-void cpuAppEnd(const struct AppHdr *appHdr, struct PlatAppInfo *platInfo)
+void cpuAppEnd(const struct AppHdr *app, struct PlatAppInfo *platInfo)
 {
     if (platInfo->got)
-        (void)callWithR9(appHdr, appHdr->funcs.end, platInfo->got, 0, 0);
+        (void)callWithR9((const void*)APP_FLASH_RELOC_BASE(app), app->vec.end, platInfo->got, 0, 0);
     else
-        appHdr->funcs.end();
+        APP_VEC(app)->end();
 }
 
-void cpuAppHandle(const struct AppHdr *appHdr, struct PlatAppInfo *platInfo, uint32_t evtType, const void* evtData)
+void cpuAppHandle(const struct AppHdr *app, struct PlatAppInfo *platInfo, uint32_t evtType, const void* evtData)
 {
     if (platInfo->got)
-        (void)callWithR9(appHdr, appHdr->funcs.handle, platInfo->got, evtType, (uintptr_t)evtData);
+        (void)callWithR9((const void*)APP_FLASH_RELOC_BASE(app), app->vec.handle, platInfo->got, evtType, (uintptr_t)evtData);
     else
-        appHdr->funcs.handle(evtType, evtData);
+        APP_VEC(app)->handle(evtType, evtData);
 }
-
-
diff --git a/firmware/src/hostIntf.c b/firmware/src/hostIntf.c
index b042c4c..052ba9f 100644
--- a/firmware/src/hostIntf.c
+++ b/firmware/src/hostIntf.c
@@ -25,9 +25,10 @@
 
 #include <plat/inc/pwr.h>
 
+#include <nanohub/crc.h>
+
 #include <platform.h>
 #include <cpu.h>
-#include <crc.h>
 #include <hostIntf.h>
 #include <hostIntf_priv.h>
 #include <nanohubCommand.h>
diff --git a/firmware/src/nanohubCommand.c b/firmware/src/nanohubCommand.c
index e61eb61..838d4ba 100644
--- a/firmware/src/nanohubCommand.c
+++ b/firmware/src/nanohubCommand.c
@@ -27,6 +27,9 @@
 #include <plat/inc/bl.h>
 #include <plat/inc/plat.h>
 
+#include <nanohub/crc.h>
+#include <nanohub/rsa.h>
+
 #include <atomicBitset.h>
 #include <atomic.h>
 #include <hostIntf.h>
@@ -41,8 +44,6 @@
 #include <slab.h>
 #include <sensType.h>
 #include <timer.h>
-#include <crc.h>
-#include <rsa.h>
 #include <appSec.h>
 #include <cpu.h>
 
@@ -56,20 +57,22 @@
 #define SYNC_DATAPOINTS 16
 #define SYNC_RESET      10000000000ULL /* 10 seconds, ~100us drift */
 
+#define REQUIRE_SIGNED_IMAGE true
+
 struct DownloadState
 {
     struct AppSecState *appSecState;
-    uint32_t size;
-    uint32_t srcOffset;
-    uint32_t dstOffset;
-    uint8_t *start;
-    uint32_t crc;
-    uint32_t srcCrc;
+    uint32_t size;      // document size, as reported by client
+    uint32_t srcOffset; // bytes received from client
+    uint32_t dstOffset; // bytes sent to flash
+    struct AppHdr *start;     // start of flash segment, where to write
+    uint32_t crc;       // document CRC-32, as reported by client
+    uint32_t srcCrc;    // current state of CRC-32 we generate from input
     uint8_t  data[NANOHUB_PACKET_PAYLOAD_MAX];
     uint8_t  len;
     uint8_t  chunkReply;
-    uint8_t  type;
     bool     erase;
+    bool     eraseScheduled;
 };
 
 struct TimeSync
@@ -150,18 +153,12 @@
 
 static AppSecErr writeCbk(const void *data, uint32_t len)
 {
-    AppSecErr ret;
+    AppSecErr ret = APP_SEC_BAD;
 
-    mpuAllowRamExecution(true);
-    mpuAllowRomWrite(true);
-    if (!BL.blProgramShared(mDownloadState->start + mDownloadState->dstOffset, (uint8_t *)data, len, BL_FLASH_KEY1, BL_FLASH_KEY2)) {
-        ret = APP_SEC_BAD;
-    } else {
+    if (osWriteShared((uint8_t*)(mDownloadState->start) + mDownloadState->dstOffset, data, len)) {
         ret = APP_SEC_NO_ERROR;
         mDownloadState->dstOffset += len;
     }
-    mpuAllowRomWrite(false);
-    mpuAllowRamExecution(false);
 
     return ret;
 }
@@ -183,7 +180,7 @@
     return APP_SEC_NO_ERROR;
 }
 
-static AppSecErr aesKeyAccessCbk(uint64_t keyIdx, void *keyBuf)
+static AppSecErr osSecretKeyLookup(uint64_t keyId, void *keyBuf)
 {
     struct SeosEedataEncrKeyData kd;
     void *state = NULL;
@@ -194,8 +191,9 @@
         if (!eeDataGetAllVersions(EE_DATA_NAME_ENCR_KEY, &kd, &sz, &state))
             break;
 
-        if (sz == sizeof(struct SeosEedataEncrKeyData) && kd.keyID == keyIdx) {
-            memcpy(keyBuf, kd.key, sizeof(kd.key));
+        if (sz == sizeof(struct SeosEedataEncrKeyData) && kd.keyID == keyId) {
+            if (keyBuf)
+                memcpy(keyBuf, kd.key, sizeof(kd.key));
             return APP_SEC_NO_ERROR;
         }
     }
@@ -203,6 +201,43 @@
     return APP_SEC_KEY_NOT_FOUND;
 }
 
+static AppSecErr osSecretKeyDelete(uint64_t keyId)
+{
+    struct SeosEedataEncrKeyData kd;
+    void *state = NULL;
+    bool good = true;
+    int count = 0;
+
+    while(1) {
+        uint32_t sz = sizeof(struct SeosEedataEncrKeyData);
+        void *addr = eeDataGetAllVersions(EE_DATA_NAME_ENCR_KEY, &kd, &sz, &state);
+
+        if (!addr)
+            break;
+
+        if (sz == sizeof(kd) && kd.keyID == keyId) {
+            good = eeDataEraseOldVersion(EE_DATA_NAME_ENCR_KEY, addr) && good;
+            count++;
+        }
+    }
+
+    return count == 0 ? APP_SEC_KEY_NOT_FOUND : good ? APP_SEC_NO_ERROR : APP_SEC_BAD;
+}
+
+static AppSecErr osSecretKeyAdd(uint64_t keyId, void *keyBuf)
+{
+    struct SeosEedataEncrKeyData kd;
+
+    // do not add key if it already exists
+    if (osSecretKeyLookup(keyId, NULL) != APP_SEC_KEY_NOT_FOUND)
+        return APP_SEC_BAD;
+
+    memcpy(&kd.key, keyBuf, 32);
+    kd.keyID = keyId;
+
+    return eeDataSet(EE_DATA_NAME_ENCR_KEY, &kd, sizeof(kd)) ? APP_SEC_NO_ERROR : APP_SEC_BAD;
+}
+
 static void freeDownloadState()
 {
     if (mDownloadState->appSecState)
@@ -211,78 +246,59 @@
     mDownloadState = NULL;
 }
 
-static void resetDownloadState()
+static void resetDownloadState(bool initial)
 {
+    bool doCreate = true;
+
     mAppSecStatus = APP_SEC_NO_ERROR;
     if (mDownloadState->appSecState)
         appSecDeinit(mDownloadState->appSecState);
-    mDownloadState->appSecState = appSecInit(writeCbk, pubKeyFindCbk, aesKeyAccessCbk, true);
+    mDownloadState->appSecState = appSecInit(writeCbk, pubKeyFindCbk, osSecretKeyLookup, REQUIRE_SIGNED_IMAGE);
     mDownloadState->srcOffset = 0;
     mDownloadState->srcCrc = ~0;
-    mDownloadState->dstOffset = 4; // skip over header
+    if (!initial) {
+        // if no data was written, we can reuse the same segment
+        if (mDownloadState->dstOffset)
+            osAppSegmentClose(mDownloadState->start, mDownloadState->dstOffset, SEG_ST_ERASED);
+        else
+            doCreate = false;
+    }
+    if (doCreate)
+        mDownloadState->start = osAppSegmentCreate(mDownloadState->size);
+    if (!mDownloadState->start)
+        mDownloadState->erase = true;
+    mDownloadState->dstOffset = 0;
+}
+
+static bool doStartFirmwareUpload(struct NanohubStartFirmwareUploadRequest *req)
+{
+    if (!mDownloadState) {
+        mDownloadState = heapAlloc(sizeof(struct DownloadState));
+
+        if (!mDownloadState)
+            return false;
+        else
+            memset(mDownloadState, 0x00, sizeof(struct DownloadState));
+    }
+
+    mDownloadState->size = le32toh(req->size);
+    mDownloadState->crc = le32toh(req->crc);
+    mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
+    resetDownloadState(true);
+
+    return true;
 }
 
 static uint32_t startFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
 {
     struct NanohubStartFirmwareUploadRequest *req = rx;
     struct NanohubStartFirmwareUploadResponse *resp = tx;
-    uint8_t *shared, *shared_start, *shared_end;
-    int len, total_len;
-    uint32_t sharedSz;
 
-    shared_start = platGetSharedAreaInfo(&sharedSz);
-    shared_end = shared_start + sharedSz;
+    resp->accepted = doStartFirmwareUpload(req);
 
-    if (!mDownloadState) {
-        mDownloadState = heapAlloc(sizeof(struct DownloadState));
-
-        if (!mDownloadState) {
-            resp->accepted = false;
-            return sizeof(*resp);
-        } else {
-            memset(mDownloadState, 0x00, sizeof(struct DownloadState));
-        }
-    }
-
-    mDownloadState->type = req->type;
-    mDownloadState->size = le32toh(req->size);
-    mDownloadState->crc = le32toh(req->crc);
-    mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
-
-    for (shared = shared_start;
-         shared < shared_end && shared[0] != 0xFF;
-         shared += total_len) {
-        len = (shared[1] << 16) | (shared[2] << 8) | shared[3];
-        total_len = sizeof(uint32_t) + ((len + 3) & ~3) + sizeof(uint32_t);
-    }
-
-    if (shared + sizeof(uint32_t) + ((mDownloadState->size + 3) & ~3) + sizeof(uint32_t) < shared_end) {
-        mDownloadState->start = shared;
-        mDownloadState->erase = false;
-    } else {
-        mDownloadState->start = shared_start;
-        mDownloadState->erase = true;
-    }
-    resetDownloadState();
-
-    resp->accepted = true;
     return sizeof(*resp);
 }
 
-static void firmwareErase(void *cookie)
-{
-    osLog(LOG_INFO, "hostIntfFirmwareErase: Firmware Erase\n");
-    if (mDownloadState->erase == true) {
-        mpuAllowRamExecution(true);
-        mpuAllowRomWrite(true);
-        (void)BL.blEraseShared(BL_FLASH_KEY1, BL_FLASH_KEY2);
-        mpuAllowRomWrite(false);
-        mpuAllowRamExecution(false);
-        mDownloadState->erase = false;
-        hostIntfSetInterrupt(NANOHUB_INT_CMD_WAIT);
-    }
-}
-
 static AppSecErr giveAppSecTimeIfNeeded(struct AppSecState *state, AppSecErr prevRet)
 {
     /* XXX: this will need to go away for real asynchronicity */
@@ -293,198 +309,267 @@
     return prevRet;
 }
 
-static uint8_t firmwareFinish(bool valid)
+static void deferredUpdateOs(void *cookie)
 {
-    uint8_t buffer[7];
-    int padlen;
-    uint32_t crc;
-    uint16_t marker;
-    static const char magic[] = APP_HDR_MAGIC;
-    const struct AppHdr *app;
+    const struct AppHdr *app = cookie;
+    struct OsUpdateHdr *os = (struct OsUpdateHdr *)(&(app->hdr) + 1);
+    uint32_t uploadStatus = OS_UPDT_HDR_CHECK_FAILED;
+    uint8_t marker = OS_UPDT_MARKER_DOWNLOADED;
+    struct Segment *seg = osGetSegment(app);
+    uint32_t segSize = osSegmentGetSize(seg);
+
+    osLog(LOG_INFO, "%s: checking OS image @ %p\n", __func__, os);
+    // some sanity checks before asking BL to do image lookup
+    hostIntfSetBusy(true);
+    if (segSize >= (sizeof(*app) + sizeof(*os)) && segSize > os->size) {
+        if (osWriteShared(&os->marker, &marker, sizeof(os->marker)))
+            uploadStatus = BL.blVerifyOsUpdate();
+        else
+            osLog(LOG_ERROR, "%s: could not set marker on OS image\n", __func__);
+    }
+    hostIntfSetBusy(false);
+    osLog(LOG_INFO, "%s: status=%" PRIu32 "\n", __func__, uploadStatus);
+}
+
+static AppSecErr updateKey(const struct AppHdr *app)
+{
+    AppSecErr ret;
+    struct KeyInfo *ki = (struct KeyInfo *)(&(app->hdr) + 1);
+    uint8_t *data = (uint8_t *)(ki + 1);
+    uint64_t keyId = KEY_ID_MAKE(APP_ID_GET_VENDOR(app->hdr.appId), ki->id);
+    const char *op;
+
+    if ((app->hdr.fwFlags & FL_KEY_HDR_DELETE) != 0) {
+        // removing existing key
+        ret = osSecretKeyDelete(keyId);
+        op = "Removing";
+    } else {
+        // adding new key
+        ret = osSecretKeyAdd(keyId, data);
+        op = "Adding";
+    }
+    osLog(LOG_INFO, "%s: %s key: id=%016" PRIX64 "; ret=%" PRIu32 "\n",
+          __func__, op, keyId, ret);
+
+    return ret;
+}
+
+static uint32_t firmwareFinish(bool valid)
+{
+    struct AppHdr *app;
+    struct Segment *storageSeg;
+    uint32_t segState;
+    uint32_t ret = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
+
+    if (!mDownloadState)
+        return mAppSecStatus;
 
     mAppSecStatus = appSecRxDataOver(mDownloadState->appSecState);
     mAppSecStatus = giveAppSecTimeIfNeeded(mDownloadState->appSecState, mAppSecStatus);
 
-    if (mAppSecStatus == APP_SEC_NO_ERROR && valid && mDownloadState->type == BL_FLASH_APP_ID) {
-        app = (const struct AppHdr *)&mDownloadState->start[4];
+    app = mDownloadState->start;
+    storageSeg = osGetSegment(app);
 
-        if (app->marker != APP_HDR_MARKER_UPLOADING ||
-                mDownloadState->size < sizeof(uint32_t) + sizeof(struct AppHdr) ||
-                memcmp(magic, app->magic, sizeof(magic)-1) ||
-                app->fmtVer != APP_HDR_VER_CUR) {
-            marker = APP_HDR_MARKER_DELETED;
+    if (mAppSecStatus == APP_SEC_NO_ERROR && valid) {
+        if (storageSeg->state != SEG_ST_RESERVED ||
+                mDownloadState->size < sizeof(struct FwCommonHdr) ||
+                app->hdr.magic != APP_HDR_MAGIC ||
+                app->hdr.fwVer != APP_HDR_VER_CUR) {
+            segState = SEG_ST_ERASED;
         } else {
-            marker = APP_HDR_MARKER_VALID;
-        }
-
-        osLog(LOG_INFO, "Loaded %s app: %ld bytes @ %p\n", marker == APP_HDR_MARKER_VALID ? "valid" : "invalid", mDownloadState->size, mDownloadState->start);
-
-        mpuAllowRamExecution(true);
-        mpuAllowRomWrite(true);
-        if (!BL.blProgramShared((uint8_t *)&app->marker, (uint8_t *)&marker, sizeof(marker), BL_FLASH_KEY1, BL_FLASH_KEY2)) {
-            mpuAllowRomWrite(false);
-            mpuAllowRamExecution(false);
-            return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
+            segState = SEG_ST_VALID;
         }
     } else {
-        mpuAllowRamExecution(true);
-        mpuAllowRomWrite(true);
+        segState = SEG_ST_ERASED;
     }
 
-    if (mAppSecStatus == APP_SEC_NO_ERROR && valid)
-        buffer[0] = ((mDownloadState->type & 0xF) << 4) | (mDownloadState->type & 0xF);
-    else
-        buffer[0] = 0x00;
+    if (!osAppSegmentClose(app, mDownloadState->dstOffset, segState))
+        ret = NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
 
-    buffer[1] = (mDownloadState->dstOffset - 4) >> 16;
-    buffer[2] = (mDownloadState->dstOffset - 4) >> 8;
-    buffer[3] = (mDownloadState->dstOffset - 4);
+    segState = osAppSegmentGetState(app);
+    osLog(LOG_INFO, "Loaded %s image type %" PRIu8 ": %" PRIu32
+                    " bytes @ %p; state=%02" PRIX32 "\n",
+                    segState == SEG_ST_VALID ? "valid" : "invalid",
+                    app->hdr.payInfoType, mDownloadState->size,
+                    mDownloadState->start, segState);
 
-    if (!BL.blProgramShared(mDownloadState->start, buffer, 4, BL_FLASH_KEY1, BL_FLASH_KEY2)) {
-        mpuAllowRomWrite(false);
-        mpuAllowRamExecution(false);
-        return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
+    freeDownloadState(); // no more access to mDownloadState
+
+    // take extra care about some special payload types
+    if (ret == APP_SEC_NO_ERROR) {
+        switch(app->hdr.payInfoType) {
+        case LAYOUT_OS:
+            osLog(LOG_INFO, "Performing OS update\n");
+            // we want to give this message a chance to reach host before we start erasing stuff
+            osDefer(deferredUpdateOs, (void*)app, false);
+            break;
+        case LAYOUT_KEY:
+            ret = updateKey(app);
+            break;
+        }
     }
 
-    crc = ~crc32(mDownloadState->start, mDownloadState->dstOffset, ~0);
-    padlen =  (4 - (mDownloadState->dstOffset & 3)) & 3;
-    memset(buffer, 0x00, padlen);
-    memcpy(&buffer[padlen], &crc, sizeof(uint32_t));
-    mDownloadState->size = mDownloadState->dstOffset + padlen + sizeof(uint32_t);
-
-    if (!BL.blProgramShared(mDownloadState->start + mDownloadState->dstOffset, buffer, padlen + sizeof(uint32_t), BL_FLASH_KEY1, BL_FLASH_KEY2)) {
-        mpuAllowRomWrite(false);
-        mpuAllowRamExecution(false);
-        return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
+    if (ret != APP_SEC_NO_ERROR || (app->hdr.fwFlags & FL_APP_HDR_VOLATILE)) {
+        if ((app->hdr.fwFlags | FL_APP_HDR_SECURE))
+            osAppWipeData((struct AppHdr*)app);
+        osAppSegmentSetState(app, SEG_ST_ERASED);
     }
 
-    mpuAllowRomWrite(false);
-    mpuAllowRamExecution(false);
-    freeDownloadState();
-
-    return NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
+    return ret;
 }
 
-static void firmwareWrite(void *cookie)
+static void firmwareErase(void *cookie)
+{
+    if (mDownloadState->erase == true) {
+        osLog(LOG_INFO, "%s: erasing shared area\n", __func__);
+        osEraseShared();
+        mDownloadState->start = osAppSegmentCreate(mDownloadState->size);
+        if (!mDownloadState->start)
+            firmwareFinish(false);
+        mDownloadState->erase = false;
+        hostIntfSetInterrupt(NANOHUB_INT_CMD_WAIT);
+    }
+    mDownloadState->eraseScheduled = false;
+}
+
+static bool firmwareWrite(bool checkCrc)
+{
+    /* XXX: this will need to change for real asynchronicity */
+    const uint8_t *data = mDownloadState->data;
+    uint32_t len = mDownloadState->len, lenLeft;
+    mAppSecStatus = APP_SEC_NO_ERROR;
+    bool valid;
+    bool finished = false;
+
+    while (len) {
+        mAppSecStatus = appSecRxData(mDownloadState->appSecState, data, len, &lenLeft);
+        data += len - lenLeft;
+        len = lenLeft;
+
+        mAppSecStatus = giveAppSecTimeIfNeeded(mDownloadState->appSecState, mAppSecStatus);
+    }
+
+    valid = (mAppSecStatus == APP_SEC_NO_ERROR);
+    if (valid) {
+        if (mDownloadState->srcOffset == mDownloadState->size) {
+            finished = true;
+            valid = !checkCrc || mDownloadState->crc == ~mDownloadState->srcCrc;
+        } else if (mDownloadState->srcOffset > mDownloadState->size) {
+            valid = false;
+        }
+    }
+    if (!valid)
+        finished = true;
+    if (finished)
+        firmwareFinish(valid);
+
+    return valid;
+}
+
+static uint32_t doFirmwareChunk(uint8_t *data, uint32_t offset, uint32_t len, bool checkCrc)
 {
     uint32_t reply;
 
-    if (mDownloadState->type == BL_FLASH_APP_ID) {
-        /* XXX: this will need to change for real asynchronicity */
-        const uint8_t *data = mDownloadState->data;
-        uint32_t len = mDownloadState->len, lenLeft;
-        mAppSecStatus = APP_SEC_NO_ERROR;
-
-        while (len) {
-            mAppSecStatus = appSecRxData(mDownloadState->appSecState, data, len, &lenLeft);
-            data += len - lenLeft;
-            len = lenLeft;
-
-            mAppSecStatus = giveAppSecTimeIfNeeded(mDownloadState->appSecState, mAppSecStatus);
-        }
-    }
-    else
-        mAppSecStatus = writeCbk(mDownloadState->data, mDownloadState->len);
-
-    if (mAppSecStatus == APP_SEC_NO_ERROR) {
-        if (mDownloadState->srcOffset == mDownloadState->size && mDownloadState->crc == ~mDownloadState->srcCrc) {
-            reply = firmwareFinish(true);
-            if (mDownloadState)
-                mDownloadState->chunkReply = reply;
-        } else {
-            mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
-        }
+    if (!mDownloadState) {
+        reply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
+    } else if (mDownloadState->chunkReply != NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED) {
+        reply = mDownloadState->chunkReply;
+        firmwareFinish(false);
     } else {
-        freeDownloadState();
+        if (mDownloadState->erase == true) {
+            reply = NANOHUB_FIRMWARE_CHUNK_REPLY_WAIT;
+            if (!mDownloadState->eraseScheduled)
+                mDownloadState->eraseScheduled = osDefer(firmwareErase, NULL, false);
+        } else if (!mDownloadState->start) {
+            // this means we can't allocate enough space even after we did erase
+            reply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
+            firmwareFinish(false);
+        } else if (offset != mDownloadState->srcOffset) {
+            reply = NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART;
+            resetDownloadState(false);
+        } else {
+            if (checkCrc)
+                mDownloadState->srcCrc = crc32(data, len, mDownloadState->srcCrc);
+            mDownloadState->srcOffset += len;
+            memcpy(mDownloadState->data, data, len);
+            mDownloadState->len = len;
+            reply = firmwareWrite(checkCrc) ? NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED :
+                                              NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL;
+        }
     }
-    hostIntfSetBusy(false);
+
+    return reply;
 }
 
 static uint32_t firmwareChunk(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
 {
-    uint32_t offset;
-    uint8_t len;
     struct NanohubFirmwareChunkRequest *req = rx;
     struct NanohubFirmwareChunkResponse *resp = tx;
+    uint32_t offset = le32toh(req->offset);
+    uint8_t len = rx_len - sizeof(req->offset);
 
-    offset = le32toh(req->offset);
-    len = rx_len - sizeof(req->offset);
-
-    if (!mDownloadState) {
-        resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
-    } else if (mDownloadState->chunkReply != NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED) {
-        resp->chunkReply = mDownloadState->chunkReply;
-        freeDownloadState();
-    } else if (mDownloadState->erase == true) {
-        resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_WAIT;
-        osDefer(firmwareErase, NULL, false);
-    } else if (offset != mDownloadState->srcOffset) {
-        resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART;
-        resetDownloadState();
-    } else {
-        mDownloadState->srcCrc = crc32(req->data, len, mDownloadState->srcCrc);
-        mDownloadState->srcOffset += len;
-        if ((mDownloadState->srcOffset == mDownloadState->size && mDownloadState->crc != ~mDownloadState->srcCrc) || (mDownloadState->srcOffset > mDownloadState->size)) {
-            firmwareFinish(false);
-            resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL;
-        } else {
-            memcpy(mDownloadState->data, req->data, len);
-            mDownloadState->len = len;
-            resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
-            hostIntfSetBusy(true);
-            osDefer(firmwareWrite, NULL, false);
-        }
-    }
+    resp->chunkReply = doFirmwareChunk(req->data, offset, len, true);
 
     return sizeof(*resp);
 }
 
+static uint32_t doFinishFirmwareUpload()
+{
+    uint32_t reply;
+
+    if (!mDownloadState) {
+        switch (mAppSecStatus) {
+        case APP_SEC_NO_ERROR:
+            reply = NANOHUB_FIRMWARE_UPLOAD_SUCCESS;
+            break;
+        case APP_SEC_KEY_NOT_FOUND:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_KEY_NOT_FOUND;
+            break;
+        case APP_SEC_HEADER_ERROR:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_HEADER_ERROR;
+            break;
+        case APP_SEC_TOO_MUCH_DATA:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_MUCH_DATA;
+            break;
+        case APP_SEC_TOO_LITTLE_DATA:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_LITTLE_DATA;
+            break;
+        case APP_SEC_SIG_VERIFY_FAIL:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_VERIFY_FAIL;
+            break;
+        case APP_SEC_SIG_DECODE_FAIL:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_DECODE_FAIL;
+            break;
+        case APP_SEC_SIG_ROOT_UNKNOWN:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_ROOT_UNKNOWN;
+            break;
+        case APP_SEC_MEMORY_ERROR:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_MEMORY_ERROR;
+            break;
+        case APP_SEC_INVALID_DATA:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_INVALID_DATA;
+            break;
+        case APP_SEC_VERIFY_FAILED:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_VERIFY_FAILED;
+            break;
+        default:
+            reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_BAD;
+            break;
+        }
+    } else if (mDownloadState->srcOffset == mDownloadState->size) {
+        reply = NANOHUB_FIRMWARE_UPLOAD_PROCESSING;
+    } else {
+        reply = firmwareFinish(false);
+    }
+
+    return reply;
+}
+
 static uint32_t finishFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
 {
     struct NanohubFinishFirmwareUploadResponse *resp = tx;
-
-    if (!mDownloadState) {
-        switch (mAppSecStatus) {
-        case APP_SEC_NO_ERROR:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_SUCCESS;
-            break;
-        case APP_SEC_KEY_NOT_FOUND:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_KEY_NOT_FOUND;
-            break;
-        case APP_SEC_HEADER_ERROR:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_HEADER_ERROR;
-            break;
-        case APP_SEC_TOO_MUCH_DATA:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_MUCH_DATA;
-            break;
-        case APP_SEC_TOO_LITTLE_DATA:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_LITTLE_DATA;
-            break;
-        case APP_SEC_SIG_VERIFY_FAIL:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_VERIFY_FAIL;
-            break;
-        case APP_SEC_SIG_DECODE_FAIL:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_DECODE_FAIL;
-            break;
-        case APP_SEC_SIG_ROOT_UNKNOWN:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_ROOT_UNKNOWN;
-            break;
-        case APP_SEC_MEMORY_ERROR:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_MEMORY_ERROR;
-            break;
-        case APP_SEC_INVALID_DATA:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_INVALID_DATA;
-            break;
-        default:
-            resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_BAD;
-            break;
-        }
-    } else if (mDownloadState->srcOffset == mDownloadState->size) {
-        resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_PROCESSING;
-    } else {
-        resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_WAITING_FOR_DATA;
-    }
-
+    resp->uploadReply = doFinishFirmwareUpload();
+    osLog(LOG_INFO, "%s: reply=%" PRIu8 "\n", __func__, resp->uploadReply);
     return sizeof(*resp);
 }
 
@@ -972,10 +1057,10 @@
 static void halStartUpload(void *rx, uint8_t rx_len)
 {
     struct NanohubHalStartUploadRx *req = rx;
+    struct NanohubStartFirmwareUploadRequest hwReq = {
+        .size= req->length
+    };
     struct NanohubHalStartUploadTx *resp;
-    uint8_t *shared, *shared_start, *shared_end;
-    int len, total_len;
-    uint32_t sharedSz;
 
     if (!(resp = heapAlloc(sizeof(*resp))))
         return;
@@ -983,53 +1068,18 @@
     resp->hdr.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0);
     resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1;
     resp->hdr.msg = NANOHUB_HAL_START_UPLOAD;
+    resp->success = doStartFirmwareUpload(&hwReq);
 
-    shared_start = platGetSharedAreaInfo(&sharedSz);
-    shared_end = shared_start + sharedSz;
-
-    if (!mDownloadState) {
-        mDownloadState = heapAlloc(sizeof(struct DownloadState));
-
-        if (!mDownloadState) {
-            resp->success = false;
-            osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
-            return;
-        } else {
-            memset(mDownloadState, 0x00, sizeof(struct DownloadState));
-        }
-    }
-
-    mDownloadState->type = req->isOs ? BL_FLASH_KERNEL_ID : BL_FLASH_APP_ID;
-    mDownloadState->size = le32toh(req->length);
-    mDownloadState->crc = le32toh(0x00000000);
-    mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
-
-    for (shared = shared_start;
-         shared < shared_end && shared[0] != 0xFF;
-         shared += total_len) {
-        len = (shared[1] << 16) | (shared[2] << 8) | shared[3];
-        total_len = sizeof(uint32_t) + ((len + 3) & ~3) + sizeof(uint32_t);
-    }
-
-    if (shared + sizeof(uint32_t) + ((mDownloadState->size + 3) & ~3) + sizeof(uint32_t) < shared_end) {
-        mDownloadState->start = shared;
-        mDownloadState->erase = false;
-    } else {
-        mDownloadState->start = shared_start;
-        mDownloadState->erase = true;
-    }
-    resetDownloadState();
-
-    resp->success = true;
     osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
 }
 
 static void halContUpload(void *rx, uint8_t rx_len)
 {
+    uint32_t offset;
+    uint32_t reply;
+    uint8_t len;
     struct NanohubHalContUploadRx *req = rx;
     struct NanohubHalContUploadTx *resp;
-    uint32_t offset;
-    uint8_t len;
 
     if (!(resp = heapAlloc(sizeof(*resp))))
         return;
@@ -1038,31 +1088,17 @@
     resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1;
     resp->hdr.msg = NANOHUB_HAL_CONT_UPLOAD;
 
-    offset = le32toh(req->offset);
-    len = rx_len - sizeof(req->offset);
-
     if (!mDownloadState) {
-        resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
-    } else if (mDownloadState->erase == true) {
-        resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_WAIT;
-        firmwareErase(NULL);
-    } else if (offset != mDownloadState->srcOffset) {
-        resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART;
-        resetDownloadState();
-    } else if (mDownloadState->srcOffset + len <= mDownloadState->size) {
-        mDownloadState->srcOffset += len;
-        memcpy(mDownloadState->data, req->data, len);
-        mDownloadState->len = len;
-        hostIntfSetBusy(true);
-        firmwareWrite(NULL);
-        if (mDownloadState)
-            resp->success = mDownloadState->chunkReply;
-        else
-            resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
+        reply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
     } else {
-        resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
+        offset = le32toh(req->offset);
+        len = rx_len - sizeof(req->offset);
+        reply = doFirmwareChunk(req->data, offset, len, false);
     }
-    resp->success = !resp->success;
+    if (reply != NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED)
+        osLog(LOG_ERROR, "%s: reply=%" PRIu32 "\n", __func__, reply);
+
+    resp->success = (reply == NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED);
 
     osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
 }
@@ -1070,6 +1106,7 @@
 static void halFinishUpload(void *rx, uint8_t rx_len)
 {
     struct NanohubHalFinishUploadTx *resp;
+    uint32_t reply;
 
     if (!(resp = heapAlloc(sizeof(*resp))))
         return;
@@ -1078,48 +1115,11 @@
     resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1;
     resp->hdr.msg = NANOHUB_HAL_FINISH_UPLOAD;
 
-    if (!mDownloadState) {
-        switch (mAppSecStatus) {
-        case APP_SEC_NO_ERROR:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_SUCCESS;
-            break;
-        case APP_SEC_KEY_NOT_FOUND:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_KEY_NOT_FOUND;
-            break;
-        case APP_SEC_HEADER_ERROR:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_HEADER_ERROR;
-            break;
-        case APP_SEC_TOO_MUCH_DATA:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_MUCH_DATA;
-            break;
-        case APP_SEC_TOO_LITTLE_DATA:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_LITTLE_DATA;
-            break;
-        case APP_SEC_SIG_VERIFY_FAIL:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_VERIFY_FAIL;
-            break;
-        case APP_SEC_SIG_DECODE_FAIL:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_DECODE_FAIL;
-            break;
-        case APP_SEC_SIG_ROOT_UNKNOWN:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_ROOT_UNKNOWN;
-            break;
-        case APP_SEC_MEMORY_ERROR:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_MEMORY_ERROR;
-            break;
-        case APP_SEC_INVALID_DATA:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_INVALID_DATA;
-            break;
-        default:
-            resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_BAD;
-            break;
-        }
-    } else if (mDownloadState->srcOffset == mDownloadState->size) {
-        resp->success = NANOHUB_FIRMWARE_UPLOAD_PROCESSING;
-    } else {
-        resp->success = NANOHUB_FIRMWARE_UPLOAD_WAITING_FOR_DATA;
-    }
-    resp->success = !resp->success;
+    reply = doFinishFirmwareUpload();
+
+    osLog(LOG_INFO, "%s: reply=%" PRIu32 "\n", __func__, reply);
+
+    resp->success = (reply == NANOHUB_FIRMWARE_UPLOAD_SUCCESS);
 
     osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
 }
diff --git a/firmware/src/platform/stm32f4xx/bl.c b/firmware/src/platform/stm32f4xx/bl.c
index 61f5f90..ac2d4ec 100644
--- a/firmware/src/platform/stm32f4xx/bl.c
+++ b/firmware/src/platform/stm32f4xx/bl.c
@@ -16,17 +16,21 @@
 
 
 #include <variant/inc/variant.h>
+
 #include <plat/inc/cmsis.h>
 #include <plat/inc/gpio.h>
 #include <plat/inc/pwr.h>
 #include <plat/inc/bl.h>
+
+#include <nanohub/sha2.h>
+#include <nanohub/aes.h>
+#include <nanohub/rsa.h>
+#include <nanohub/nanohub.h>
+
 #include <printf.h>
 #include <string.h>
 #include <alloca.h>
 #include <gpio.h>
-#include <sha2.h>
-#include <aes.h>
-#include <rsa.h>
 
 static uint32_t blVerifyOsImage(const uint8_t *addr, struct OsUpdateHdr **start, uint32_t *size);
 
diff --git a/firmware/src/platform/stm32f4xx/crc.c b/firmware/src/platform/stm32f4xx/crc.c
index 8538cc5..771703d 100644
--- a/firmware/src/platform/stm32f4xx/crc.c
+++ b/firmware/src/platform/stm32f4xx/crc.c
@@ -16,7 +16,7 @@
 
 #include <string.h>
 
-#include <crc.h>
+#include <nanohub/crc.h>
 #include <seos.h>
 
 #include <plat/inc/pwr.h>
diff --git a/firmware/src/platform/stm32f4xx/eeData.c b/firmware/src/platform/stm32f4xx/eeData.c
index bcd8893..906b9f3 100644
--- a/firmware/src/platform/stm32f4xx/eeData.c
+++ b/firmware/src/platform/stm32f4xx/eeData.c
@@ -20,7 +20,6 @@
 #include <stdint.h>
 #include <eeData.h>
 
-
 extern uint32_t __eedata_start[], __eedata_end[];
 
 //STM32F4xx eedata stores data in 4-byte aligned chunks
@@ -65,7 +64,7 @@
     return name && name < EE_DATA_NAME_MAX;
 }
 
-static bool eeDataGetEx(uint32_t name, uint32_t *offsetP, bool first, void *buf, uint32_t *szP)
+static void *eeDataGetEx(uint32_t name, uint32_t *offsetP, bool first, void *buf, uint32_t *szP)
 {
     uint32_t sz = 0;
     void *data;
@@ -76,7 +75,7 @@
     //find the data item
     data = eeFind(name, offsetP, first, &sz);
     if (!data)
-        return false;
+        return NULL;
 
     if (buf && szP) {    //get the data
         if (sz > *szP)
@@ -87,25 +86,22 @@
     else if (szP)        //get size
         *szP = sz;
 
-    return true;
+    return (uint32_t*)data - 1;
 }
 
 bool eeDataGet(uint32_t name, void *buf, uint32_t *szP)
 {
     uint32_t offset = 0;
 
-    return eeDataGetEx(name, &offset, false, buf, szP);
+    return eeDataGetEx(name, &offset, false, buf, szP) != NULL;
 }
 
-bool eeDataGetAllVersions(uint32_t name, void *buf, uint32_t *szP, void **stateP)
+void *eeDataGetAllVersions(uint32_t name, void *buf, uint32_t *szP, void **stateP)
 {
     uint32_t offset = *(uint32_t*)stateP;
-    bool ret;
-
-    ret = eeDataGetEx(name, &offset, true, buf, szP);
+    void *addr = eeDataGetEx(name, &offset, true, buf, szP);
     *(uint32_t*)stateP = offset;
-
-    return ret;
+    return addr;
 }
 
 static bool eeWrite(void *dst, const void *src, uint32_t len)
@@ -141,13 +137,17 @@
     return ret;
 }
 
-bool eeDataEraseOldVersion(uint32_t name, void *state)
+bool eeDataEraseOldVersion(uint32_t name, void *vaddr)
 {
-    uint32_t v = *(uint32_t*)state;
+    uint32_t *addr = (uint32_t*)vaddr;
+    uint32_t v;
 
-    if (!eeIsValidName(name))
+    // sanity check
+    if (!eeIsValidName(name) || addr < __eedata_start || addr >= (__eedata_end - 1))
         return false;
 
+    v = *addr;
+
     //verify name
     if ((v & EE_DATA_NAME_MAX) != name)
         return false;
@@ -156,15 +156,5 @@
     v &=~ EE_DATA_NAME_MAX;
 
     //store result
-    return eeWrite(state, &v, sizeof(v));
+    return eeWrite(addr, &v, sizeof(v));
 }
-
-
-
-
-
-
-
-
-
-
diff --git a/firmware/src/rsa.c b/firmware/src/rsa.c
deleted file mode 100644
index 3382b72..0000000
--- a/firmware/src/rsa.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2016 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 <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <rsa.h>
-
-
-static bool biModIterative(uint32_t *num, const uint32_t *denum, uint32_t *tmp, uint32_t *state1, uint32_t *state2, uint32_t step)
-//num %= denum where num is RSA_LEN * 2 and denum is RSA_LEN and tmp is RSA_LEN + limb_sz
-//will need to be called till it returns true (up to RSA_LEN * 2 + 2 times)
-{
-    uint32_t bitsh = *state1, limbsh = *state2;
-    bool ret = false;
-    int64_t t;
-    int32_t i;
-
-    //first step is init
-    if (!step) {
-        //initially set it up left shifted as far as possible
-        memcpy(tmp + 1, denum, RSA_BYTES);
-        tmp[0] = 0;
-        bitsh = 32;
-        limbsh = RSA_LIMBS - 1;
-        goto out;
-    }
-
-    //second is shifting denum
-    if (step == 1) {
-        while (!(tmp[RSA_LIMBS] & 0x80000000)) {
-            for (i = RSA_LIMBS; i > 0; i--) {
-                tmp[i] <<= 1;
-                if (tmp[i - 1] & 0x80000000)
-                    tmp[i]++;
-            }
-            //no need to adjust tmp[0] as it is still zero
-            bitsh++;
-        }
-        goto out;
-    }
-
-    //all future steps do the division
-
-    //check if we should subtract (uses less space than subtracting and unroling it later)
-    for (i = RSA_LIMBS; i >= 0; i--) {
-        if (num[limbsh + i] < tmp[i])
-            goto dont_subtract;
-        if (num[limbsh + i] > tmp[i])
-            break;
-    }
-
-    //subtract
-    t = 0;
-    for (i = 0; i <= RSA_LIMBS; i++) {
-        t += (uint64_t)num[limbsh + i];
-        t -= (uint64_t)tmp[i];
-        num[limbsh + i] = t;
-        t >>= 32;
-    }
-
-    //carry the subtraction's carry to the end
-    for (i = RSA_LIMBS + limbsh + 1; i < RSA_LIMBS * 2; i++) {
-        t += (uint64_t)num[i];
-        num[i] = t;
-        t >>= 32;
-    }
-
-dont_subtract:
-    //handle bitshifts/refills
-    if (!bitsh) {                          // tmp = denum << 32
-        if (!limbsh) {
-            ret = true;
-            goto out;
-        }
-
-        memcpy(tmp + 1, denum, RSA_BYTES);
-        tmp[0] = 0;
-        bitsh = 32;
-        limbsh--;
-    }
-    else {                                 // tmp >>= 1
-        for (i = 0; i < RSA_LIMBS; i++) {
-            tmp[i] >>= 1;
-            if (tmp[i + 1] & 1)
-                tmp[i] += 0x80000000;
-        }
-        tmp[i] >>= 1;
-        bitsh--;
-    }
-
-
-out:
-    *state1 = bitsh;
-    *state2 = limbsh;
-    return ret;
-}
-
-static void biMulIterative(uint32_t *ret, const uint32_t *a, const uint32_t *b, uint32_t step) //ret = a * b, call with step = [0..RSA_LIMBS)
-{
-    uint32_t j, c;
-    uint64_t r;
-
-    //zero the result on first call
-    if (!step)
-        memset(ret, 0, RSA_BYTES * 2);
-
-    //produce a partial sum & add it in
-    c = 0;
-    for (j = 0; j < RSA_LIMBS; j++) {
-        r = (uint64_t)a[step] * b[j] + c + ret[step + j];
-        ret[step + j] = r;
-        c = r >> 32;
-    }
-
-    //carry the carry to the end
-    for (j = step + RSA_LIMBS; j < RSA_LIMBS * 2; j++) {
-        r = (uint64_t)ret[j] + c;
-        ret[j] = r;
-        c = r >> 32;
-    }
-}
-
-/*
- * Piecewise RSA:
- * normal RSA public op with 65537 exponent does 34 operations. 17 muls and 17 mods, as follows:
- * 16x {mul, mod} to calculate a ^ 65536 mod c
- * 1x {mul, mod} to calculate a ^ 65537 mod c
- * we break up each mul and mod itself into more steps. mul needs RSA_LIMBS steps, and mod needs up to RSA_LEN * 2 + 2 steps
- * so if we allocate RSA_LEN * 3 step values to mod, each mul-mod pair will use <= RSA_LEN * 4 step values
- * and the whole opetaion will need <= RSA_LEN * 4 * 34 step values, which fits into a uint32. cool. In fact
- * some values will be skipped, but this makes life easier, really. Call this func with *stepP = 0, and keep calling till
- * output stepP is zero. We'll call each of the RSA_LEN * 4 pieces a gigastep, and have 17 of them as seen above. Each
- * will be logically separated into 4 megasteps. First will contain the MUL, last 3 the MOD and maybe the memcpy.
- * In the first 16 gigasteps, the very last step of the gigastep will be used for the memcpy call.
- *
- * The initial non-iterative RSA logic looks as follows, shown here for clarity:
- *
- *   memcpy(state->tmpB, a, RSA_BYTES);
- *   for (i = 0; i < 16; i++) {
- *       biMul(state->tmpA, state->tmpB, state->tmpB);
- *       biMod(state->tmpA, c, state->tmpB);
- *       memcpy(state->tmpB, state->tmpA, RSA_BYTES);
- *   }
- *
- *   //calculate a ^ 65537 mod c into state->tmpA [ at this point this means do state->tmpA = (state->tmpB * a) % c ]
- *   biMul(state->tmpA, state->tmpB, a);
- *   biMod(state->tmpA, c, state->tmpB);
- *
- *   //return result
- *   return state->tmpA;
- *
- */
-
-const uint32_t* rsaPubOpIterative(struct RsaState* state, const uint32_t *a, const uint32_t *c, uint32_t *state1, uint32_t *state2, uint32_t *stepP)
-{
-    uint32_t step = *stepP, gigastep, gigastepBase, gigastepSubstep, megaSubstep;
-
-    //step 0: copy a -> tmpB
-    if (!step) {
-        memcpy(state->tmpB, a, RSA_BYTES);
-        step = 1;
-    }
-    else { //subsequent steps: do real work
-
-
-        gigastep = (step - 1) / (RSA_LEN * 4);
-        gigastepSubstep = (step - 1) % (RSA_LEN * 4);
-        gigastepBase = gigastep * (RSA_LEN * 4);
-        megaSubstep = gigastepSubstep / RSA_LEN;
-
-        if (!megaSubstep) { // first megastep of the gigastep - MUL
-            biMulIterative(state->tmpA, state->tmpB, gigastep == 16 ? a : state->tmpB, gigastepSubstep);
-            if (gigastepSubstep == RSA_LIMBS - 1) //MUL is done - do mod next
-                step = gigastepBase + RSA_LEN + 1;
-            else                                  //More of MUL is left to do
-                step++;
-        }
-        else if (gigastepSubstep != RSA_LEN * 4 - 1){   // second part of gigastep - MOD
-            if (biModIterative(state->tmpA, c, state->tmpB, state1, state2, gigastepSubstep - RSA_LEN)) { //MOD is done
-                if (gigastep == 16) // we're done
-                    step = 0;
-                else              // last part of the gigastep is a copy
-                    step = gigastepBase + RSA_LEN * 4 - 1 + 1;
-            }
-            else
-                step++;
-        }
-        else {   //last part - memcpy
-            memcpy(state->tmpB, state->tmpA, RSA_BYTES);
-            step++;
-        }
-    }
-
-    *stepP = step;
-    return state->tmpA;
-}
-
-#if defined(RSA_SUPPORT_PRIV_OP_LOWRAM) || defined (RSA_SUPPORT_PRIV_OP_BIGRAM)
-#include <stdio.h>
-const uint32_t* rsaPubOp(struct RsaState* state, const uint32_t *a, const uint32_t *c)
-{
-    const uint32_t *ret;
-    uint32_t state1 = 0, state2 = 0, step = 0, ns = 0;
-
-    do {
-        ret = rsaPubOpIterative(state, a, c, &state1, &state2, &step);
-        ns++;
-    } while(step);
-
-fprintf(stderr, "steps: %u\n", ns);
-
-    return ret;
-}
-
-static void biMod(uint32_t *num, const uint32_t *denum, uint32_t *tmp)
-{
-    uint32_t state1 = 0, state2 = 0, step;
-
-    for (step = 0; !biModIterative(num, denum, tmp, &state1, &state2, step); step++);
-}
-
-static void biMul(uint32_t *ret, const uint32_t *a, const uint32_t *b)
-{
-    uint32_t step;
-
-    for (step = 0; step < RSA_LIMBS; step++)
-        biMulIterative(ret, a, b, step);
-}
-
-const uint32_t* rsaPrivOp(struct RsaState* state, const uint32_t *a, const uint32_t *b, const uint32_t *c)
-{
-    uint32_t i;
-
-    memcpy(state->tmpC, a, RSA_BYTES);  //tC will hold our powers of a
-
-    memset(state->tmpA, 0, RSA_BYTES * 2); //tA will hold result
-    state->tmpA[0] = 1;
-
-    for (i = 0; i < RSA_LEN; i++) {
-        //if the bit is set, multiply the current power of A into result
-        if (b[i / 32] & (1 << (i % 32))) {
-            memcpy(state->tmpB, state->tmpA, RSA_BYTES);
-            biMul(state->tmpA, state->tmpB, state->tmpC);
-            biMod(state->tmpA, c, state->tmpB);
-        }
-
-        //calculate the next power of a and modulus it
-#if defined(RSA_SUPPORT_PRIV_OP_LOWRAM)
-        memcpy(state->tmpB, state->tmpA, RSA_BYTES); //save tA
-        biMul(state->tmpA, state->tmpC, state->tmpC);
-        biMod(state->tmpA, c, state->tmpC);
-        memcpy(state->tmpC, state->tmpA, RSA_BYTES);
-        memcpy(state->tmpA, state->tmpB, RSA_BYTES); //restore tA
-#elif defined (RSA_SUPPORT_PRIV_OP_BIGRAM)
-        memcpy(state->tmpB, state->tmpC, RSA_BYTES);
-        biMul(state->tmpC, state->tmpB, state->tmpB);
-        biMod(state->tmpC, c, state->tmpB);
-#endif
-    }
-
-    return state->tmpA;
-}
-#endif
-
-
-
-
-
-
-
-
diff --git a/firmware/src/seos.c b/firmware/src/seos.c
index 48f74c3..c0a0dd4 100644
--- a/firmware/src/seos.c
+++ b/firmware/src/seos.c
@@ -34,12 +34,14 @@
 #include <heap.h>
 #include <slab.h>
 #include <cpu.h>
-#include <crc.h>
 #include <util.h>
 #include <mpu.h>
 #include <nanohubPacket.h>
 #include <atomic.h>
 
+#include <nanohub/nanohub.h>
+#include <nanohub/crc.h>
+
 #define NO_NODE (TaskIndex)(-1)
 #define for_each_task(listHead, task) for (task = osTaskByIdx((listHead)->next); task; task = osTaskByIdx(task->list.next))
 #define MAKE_NEW_TID(task) task->tid = ((task->tid + TASK_TID_INCREMENT) & TASK_TID_COUNTER_MASK) | \
@@ -75,7 +77,7 @@
 
 struct Task {
     /* App entry points */
-    const struct AppHdr *appHdr;
+    const struct AppHdr *app;
 
     /* per-platform app info */
     struct PlatAppInfo platInfo;
@@ -339,7 +341,7 @@
 static inline bool osTaskInit(struct Task *task)
 {
     struct Task *preempted = osSetCurrentTask(task);
-    bool done = cpuAppInit(task->appHdr, &task->platInfo, task->tid);
+    bool done = cpuAppInit(task->app, &task->platInfo, task->tid);
     osSetCurrentTask(preempted);
     return done;
 }
@@ -349,7 +351,7 @@
     struct Task *preempted = osSetCurrentTask(task);
     uint16_t tid = task->tid;
 
-    cpuAppEnd(task->appHdr, &task->platInfo);
+    cpuAppEnd(task->app, &task->platInfo);
 
     // task was supposed to release it's resources,
     // but we do our cleanup anyway
@@ -365,7 +367,7 @@
 static inline void osTaskHandle(struct Task *task, uint32_t evtType, const void* evtData)
 {
     struct Task *preempted = osSetCurrentTask(task);
-    cpuAppHandle(task->appHdr, &task->platInfo, evtType, evtData);
+    cpuAppHandle(task->app, &task->platInfo, evtType, evtData);
     osSetCurrentTask(preempted);
 }
 
@@ -414,97 +416,256 @@
     struct Task *task;
 
     for_each_task(&mTasks, task) {
-        if (task->appHdr && task->appHdr->appId == appID)
+        if (task->app && task->app->hdr.appId == appID)
             return task;
     }
 
     return NULL;
 }
 
-struct ExtAppIterator {
-    const uint8_t *shared;
-    const uint8_t *sharedEnd;
-    const struct AppHdr *app;
-    uint32_t appLen;
-};
-
-static void osExtAppIteratorInit(struct ExtAppIterator *it)
+void osSegmentIteratorInit(struct SegmentIterator *it)
 {
     uint32_t sz;
+    uint8_t *start = platGetSharedAreaInfo(&sz);
 
-    it->shared = platGetSharedAreaInfo(&sz);
-    it->sharedEnd = it->shared + sz;
-    it->app = NULL;
-    it->appLen = 0;
+    it->shared    = (const struct Segment *)(start);
+    it->sharedEnd = (const struct Segment *)(start + sz);
+    it->seg       = NULL;
 }
 
-static bool osExtAppIteratorNext(struct ExtAppIterator *it)
-{
-    struct AppHdr *app;
-    uint32_t len;
-    uint32_t total_len;
-    const uint8_t *p = it->shared;
-    uint8_t id1, id2;
-
-    // 32-bit header: 1 byte MARK, 3-byte len (BE, in bytes), data is 32-bit aligned; 32-bit footer: CRC-32, including header
-    // both will soon be obsoleted; app header has enough data to find next app (app->rel_end)
-    do {
-        id1 = p[0] & 0x0F;
-        id2 = (p[0] >> 4) & 0x0F;
-        len = (p[1] << 16) | (p[2] << 8) | p[3];
-        total_len = sizeof(uint32_t) + ((len + 3) & ~3) + sizeof(uint32_t);
-        if ((p + total_len) > it->sharedEnd)
-            return false;
-        app = (struct AppHdr *)(&p[4]);
-        p += total_len;
-    } while (id1 != id2 && id1 != BL_FLASH_APP_ID);
-
-    it->shared = p;
-    it->appLen = len;
-    it->app = app;
-
-    return true;
-}
-
-static bool osExtAppIsValid(const struct AppHdr *app, uint32_t len)
-{
-    static const char magic[] = APP_HDR_MAGIC;
-
-    return len >= sizeof(struct AppHdr) &&
-           memcmp(magic, app->magic, sizeof(magic) - 1) == 0 &&
-           app->fmtVer == APP_HDR_VER_CUR &&
-           app->marker == APP_HDR_MARKER_VALID;
-}
-
-static bool osExtAppErase(const struct AppHdr *app)
+bool osAppSegmentSetState(const struct AppHdr *app, uint32_t segState)
 {
     bool done;
-    uint16_t marker = APP_HDR_MARKER_DELETED;
+    struct Segment *seg = osGetSegment(app);
+    uint8_t state = segState;
+
+    if (!seg)
+        return false;
 
     mpuAllowRamExecution(true);
     mpuAllowRomWrite(true);
-    done = BL.blProgramShared((uint8_t *)&app->marker, (uint8_t *)&marker, sizeof(marker), BL_FLASH_KEY1, BL_FLASH_KEY2);
+    done = BL.blProgramShared(&seg->state, &state, sizeof(state), BL_FLASH_KEY1, BL_FLASH_KEY2);
     mpuAllowRomWrite(false);
     mpuAllowRamExecution(false);
 
     return done;
 }
 
+bool osSegmentSetSize(struct Segment *seg, uint32_t size)
+{
+    bool ret = true;
+
+    if (!seg)
+        return false;
+
+    if (size > SEG_SIZE_MAX) {
+        seg->state = SEG_ST_ERASED;
+        size = SEG_SIZE_MAX;
+        ret = false;
+    }
+    seg->size[0] = size;
+    seg->size[1] = size >> 8;
+    seg->size[2] = size >> 16;
+
+    return ret;
+}
+
+struct Segment *osSegmentGetEnd()
+{
+    uint32_t size;
+    uint8_t *start = platGetSharedAreaInfo(&size);
+    return (struct Segment *)(start + size);
+}
+
+struct Segment *osGetSegment(const struct AppHdr *app)
+{
+    uint32_t size;
+    uint8_t *start = platGetSharedAreaInfo(&size);
+
+    return (struct Segment *)((uint8_t*)app &&
+                              (uint8_t*)app >= start &&
+                              (uint8_t*)app < (start + size) ?
+                              (uint8_t*)app - sizeof(struct Segment) : NULL);
+}
+
+bool osEraseShared()
+{
+    mpuAllowRamExecution(true);
+    mpuAllowRomWrite(true);
+    (void)BL.blEraseShared(BL_FLASH_KEY1, BL_FLASH_KEY2);
+    mpuAllowRomWrite(false);
+    mpuAllowRamExecution(false);
+    return true;
+}
+
+bool osWriteShared(void *dest, const void *src, uint32_t len)
+{
+    bool ret;
+
+    mpuAllowRamExecution(true);
+    mpuAllowRomWrite(true);
+    ret = BL.blProgramShared(dest, src, len, BL_FLASH_KEY1, BL_FLASH_KEY2);
+    mpuAllowRomWrite(false);
+    mpuAllowRamExecution(false);
+
+    return ret;
+}
+
+struct AppHdr *osAppSegmentCreate(uint32_t size)
+{
+    struct SegmentIterator it;
+    const struct Segment *storageSeg = NULL;
+    struct AppHdr *app;
+
+    osSegmentIteratorInit(&it);
+    while (osSegmentIteratorNext(&it)) {
+        if (osSegmentGetState(it.seg) == SEG_ST_EMPTY) {
+            storageSeg = it.seg;
+            break;
+        }
+    }
+    if (!storageSeg || osSegmentSizeGetNext(storageSeg, size) > it.sharedEnd)
+        return NULL;
+
+    app = osSegmentGetData(storageSeg);
+    osAppSegmentSetState(app, SEG_ST_RESERVED);
+
+    return app;
+}
+
+bool osAppSegmentClose(struct AppHdr *app, uint32_t segDataSize, uint32_t segState)
+{
+    struct Segment seg;
+
+    // this is enough for holding padding to uint32_t and the footer
+    uint8_t footer[sizeof(uint32_t) + FOOTER_SIZE];
+    int footerLen;
+    bool ret;
+    uint32_t totalSize;
+    uint8_t *start = platGetSharedAreaInfo(&totalSize);
+    uint8_t *end = start + totalSize;
+    int32_t fullSize = segDataSize + sizeof(seg); // without footer or padding
+    struct Segment *storageSeg = osGetSegment(app);
+
+    // sanity check
+    if (segDataSize >= SEG_SIZE_MAX)
+        return false;
+
+    // physical limits check
+    if (osSegmentSizeAlignedWithFooter(segDataSize) + sizeof(struct Segment) > totalSize)
+        return false;
+
+    // available space check: we could truncate size, instead of disallowing it,
+    // but we know that we performed validation on the size before, in *Create call,
+    // and it was fine, so this must be a programming error, and so we fail.
+    // on a side note: size may grow or shrink compared to original estimate.
+    // typically it shrinks, since we skip some header info and padding, as well
+    // as signature blocks, but it is possible that at some point we may produce
+    // more data for some reason. At that time the logic here may need to change
+    if (osSegmentSizeGetNext(storageSeg, segDataSize) > (struct Segment*)end)
+        return false;
+
+    seg.state = segState;
+    osSegmentSetSize(&seg, segDataSize);
+
+    ret = osWriteShared((uint8_t*)storageSeg, (uint8_t*)&seg, sizeof(seg));
+
+    footerLen = (-fullSize) & 3;
+    memset(footer, 0x00, footerLen);
+
+#ifdef SEGMENT_CRC_SUPPORT
+    struct SegmentFooter segFooter {
+        .crc = ~crc32(storageSeg, fullSize, ~0),
+    };
+    memcpy(&footer[footerLen], &segFooter, sizeof(segFooter));
+    footerLen += sizeof(segFooter);
+#endif
+
+    if (ret && footerLen)
+        ret = osWriteShared((uint8_t*)storageSeg + fullSize, footer, footerLen);
+
+    return ret;
+}
+
+bool osAppWipeData(struct AppHdr *app)
+{
+    struct Segment *seg = osGetSegment(app);
+    int32_t size = osSegmentGetSize(seg);
+    uint8_t *p = (uint8_t*)app;
+    uint32_t state = osSegmentGetState(seg);
+    uint8_t buf[256];
+    bool done = true;
+
+    if (!seg || size == SEG_SIZE_INVALID || state == SEG_ST_EMPTY) {
+        osLog(LOG_ERROR, "%s: can't erase segment: app=%p; seg=%p"
+                         "; size=%" PRIu32
+                         "; state=%" PRIu32
+                         "\n",
+                         __func__, app, seg, size, state);
+        return false;
+    }
+
+    size = osSegmentSizeAlignedWithFooter(size);
+
+    memset(buf, 0, sizeof(buf));
+    while (size > 0) {
+        uint32_t flashSz = size > sizeof(buf) ? sizeof(buf) : size;
+        // keep trying to zero-out stuff even in case of intermittent failures.
+        // flash write may occasionally fail on some byte, but it is not good enough
+        // reason to not rewrite other bytes
+        bool res = osWriteShared(p, buf, flashSz);
+        done = done && res;
+        size -= flashSz;
+        p += flashSz;
+    }
+
+    return done;
+}
+
+static inline bool osAppIsValid(const struct AppHdr *app)
+{
+    return app->hdr.magic == APP_HDR_MAGIC &&
+           app->hdr.fwVer == APP_HDR_VER_CUR &&
+           (app->hdr.fwFlags & FL_APP_HDR_APPLICATION) != 0 &&
+           app->hdr.payInfoType == LAYOUT_APP;
+}
+
+static bool osExtAppIsValid(const struct AppHdr *app, uint32_t len)
+{
+    //TODO: when CRC support is ready, add CRC check here
+    return  osAppIsValid(app) &&
+            len >= sizeof(*app) &&
+            osAppSegmentGetState(app) == SEG_ST_VALID &&
+            !(app->hdr.fwFlags & FL_APP_HDR_INTERNAL);
+}
+
+static bool osIntAppIsValid(const struct AppHdr *app)
+{
+    return  osAppIsValid(app) &&
+            osAppSegmentGetState(app) == SEG_STATE_INVALID &&
+            (app->hdr.fwFlags & FL_APP_HDR_INTERNAL) != 0;
+}
+
+static inline bool osExtAppErase(const struct AppHdr *app)
+{
+    return osAppSegmentSetState(app, SEG_ST_ERASED);
+}
+
 static struct Task *osLoadApp(const struct AppHdr *app) {
     struct Task *task;
 
     task = osAllocTask();
     if (!task) {
-        osLog(LOG_WARN, "External app id %016" PRIX64 " @ %p cannot be used as too many apps already exist.\n", app->appId, app);
+        osLog(LOG_WARN, "External app id %016" PRIX64 " @ %p cannot be used as too many apps already exist.\n", app->hdr.appId, app);
         return NULL;
     }
-    task->appHdr = app;
-    bool done = app->marker == APP_HDR_MARKER_INTERNAL ?
-                cpuInternalAppLoad(task->appHdr, &task->platInfo) :
-                cpuAppLoad(task->appHdr, &task->platInfo);
+    task->app = app;
+    bool done = (app->hdr.fwFlags & FL_APP_HDR_INTERNAL) ?
+                cpuInternalAppLoad(task->app, &task->platInfo) :
+                cpuAppLoad(task->app, &task->platInfo);
 
     if (!done) {
-        osLog(LOG_WARN, "App @ %p ID %016" PRIX64 " failed to load\n", app, app->appId);
+        osLog(LOG_WARN, "App @ %p ID %016" PRIX64 " failed to load\n", app, app->hdr.appId);
         osFreeTask(task);
         task = NULL;
     }
@@ -515,7 +676,7 @@
 static void osUnloadApp(struct Task *task)
 {
     // this is called on task that has stopped running, or had never run
-    cpuAppUnload(task->appHdr, &task->platInfo);
+    cpuAppUnload(task->app, &task->platInfo);
     osFreeTask(task);
 }
 
@@ -532,7 +693,7 @@
         done = osTaskInit(task);
 
         if (!done) {
-            osLog(LOG_WARN, "App @ %p ID %016" PRIX64 "failed to init\n", task->appHdr, task->appHdr->appId);
+            osLog(LOG_WARN, "App @ %p ID %016" PRIX64 "failed to init\n", task->app, task->app->hdr.appId);
             osUnloadApp(task);
         } else {
             osAddTask(task);
@@ -561,16 +722,22 @@
     return true;
 }
 
-static bool osExtAppFind(struct ExtAppIterator *it, uint64_t appId)
+static bool osExtAppFind(struct SegmentIterator *it, uint64_t appId)
 {
     uint64_t vendor = APP_ID_GET_VENDOR(appId);
     uint64_t seqId = APP_ID_GET_SEQ_ID(appId);
     uint64_t curAppId;
     const struct AppHdr *app;
+    const struct Segment *seg;
 
-    while (osExtAppIteratorNext(it)) {
-        app = it->app;
-        curAppId = app->appId;
+    while (osSegmentIteratorNext(it)) {
+        seg = it->seg;
+        if (seg->state == SEG_ST_EMPTY)
+            break;
+        if (seg->state != SEG_ST_VALID)
+            continue;
+        app = osSegmentGetData(seg);
+        curAppId = app->hdr.appId;
 
         if ((vendor == APP_VENDOR_ANY || vendor == APP_ID_GET_VENDOR(curAppId)) &&
             (seqId == APP_SEQ_ID_ANY || seqId == APP_ID_GET_SEQ_ID(curAppId)))
@@ -583,26 +750,26 @@
 static uint32_t osExtAppStopEraseApps(uint64_t appId, bool doErase)
 {
     const struct AppHdr *app;
-    uint32_t len;
+    int32_t len;
     struct Task *task;
-    struct ExtAppIterator it;
+    struct SegmentIterator it;
     uint32_t stopCount = 0;
     uint32_t eraseCount = 0;
     uint32_t appCount = 0;
     uint32_t taskCount = 0;
     struct MgmtStatus stat = { .value = 0 };
 
-    osExtAppIteratorInit(&it);
+    osSegmentIteratorInit(&it);
     while (osExtAppFind(&it, appId)) {
-        app = it.app;
-        len = it.appLen;
+        app = osSegmentGetData(it.seg);
+        len = osSegmentGetSize(it.seg);
         if (!osExtAppIsValid(app, len))
             continue;
         appCount++;
-        task = osTaskFindByAppID(app->appId);
+        task = osTaskFindByAppID(app->hdr.appId);
         if (task)
             taskCount++;
-        if (task && task->appHdr == app && app->marker == APP_HDR_MARKER_VALID) {
+        if (task && task->app == app) {
             if (osStopTask(task))
                 stopCount++;
             else
@@ -629,22 +796,48 @@
     return osExtAppStopEraseApps(appId, true);
 }
 
+static void osScanExternal()
+{
+    struct SegmentIterator it;
+    osSegmentIteratorInit(&it);
+    while (osSegmentIteratorNext(&it)) {
+        switch (osSegmentGetState(it.seg)) {
+        case SEG_ST_EMPTY:
+            // everything looks good
+            osLog(LOG_INFO, "External area is good\n");
+            return;
+        case SEG_ST_ERASED:
+        case SEG_ST_VALID:
+            // this is valid stuff, ignore
+            break;
+        case SEG_ST_RESERVED:
+        default:
+            // something is wrong: erase everything
+            osLog(LOG_ERROR, "External area is damaged. Erasing\n");
+            osEraseShared();
+            return;
+        }
+    }
+}
+
 uint32_t osExtAppStartApps(uint64_t appId)
 {
     const struct AppHdr *app;
-    size_t len;
-    struct ExtAppIterator it;
-    struct ExtAppIterator checkIt;
+    int32_t len;
+    struct SegmentIterator it;
+    struct SegmentIterator checkIt;
     uint32_t startCount = 0;
     uint32_t eraseCount = 0;
     uint32_t appCount = 0;
     uint32_t taskCount = 0;
     struct MgmtStatus stat = { .value = 0 };
 
-    osExtAppIteratorInit(&it);
+    osScanExternal();
+
+    osSegmentIteratorInit(&it);
     while (osExtAppFind(&it, appId)) {
-        app = it.app;
-        len = it.appLen;
+        app = osSegmentGetData(it.seg);
+        len = osSegmentGetSize(it.seg);
 
         // skip erased or malformed apps
         if (!osExtAppIsValid(app, len))
@@ -653,13 +846,13 @@
         appCount++;
         checkIt = it;
         // find the most recent copy
-        while (osExtAppFind(&checkIt, app->appId)) {
+        while (osExtAppFind(&checkIt, app->hdr.appId)) {
             if (osExtAppErase(app)) // erase the old one, so we skip it next time
                 eraseCount++;
-            app = checkIt.app;
+            app = osSegmentGetData(checkIt.seg);
         }
 
-        if (osTaskFindByAppID(app->appId)) {
+        if (osTaskFindByAppID(app->hdr.appId)) {
             // this either the most recent external app with the same ID,
             // or internal app with the same id; in both cases we do nothing
             taskCount++;
@@ -701,21 +894,25 @@
     /* first enum all internal apps, making sure to check for dupes */
     osLog(LOG_DEBUG, "Starting internal apps...\n");
     for (i = 0, app = platGetInternalAppList(&nApps); i < nApps; i++, app++) {
-        if (app->fmtVer != APP_HDR_VER_CUR) {
-            osLog(LOG_WARN, "Unexpected app @ %p ID %016" PRIX64 "header version: %d\n",
-                  app, app->appId, app->fmtVer);
+        if (!osIntAppIsValid(app)) {
+            osLog(LOG_WARN, "Invalid internal app @ %p ID %016" PRIX64
+                            "header version: %" PRIu16
+                            "\n",
+                            app, app->hdr.appId, app->hdr.fwVer);
             continue;
         }
 
-        if (app->marker != APP_HDR_MARKER_INTERNAL) {
-            osLog(LOG_WARN, "Invalid marker on internal app: [%p]=0x%04X ID %016" PRIX64 "; ignored\n",
-                  app, app->marker, app->appId);
+        if (!(app->hdr.fwFlags & FL_APP_HDR_INTERNAL)) {
+            osLog(LOG_WARN, "Internal app is not marked: [%p]: flags: 0x%04" PRIX16
+                            "; ID: %016" PRIX64
+                            "; ignored\n",
+                            app, app->hdr.fwFlags, app->hdr.appId);
             continue;
         }
-        if ((task = osTaskFindByAppID(app->appId))) {
+        if ((task = osTaskFindByAppID(app->hdr.appId))) {
             osLog(LOG_WARN, "Internal app ID %016" PRIX64
                             "@ %p attempting to update internal app @ %p; app @%p ignored.\n",
-                            app->appId, app, task->appHdr, app);
+                            app->hdr.appId, app, task->app, app);
             continue;
         }
         if (osStartApp(app))
@@ -1005,7 +1202,7 @@
     struct Task *task;
 
     for_each_task(&mTasks, task) {
-        if (task->appHdr && task->appHdr->appId == appId) {
+        if (task->app && task->app->hdr.appId == appId) {
             *tid = task->tid;
             return true;
         }
@@ -1020,11 +1217,11 @@
     struct Task *task;
 
     for_each_task(&mTasks, task) {
-        const struct AppHdr *app = task->appHdr;
-        if (app && app->appId == appId) {
+        const struct AppHdr *app = task->app;
+        if (app && app->hdr.appId == appId) {
             *appIdx = i;
-            *appVer = app->appVer;
-            *appSize = app->rel_end;
+            *appVer = app->hdr.appVer;
+            *appSize = app->sect.rel_end;
             return true;
         }
         i++;
@@ -1042,10 +1239,10 @@
         if (i != appIdx) {
             ++i;
         } else {
-            const struct AppHdr *app = task->appHdr;
-            *appId = app->appId;
-            *appVer = app->appVer;
-            *appSize = app->rel_end;
+            const struct AppHdr *app = task->app;
+            *appId = app->hdr.appId;
+            *appVer = app->hdr.appVer;
+            *appSize = app->sect.rel_end;
             return true;
         }
     }
@@ -1119,6 +1316,3 @@
 };
 
 #endif
-
-
-PREPOPULATED_ENCR_KEY(google_encr_key, ENCR_KEY_GOOGLE_PREPOPULATED, 0xf1, 0x51, 0x9b, 0x2e, 0x26, 0x6c, 0xeb, 0xe7, 0xd6, 0xd6, 0x0d, 0x17, 0x11, 0x94, 0x99, 0x19, 0x1c, 0xfb, 0x71, 0x56, 0x53, 0xf7, 0xe0, 0x7d, 0x90, 0x07, 0x53, 0x68, 0x10, 0x95, 0x1b, 0x70);
diff --git a/firmware/src/sha2.c b/firmware/src/sha2.c
deleted file mode 100644
index cc6e76b..0000000
--- a/firmware/src/sha2.c
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2016 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 <sha2.h>
-
-
-void sha2init(struct Sha2state *state)
-{
-    state->h[0] = 0x6a09e667;
-    state->h[1] = 0xbb67ae85;
-    state->h[2] = 0x3c6ef372;
-    state->h[3] = 0xa54ff53a;
-    state->h[4] = 0x510e527f;
-    state->h[5] = 0x9b05688c;
-    state->h[6] = 0x1f83d9ab;
-    state->h[7] = 0x5be0cd19;
-    state->msgLen = 0;
-    state->bufBytesUsed = 0;
-}
-
-#ifdef ARM
-
-    #define STRINFIGY2(b) #b
-    #define STRINGIFY(b) STRINFIGY2(b)
-    #define ror(v, b) ({uint32_t ret; if (b) asm("ror %0, #" STRINGIFY(b) :"=r"(ret):"0"(v)); else ret = v; ret;})
-
-#else
-
-    inline static uint32_t ror(uint32_t val, uint32_t by)
-    {
-        if (!by)
-            return val;
-
-        val = (val >> by) | (val << (32 - by));
-
-        return val;
-    }
-
-#endif
-
-
-static void sha2processBlock(struct Sha2state *state)
-{
-    static const uint32_t k[] = {
-        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
-        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
-        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
-        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
-        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
-        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
-        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
-        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
-    };
-    uint32_t i, a, b, c, d, e, f, g, h;
-
-    //byteswap the input (if we're on a little endian cpu, as we are)
-    for (i = 0; i < SHA2_BLOCK_SIZE / sizeof(uint32_t); i++)
-        state->w[i] = __builtin_bswap32(state->w[i]);
-
-    //expand input
-    for (;i < SHA2_WORDS_STATE_SIZE; i++) {
-        uint32_t s0 = ror(state->w[i-15], 7) ^ ror(state->w[i-15], 18) ^ (state->w[i-15] >> 3);
-        uint32_t s1 = ror(state->w[i-2], 17) ^ ror(state->w[i-2], 19) ^ (state->w[i-2] >> 10);
-        state->w[i] = state->w[i - 16] + s0 + state->w[i - 7] + s1;
-    }
-
-    //init working variables
-    a = state->h[0];
-    b = state->h[1];
-    c = state->h[2];
-    d = state->h[3];
-    e = state->h[4];
-    f = state->h[5];
-    g = state->h[6];
-    h = state->h[7];
-
-    //64 rounds
-    for (i = 0; i < 64; i++) {
-        uint32_t s1 = ror(e, 6) ^ ror(e, 11) ^ ror(e, 25);
-        uint32_t ch = (e & f) ^ ((~e) & g);
-        uint32_t temp1 = h + s1 + ch + k[i] + state->w[i];
-        uint32_t s0 = ror(a, 2) ^ ror(a, 13) ^ ror(a, 22);
-        uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
-        uint32_t temp2 = s0 + maj;
-
-        h = g;
-        g = f;
-        f = e;
-        e = d + temp1;
-        d = c;
-        c = b;
-        b = a;
-        a = temp1 + temp2;
-    }
-
-    //put result back into state
-    state->h[0] += a;
-    state->h[1] += b;
-    state->h[2] += c;
-    state->h[3] += d;
-    state->h[4] += e;
-    state->h[5] += f;
-    state->h[6] += g;
-    state->h[7] += h;
-}
-
-void sha2processBytes(struct Sha2state *state, const void *bytes, uint32_t numBytes)
-{
-    const uint8_t *inBytes = (const uint8_t*)bytes;
-
-    state->msgLen += numBytes;
-    while (numBytes) {
-        uint32_t bytesToCopy;
-
-        //step 1: copy data into state if there is space & there is data
-        bytesToCopy = numBytes;
-        if (bytesToCopy > SHA2_BLOCK_SIZE - state->bufBytesUsed)
-            bytesToCopy = SHA2_BLOCK_SIZE - state->bufBytesUsed;
-        memcpy(state->b + state->bufBytesUsed, inBytes, bytesToCopy);
-        inBytes += bytesToCopy;
-        numBytes -= bytesToCopy;
-        state->bufBytesUsed += bytesToCopy;
-
-        //step 2: if there is a full block, process it
-        if (state->bufBytesUsed == SHA2_BLOCK_SIZE) {
-            sha2processBlock(state);
-            state->bufBytesUsed = 0;
-        }
-    }
-}
-
-const uint32_t* sha2finish(struct Sha2state *state)
-{
-    uint8_t appendend = 0x80;
-    uint64_t dataLenInBits = state->msgLen * 8;
-    uint32_t i;
-
-    //append the one
-    sha2processBytes(state, &appendend, 1);
-
-    //append the zeroes
-    appendend = 0;
-    while (state->bufBytesUsed != 56)
-        sha2processBytes(state, &appendend, 1);
-
-    //append the length in bits (we can safely write into state since we're sure where to write to (we're definitely 56-bytes into a block)
-    for (i = 0; i < 8; i++, dataLenInBits >>= 8)
-        state->b[63 - i] = dataLenInBits;
-
-    //process last block
-    sha2processBlock(state);
-
-    //return pointer to hash
-    return state->h;
-}
-
-
-
-
-
-
diff --git a/firmware/src/softcrc.c b/firmware/src/softcrc.c
deleted file mode 100644
index 4a37f7e..0000000
--- a/firmware/src/softcrc.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2016 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 <stdint.h>
-#include <crc.h>
-
-/* this implements crc32 as crc.h defines it. It is not a normal CRC by any measure, so be careful with it */
-
-static const uint32_t crctab[] =
-{
-        0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
-        0x130476DC ,0x17C56B6B ,0x1A864DB2 ,0x1E475005,
-        0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
-        0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
-};
-
-static uint32_t crcOneWord(uint32_t crc, uint32_t data, int cnt)
-{
-        uint32_t i;
-
-        crc = crc ^ data;
-        for (i = 0; i < cnt; i++)
-                crc = (crc << 4) ^ crctab[crc >> 28];
-
-        return crc;
-}
-
-uint32_t crc32(const void *buf, size_t size, uint32_t crc)
-{
-        const uint32_t *data32 = (const uint32_t *)buf;
-        const uint8_t *data8;
-        uint32_t word, i;
-
-        // word by word crc32
-        for (i = 0; i < size / 4; i++)
-                crc = crcOneWord(crc, *data32++, 8);
-
-        data8 = (const uint8_t*)data32;
-
-        // zero pad last word if required
-        if (size & 0x3) {
-                for (i *= 4, word = 0; i < size; i++)
-                        word |= (*data8++) << ((i & 0x3) * 8);
-                crc = crcOneWord(crc, word, 8);
-        }
-
-        return crc;
-}