Compare commits

...

35 Commits
2.2.0 ... 2.2.2

Author SHA1 Message Date
0dfda6fce2 Merge pull request #185 from solokeys/wallet-api
Fix FIDO2 get assertion issues with "custom" requests
2019-04-24 20:25:19 -04:00
09b73d694f fix build 2019-04-24 19:33:26 -04:00
9ab5e761c3 Update tinycbor 2019-04-24 19:04:33 -04:00
b3604f49ba use ctap2 instead of client 2019-04-24 19:00:32 -04:00
6ae1cd3865 remove not-useful logs 2019-04-24 18:36:36 -04:00
f9d3b9561d test that getassertions are correct for solo ext 2019-04-24 18:27:11 -04:00
ec98af115f restore button in ctap_make_auth_data 2019-04-24 16:54:26 -04:00
fecf258116 disable wallet by default 2019-04-24 16:44:25 -04:00
437f691d12 Update solo.c 2019-04-24 16:41:22 -04:00
55aadfd78e add delay implementation 2019-04-24 11:45:41 -04:00
813eb97d2f reuse memory for allow_list of creds 2019-04-24 11:45:30 -04:00
32afdccfb3 Rebump 2019-04-24 17:41:08 +02:00
41ae0e4a2c Update application.mk 2019-04-24 00:16:19 -04:00
b0baace2e7 move custom credid to different location 2019-04-24 00:15:32 -04:00
6ff4200f5d unbump 2019-04-23 21:58:26 -04:00
1fab0b8f1f add wallet api in as compile option 2019-04-23 21:57:50 -04:00
ce96fffddd add info to authData for ext reqs 2019-04-23 21:57:27 -04:00
4fb25e165a remove old web assets 2019-04-23 21:15:11 -04:00
8fc0da7934 move args to device_init 2019-04-23 21:12:40 -04:00
494e856198 Merge pull request #131 from rgerganov/hidg
Add support for hidg devices on Linux
2019-04-23 20:22:33 -04:00
472b094acb bump 2019-04-23 20:21:19 -04:00
e0ce23034f Merge pull request #180 from solokeys/test_pin_fixes
Test pin fixes
2019-04-23 19:58:44 -04:00
5f3974a4e6 Merge pull request #182 from solokeys/nfc_adjustments
Nfc adjustments
2019-04-23 19:42:14 -04:00
26adac1730 size optimize tinycbor 2019-04-23 19:17:18 -04:00
eab8b81c95 include nfc in user presence test 2019-04-23 14:05:18 -04:00
325396d518 dont change clock freq if powered 2019-04-23 13:54:23 -04:00
6d04c86018 update lowFreq flag correctly 2019-04-23 13:54:10 -04:00
56d6624e4e Test correct alg parameter #179 2019-04-22 16:26:26 -04:00
3094c87b0a Test empty pinAuth in MC and GA #179 2019-04-22 16:25:08 -04:00
212f98e384 Merge pull request #179 from agl/pinfixes
Couple of fixes for PIN support
2019-04-22 16:11:34 -04:00
73f538dd0e Fix COSE type of key-agreement keys.
The key-agreement keys in the PIN protocol use COSE type -25. I'm not
sure if that's written down anywhere, but it's what everything else does
and it's an ECDH type rather than an ECDSA type.
2019-04-20 16:45:04 -07:00
a5f794c0ff Handle empty pinAuth fields.
CTAP2 specifies that an empty pinAuth field is special: it indicates
that the device should block for touch, i.e. it's just a way of letting
a user select from multiple authenticators[1].

This change handles empty pinAuth fields in GetAssertion and
MakeCredential commands.

[1] https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential
2019-04-20 16:26:32 -07:00
f28cf9c6d0 new release 2019-04-18 20:41:18 -07:00
6a288243c1 Add Makefile for installing a HID gadget
The Makefile has targets for building, installing and uninstalling a HID
gadget which acts as FIDO2 authenticator.

Tested on Ubuntu 18.04 but should work on other distros as well. The
only assumption being made is that kernel source is available at
/usr/src/linux-source-$(KERNEL_VERSION).tar.bz2. A possible improvement
would be to have a configure script which finds the correct kernel
source archive.
2019-04-16 10:54:49 +03:00
955d4f76ef Add support for hidg devices on Linux
There is a HID gadget driver on Linux which provides emulation of USB
HID devices. This could be very useful for testing the Solo firmware
without actual hardware, using only a Linux box.

This patch adds a command line argument which specifies whether the
existing UDP backing should be used or the new one which reads and
writes to /dev/hidg0.

Testing done:
 1. Created HID device with configfs
 2. Started "./main -b hidg" as root
 3. Successfully executed Webauthn registration and authentication on
 the same Linux machine

Closes: #122
2019-04-16 10:54:46 +03:00
37 changed files with 406 additions and 13070 deletions

View File

@ -33,3 +33,12 @@ WARNING: This update may break previous registrations! This is because we fixed
- Adds hmac-secret extension support. This extension is used for generating 32 or 64 byte symmetric keys using parameters from the platform and secrets on the authenticator. It's used by Windows Hello - - for offline authentication.
- Fix bug in FIDO auth, where setting the pin requires all previous registrations to use pin. Only UV bit needs to be cleared.
- Slightly change serial emulation USB descriptor to make it less abused by Linux Modem Manager.
## [2.2.0] - 2019-04-17
- Fixes the ordering of keys encoded in CBOR maps to be canonical ordering. They previously were not ordered in any particular way and caused issues for Chrome. #170
- Fixes CTAP2 implementation to accept credential IDs created by the CTAP1 implementation. So registering with U2F and later authenticating with FIDO2 should work.
## [2.2.1] - 2019-04-23
- This minor release fixes some small issues. #179, #182

View File

@ -32,7 +32,7 @@ VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS)
CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS) -g
INCLUDES = -I./tinycbor/src -I./crypto/sha256 -I./crypto/micro-ecc/ -Icrypto/tiny-AES-c/ -I./fido2/ -I./pc -I./fido2/extensions
INCLUDES += -I./crypto/cifra/src
@ -53,7 +53,7 @@ tinycbor/Makefile crypto/tiny-AES-c/aes.c:
cbor: $(LIBCBOR)
$(LIBCBOR):
cd tinycbor/ && $(MAKE) clean && $(MAKE) -j8
cd tinycbor/ && $(MAKE) clean && $(MAKE) LDFLAGS='' -j8
version:
@git describe

View File

@ -1 +1 @@
2.1.0
2.2.1

View File

@ -16,7 +16,7 @@
#define COSE_KEY_KTY_EC2 2
#define COSE_KEY_CRV_P256 1
#define COSE_ALG_ES256 -7
#define COSE_ALG_ES256 -7
#define COSE_ALG_ECDH_ES_HKDF_256 -25
#endif

View File

@ -36,16 +36,7 @@ AuthenticatorState STATE;
static void ctap_reset_key_agreement();
static struct {
CTAP_authDataHeader authData;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE-1];
uint8_t lastcmd;
uint32_t count;
uint32_t index;
uint32_t time;
uint8_t user_verified;
} getAssertionState;
struct _getAssertionState getAssertionState;
uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
{
@ -388,7 +379,6 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
crypto_aes256_encrypt(output, ext->hmac_secret.saltLen);
// output
printf1(TAG_GREEN, "have %d bytes for Extenstions encoder\r\n",*ext_encoder_buf_size);
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
{
CborEncoder hmac_secret_map;
@ -436,6 +426,8 @@ static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
{
if (cred->type == PUB_KEY_CRED_CTAP1)
return U2F_KEY_HANDLE_SIZE;
if (cred->type == PUB_KEY_CRED_CUSTOM)
return getAssertionState.customCredIdSize;
return sizeof(CredentialId);
}
@ -460,18 +452,13 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(authData->head.rpIdHash);
printf1(TAG_RED, "rpId: "); dump_hex1(TAG_RED, rp->id, rp->size);
printf1(TAG_RED, "hash: "); dump_hex1(TAG_RED, authData->head.rpIdHash, 32);
count = auth_data_update_count(&authData->head);
device_set_status(CTAPHID_STATUS_UPNEEDED);
// if NFC - not need to click a button
int but = 1;
if(!device_is_nfc())
{
but = ctap_user_presence_test();
}
int but;
but = ctap_user_presence_test();
if (!but)
{
@ -672,12 +659,17 @@ int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * d
return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
break;
case PUB_KEY_CRED_CTAP1:
printf1(TAG_CTAP,"PUB_KEY_CRED_CTAP1\r\n");
crypto_sha256_init();
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(rpIdHash);
return u2f_authenticate_credential((struct u2f_key_handle *)&desc->credential.id, rpIdHash);
break;
case PUB_KEY_CRED_CUSTOM:
return is_extension_request(getAssertionState.customCredId, getAssertionState.customCredIdSize);
break;
default:
printf1(TAG_ERR, "PUB_KEY_CRED_UNKNOWN %x\r\n",desc->type);
break;
}
return 0;
@ -702,6 +694,14 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
printf2(TAG_ERR,"error, parse_make_credential failed\n");
return ret;
}
if (MC.pinAuthEmpty)
{
if (!ctap_user_presence_test())
{
return CTAP2_ERR_OPERATION_DENIED;
}
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
{
printf2(TAG_ERR,"error, required parameter(s) for makeCredential are missing\n");
@ -738,9 +738,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
check_retr(ret);
printf1(TAG_GREEN, "checking credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &excl_cred->credential.id, sizeof(CredentialId));
// DELETE
// crypto_aes256_reset_iv(NULL);
// crypto_aes256_decrypt((uint8_t*)& excl_cred->credential.enc, CREDENTIAL_ENC_SIZE);
if (ctap_authenticate_credential(&MC.rp, excl_cred))
{
printf1(TAG_MC, "Cred %d failed!\r\n",i);
@ -861,7 +859,6 @@ uint8_t ctap_add_user_entity(CborEncoder * map, CTAP_userEntity * user)
ret = cbor_encoder_create_map(map, &entity, 1);
check_ret(ret);
printf1(TAG_GREEN,"id_size: %d\r\n", user->id_size);
{
ret = cbor_encode_text_string(&entity, "id", 2);
check_ret(ret);
@ -1103,7 +1100,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
}
check_ret(ret);
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
// if only one account for this RP, null out the user details
if (!getAssertionState.user_verified)
@ -1124,6 +1120,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
{
CTAP_getAssertion GA;
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader) + 80];
int ret = ctap_parse_get_assertion(&GA,request,length);
@ -1133,6 +1130,14 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
return ret;
}
if (GA.pinAuthEmpty)
{
if (!ctap_user_presence_test())
{
return CTAP2_ERR_OPERATION_DENIED;
}
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if (GA.pinAuthPresent)
{
ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash);
@ -1155,16 +1160,21 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
printf1(TAG_GA, "ALLOW_LIST has %d creds\n", GA.credLen);
int validCredCount = ctap_filter_invalid_credentials(&GA);
if (validCredCount > 1)
if (validCredCount == 0)
{
printf2(TAG_ERR,"Error, no authentic credential\n");
return CTAP2_ERR_NO_CREDENTIALS;
}
else if (validCredCount > 1)
{
map_size += 1;
}
if (GA.creds[validCredCount - 1].credential.user.id_size)
{
map_size += 1;
}
if (GA.extensions.hmac_secret_present == EXT_HMAC_SECRET_PARSED)
{
printf1(TAG_GA, "hmac-secret is present\r\n");
@ -1173,12 +1183,6 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encoder_create_map(encoder, &map, map_size);
check_ret(ret);
if (validCredCount == 0)
{
printf2(TAG_ERR,"Error, no authentic credential\n");
return CTAP2_ERR_NO_CREDENTIALS;
}
// if only one account for this RP, null out the user details
if (validCredCount < 2 || !getAssertionState.user_verified)
{
@ -1202,8 +1206,14 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
#ifdef ENABLE_U2F_EXTENSIONS
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
{
memset(auth_data_buf,0,sizeof(CTAP_authDataHeader));
auth_data_buf_sz = sizeof(CTAP_authDataHeader);
crypto_sha256_init();
crypto_sha256_update(GA.rp.id, GA.rp.size);
crypto_sha256_final(((CTAP_authData *)auth_data_buf)->head.rpIdHash);
((CTAP_authData *)auth_data_buf)->head.flags = (1 << 0);
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 2);
}
else
#endif
@ -1460,7 +1470,7 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encode_int(&map, RESP_keyAgreement);
check_ret(ret);
ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ES256);
ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ECDH_ES_HKDF_256);
check_retr(ret);
break;
@ -1655,7 +1665,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
else
{
printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION. lastcmd == 0x%02x\n", getAssertionState.lastcmd);
dump_hex1(TAG_GREEN, (uint8_t*)&getAssertionState, sizeof(getAssertionState));
status = CTAP2_ERR_NOT_ALLOWED;
}
break;

View File

@ -113,6 +113,7 @@
#define PUB_KEY_CRED_PUB_KEY 0x01
#define PUB_KEY_CRED_CTAP1 0x41
#define PUB_KEY_CRED_CUSTOM 0x42
#define PUB_KEY_CRED_UNKNOWN 0x3F
#define CREDENTIAL_IS_SUPPORTED 1
@ -243,6 +244,11 @@ typedef struct
uint8_t pinAuth[16];
uint8_t pinAuthPresent;
// pinAuthEmpty is true iff an empty bytestring was provided as pinAuth.
// This is exclusive with |pinAuthPresent|. It exists because an empty
// pinAuth is a special signal to block for touch. See
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorMakeCredential
uint8_t pinAuthEmpty;
int pinProtocol;
CTAP_extensions extensions;
@ -266,9 +272,14 @@ typedef struct
uint8_t pinAuth[16];
uint8_t pinAuthPresent;
// pinAuthEmpty is true iff an empty bytestring was provided as pinAuth.
// This is exclusive with |pinAuthPresent|. It exists because an empty
// pinAuth is a special signal to block for touch. See
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorGetAssertion
uint8_t pinAuthEmpty;
int pinProtocol;
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
CTAP_credentialDescriptor * creds;
uint8_t allowListPresent;
CTAP_extensions extensions;
@ -292,6 +303,19 @@ typedef struct
} CTAP_clientPin;
struct _getAssertionState {
CTAP_authDataHeader authData;
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
uint8_t lastcmd;
uint32_t count;
uint32_t index;
uint32_t time;
uint8_t user_verified;
uint8_t customCredId[256];
uint8_t customCredIdSize;
};
void ctap_response_init(CTAP_RESPONSE * resp);
uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp);

View File

@ -16,6 +16,7 @@
#include "util.h"
#include "log.h"
extern struct _getAssertionState getAssertionState;
void _check_ret(CborError ret, int line, const char * filename)
{
@ -136,7 +137,6 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
return CTAP2_ERR_LIMIT_EXCEEDED;
}
MC->credInfo.user.id_size = sz;
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->credInfo.user.id_size);
check_ret(ret);
}
else if (strcmp((const char *)key, "name") == 0)
@ -823,14 +823,22 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
ret = parse_options(&map, &MC->credInfo.rk, &MC->uv, &MC->up);
check_retr(ret);
break;
case MC_pinAuth:
case MC_pinAuth: {
printf1(TAG_MC,"CTAP_pinAuth\n");
size_t pinSize;
if (cbor_value_get_type(&map) == CborByteStringType &&
cbor_value_get_string_length(&map, &pinSize) == CborNoError &&
pinSize == 0)
{
MC->pinAuthEmpty = 1;
break;
}
ret = parse_fixed_byte_string(&map, MC->pinAuth, 16);
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
{
check_retr(ret);
}
else
{
@ -838,6 +846,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
}
MC->pinAuthPresent = 1;
break;
}
case MC_pinProtocol:
printf1(TAG_MC,"CTAP_pinProtocol\n");
if (cbor_value_get_type(&map) == CborIntegerType)
@ -874,6 +883,8 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
size_t buflen;
char type[12];
CborValue val;
cred->type = 0;
if (cbor_value_get_type(arr) != CborMapType)
{
printf2(TAG_ERR,"Error, CborMapType expected in credential\n");
@ -890,8 +901,8 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
}
buflen = sizeof(CredentialId);
cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential.id, &buflen, NULL);
ret = cbor_value_copy_byte_string(&val, (uint8_t*)&cred->credential.id, &buflen, NULL);
if (buflen == U2F_KEY_HANDLE_SIZE)
{
printf2(TAG_PARSE,"CTAP1 credential\n");
@ -899,8 +910,13 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
}
else if (buflen != sizeof(CredentialId))
{
printf2(TAG_ERR,"Ignoring credential is incorrect length\n");
printf2(TAG_ERR,"Ignoring credential is incorrect length, treating as custom\n");
cred->type = PUB_KEY_CRED_CUSTOM;
buflen = 256;
ret = cbor_value_copy_byte_string(&val, getAssertionState.customCredId, &buflen, NULL);
getAssertionState.customCredIdSize = buflen;
}
check_ret(ret);
ret = cbor_value_map_find_value(arr, "type", &val);
check_ret(ret);
@ -917,7 +933,7 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
if (strncmp(type, "public-key",11) == 0)
{
if (PUB_KEY_CRED_CTAP1 != cred->type)
if (0 == cred->type)
{
cred->type = PUB_KEY_CRED_PUB_KEY;
}
@ -985,6 +1001,8 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
CborValue it,map;
memset(GA, 0, sizeof(CTAP_getAssertion));
GA->creds = getAssertionState.creds; // Save stack memory
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_ret(ret);
@ -1055,9 +1073,18 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
ret = parse_options(&map, &GA->rk, &GA->uv, &GA->up);
check_retr(ret);
break;
case GA_pinAuth:
case GA_pinAuth: {
printf1(TAG_GA,"CTAP_pinAuth\n");
size_t pinSize;
if (cbor_value_get_type(&map) == CborByteStringType &&
cbor_value_get_string_length(&map, &pinSize) == CborNoError &&
pinSize == 0)
{
GA->pinAuthEmpty = 1;
break;
}
ret = parse_fixed_byte_string(&map, GA->pinAuth, 16);
if (CTAP1_ERR_INVALID_LENGTH != ret) // damn microsoft
{
@ -1073,6 +1100,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
GA->pinAuthPresent = 1;
break;
}
case GA_pinProtocol:
printf1(TAG_GA,"CTAP_pinProtocol\n");
if (cbor_value_get_type(&map) == CborIntegerType)

View File

@ -9,7 +9,7 @@
#include "storage.h"
void device_init();
void device_init(int argc, char *argv[]);
uint32_t millis();

View File

@ -35,6 +35,28 @@ int extension_needs_atomic_count(uint8_t klen, uint8_t * keyh)
|| ((wallet_request *) keyh)->operation == WalletSign;
}
static uint8_t * output_buffer_ptr;
uint8_t output_buffer_offset;
uint8_t output_buffer_size;
void extension_writeback_init(uint8_t * buffer, uint8_t size)
{
output_buffer_ptr = buffer;
output_buffer_offset = 0;
output_buffer_size = size;
}
void extension_writeback(uint8_t * buf, uint8_t size)
{
if ((output_buffer_offset + size) > output_buffer_size)
{
return;
}
memmove(output_buffer_ptr + output_buffer_offset, buf, size);
output_buffer_offset += size;
}
int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh)
{
int8_t ret = 0;
@ -55,8 +77,6 @@ int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen
u2f_response_writeback((uint8_t *)&ret,1);
#ifdef IS_BOOTLOADER
ret = bootloader_bridge(klen, keyh);
#elif defined(WALLET_EXTENSION)
ret = bridge_u2f_to_wallet(_chal, _appid, klen, keyh);
#else
ret = bridge_u2f_to_solo(sig, keyh, klen);
u2f_response_writeback(sig,72);
@ -82,6 +102,7 @@ int16_t extend_fido2(CredentialId * credid, uint8_t * output)
{
if (is_extension_request((uint8_t*)credid, sizeof(CredentialId)))
{
printf1(TAG_EXT,"IS EXT REQ\r\n");
output[0] = bridge_u2f_to_solo(output+1, (uint8_t*)credid, sizeof(CredentialId));
return 1;
}

View File

@ -9,6 +9,11 @@
#include "u2f.h"
#include "apdu.h"
int16_t bridge_u2f_to_extensions(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
// return 1 if request is a wallet request
int is_extension_request(uint8_t * req, int len);
int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len);
int16_t extend_fido2(CredentialId * credid, uint8_t * output);
@ -17,4 +22,8 @@ int bootloader_bridge(int klen, uint8_t * keyh);
int is_extension_request(uint8_t * kh, int len);
void extension_writeback_init(uint8_t * buffer, uint8_t size);
void extension_writeback(uint8_t * buf, uint8_t size);
#endif /* EXTENSIONS_H_ */

View File

@ -31,12 +31,15 @@
#include "log.h"
#include APP_CONFIG
// output must be at least 71 bytes
int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
{
int8_t ret = 0;
wallet_request * req = (wallet_request *) keyh;
extension_writeback_init(output, 71);
printf1(TAG_WALLET, "u2f-solo [%d]: ", keylen); dump_hex1(TAG_WALLET, keyh, keylen);
@ -61,6 +64,14 @@ int16_t bridge_u2f_to_solo(uint8_t * output, uint8_t * keyh, int keylen)
break;
#ifdef ENABLE_WALLET
case WalletSign:
case WalletRegister:
case WalletPin:
case WalletReset:
return bridge_to_wallet(keyh, keylen);
#endif
default:
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);
ret = CTAP1_ERR_INVALID_COMMAND;

View File

@ -14,8 +14,8 @@
#include "util.h"
#include "storage.h"
#include "device.h"
#include "extensions.h"
#if defined(USING_PC) || defined(IS_BOOTLOADER)
typedef enum
{
MBEDTLS_ECP_DP_NONE = 0,
@ -32,9 +32,7 @@ typedef enum
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
} mbedtls_ecp_group_id;
#else
#include "ecp.h"
#endif
// return 1 if hash is valid, 0 otherwise
@ -70,14 +68,14 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return CTAP2_ERR_NOT_ALLOWED;
}
u2f_response_writeback(KEY_AGREEMENT_PUB,sizeof(KEY_AGREEMENT_PUB));
extension_writeback(KEY_AGREEMENT_PUB,sizeof(KEY_AGREEMENT_PUB));
printf1(TAG_WALLET,"pubkey: "); dump_hex1(TAG_WALLET,KEY_AGREEMENT_PUB,64);
break;
case CP_cmdGetRetries:
printf1(TAG_WALLET,"cmdGetRetries\n");
pinTokenEnc[0] = ctap_leftover_pin_attempts();
u2f_response_writeback(pinTokenEnc,1);
extension_writeback(pinTokenEnc,1);
break;
case CP_cmdSetPin:
@ -145,7 +143,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return ret;
printf1(TAG_WALLET,"pinToken: "); dump_hex1(TAG_WALLET, PIN_TOKEN, 16);
u2f_response_writeback(pinTokenEnc, PIN_TOKEN_SIZE);
extension_writeback(pinTokenEnc, PIN_TOKEN_SIZE);
break;
@ -159,7 +157,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
return 0;
}
int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh)
int16_t bridge_to_wallet(uint8_t * keyh, uint8_t klen)
{
static uint8_t msg_buf[WALLET_MAX_BUFFER];
int reqlen = klen;
@ -259,7 +257,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
crypto_load_external_key(key, keysize);
crypto_ecdsa_sign(args[0], lens[0], sig, MBEDTLS_ECP_DP_SECP256K1);
u2f_response_writeback(sig,64);
extension_writeback(sig,64);
break;
case WalletRegister:
@ -374,39 +372,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
break;
case WalletVersion:
u2f_response_writeback((uint8_t*)WALLET_VERSION, sizeof(WALLET_VERSION)-1);
break;
case WalletRng:
printf1(TAG_WALLET,"WalletRng\n");
if ( ctap_device_locked() )
{
printf1(TAG_ERR,"device locked\n");
ret = CTAP2_ERR_NOT_ALLOWED;
goto cleanup;
}
if ( ctap_is_pin_set() )
{
if ( ! check_pinhash(req->pinAuth, msg_buf, reqlen))
{
printf2(TAG_ERR,"pinAuth is NOT valid\n");
dump_hex1(TAG_ERR,msg_buf,reqlen);
ret = CTAP2_ERR_PIN_AUTH_INVALID;
goto cleanup;
}
}
ret = ctap_generate_rng(sig, 72);
if (ret != 1)
{
printf1(TAG_WALLET,"Rng failed\n");
ret = CTAP2_ERR_PROCESSING;
goto cleanup;
}
ret = 0;
u2f_response_writeback((uint8_t *)sig,72);
break;
default:
printf2(TAG_ERR,"Invalid wallet command: %x\n",req->operation);

View File

@ -87,10 +87,7 @@ typedef enum
} WalletOperation;
int16_t bridge_u2f_to_extensions(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
// return 1 if request is a wallet request
int is_extension_request(uint8_t * req, int len);
int16_t bridge_to_wallet(uint8_t * keyh, uint8_t klen);
void wallet_init();

View File

@ -7,6 +7,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include "cbor.h"
#include "device.h"
@ -19,7 +21,8 @@
#if !defined(TEST)
int main()
int main(int argc, char *argv[])
{
uint8_t hidmsg[64];
uint32_t t1 = 0;
@ -27,32 +30,29 @@ int main()
set_logging_mask(
/*0*/
//TAG_GEN|
//TAG_MC |
//TAG_GA |
//TAG_WALLET |
// TAG_MC |
// TAG_GA |
TAG_WALLET |
TAG_STOR |
//TAG_NFC_APDU |
TAG_NFC |
//TAG_CP |
//TAG_CTAP|
// TAG_CTAP|
//TAG_HID|
//TAG_U2F|
TAG_U2F|
//TAG_PARSE |
//TAG_TIME|
//TAG_DUMP|
// TAG_DUMP|
TAG_GREEN|
TAG_RED|
TAG_EXT|
TAG_ERR
);
device_init();
device_init(argc, argv);
memset(hidmsg,0,sizeof(hidmsg));
// printf1(TAG_GEN,"recv'ing hid msg \n");
while(1)
{

View File

@ -7,6 +7,7 @@
#ifndef SRC_APP_H_
#define SRC_APP_H_
#include <stdbool.h>
#define USING_DEV_BOARD
@ -20,6 +21,8 @@
void printing_init();
extern bool use_udp;
// 0xRRGGBB
#define LED_INIT_VALUE 0x000800
#define LED_WINK_VALUE 0x000008

View File

@ -15,6 +15,7 @@
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include "device.h"
#include "cbor.h"
@ -24,6 +25,8 @@
#define RK_NUM 50
bool use_udp = true;
struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM];
} RK_STORE;
@ -118,12 +121,6 @@ void udp_send(int fd, uint8_t * buf, int size)
}
}
void udp_close(int fd)
{
close(fd);
}
uint32_t millis()
{
@ -134,18 +131,42 @@ uint32_t millis()
}
static int serverfd = 0;
static int fd = 0;
void usbhid_init()
{
// just bridge to UDP for now for pure software testing
serverfd = udp_server();
if (use_udp)
{
fd = udp_server();
}
else
{
fd = open("/dev/hidg0", O_RDWR);
if (fd < 0)
{
perror("hidg open");
exit(1);
}
}
}
// Receive 64 byte USB HID message, don't block, return size of packet, return 0 if nothing
int usbhid_recv(uint8_t * msg)
{
int l = udp_recv(serverfd, msg, HID_MESSAGE_SIZE);
int l = 0;
if (use_udp)
{
l = udp_recv(fd, msg, HID_MESSAGE_SIZE);
}
else
{
l = read(fd, msg, HID_MESSAGE_SIZE); /* Flawfinder: ignore */
if (l < 0)
{
perror("hidg read");
exit(1);
}
}
uint8_t magic_cmd[] = "\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
@ -166,12 +187,23 @@ int usbhid_recv(uint8_t * msg)
// Send 64 byte USB HID message
void usbhid_send(uint8_t * msg)
{
udp_send(serverfd, msg, HID_MESSAGE_SIZE);
if (use_udp)
{
udp_send(fd, msg, HID_MESSAGE_SIZE);
}
else
{
if (write(fd, msg, HID_MESSAGE_SIZE) < 0)
{
perror("hidg write");
exit(1);
}
}
}
void usbhid_close()
{
udp_close(serverfd);
close(fd);
}
void int_handler(int i)
@ -181,10 +213,47 @@ void int_handler(int i)
exit(0);
}
void device_init()
void usage(const char * cmd)
{
fprintf(stderr, "Usage: %s [-b udp|hidg]\n", cmd);
fprintf(stderr, " -b backing implementation: udp(default) or hidg\n");
exit(1);
}
void device_init(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "b:")) != -1)
{
switch (opt)
{
case 'b':
if (strcmp("udp", optarg) == 0)
{
use_udp = true;
}
else if (strcmp("hidg", optarg) == 0)
{
use_udp = false;
}
else
{
usage(argv[0]);
}
break;
default:
usage(argv[0]);
break;
}
}
signal(SIGINT, int_handler);
printf1(TAG_GREEN, "Using %s backing\n", use_udp ? "UDP" : "hidg");
usbhid_init();
authenticator_initialize();
@ -203,6 +272,14 @@ void main_loop_delay()
nanosleep(&ts,NULL);
}
void delay(uint32_t ms)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 1000*1000*ms;
nanosleep(&ts,NULL);
}
void heartbeat()
{

View File

@ -11,6 +11,7 @@ SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
SRC += ../../fido2/extensions/wallet.c
# Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c ../../crypto/tiny-AES-c/aes.c
@ -81,4 +82,4 @@ cbor:
cd ../../tinycbor/ && make clean
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
LDFLAGS="$(LDFLAGS_LIB)" \
CFLAGS="$(CFLAGS)"
CFLAGS="$(CFLAGS) -Os"

View File

@ -23,6 +23,7 @@
//#define USING_DEV_BOARD
#define ENABLE_U2F_EXTENSIONS
// #define ENABLE_WALLET
#define ENABLE_U2F

View File

@ -107,17 +107,17 @@ void device_reboot()
NVIC_SystemReset();
}
void device_init()
void device_init(int argc, char *argv[])
{
hw_init(LOW_FREQUENCY);
isLowFreq = 0;
haveNFC = nfc_init();
if (haveNFC)
{
printf1(TAG_NFC, "Have NFC\r\n");
isLowFreq = 1;
}
else
{
@ -460,6 +460,10 @@ static int handle_packets()
int ctap_user_presence_test()
{
int ret;
if (device_is_nfc())
{
return 1;
}
#if SKIP_BUTTON_CHECK_WITH_DELAY
int i=500;
while(i--)

View File

@ -496,9 +496,9 @@ void nfc_process_iblock(uint8_t * buf, int len)
// WTX_on(WTX_TIME_DEFAULT);
// SystemClock_Config_LF32();
// delay(300);
device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
u2f_request_nfc(&buf[1], len, &ctap_resp);
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
// if (!WTX_off())
// return;

65
tools/gadgetfs/Makefile Normal file
View File

@ -0,0 +1,65 @@
TOP := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
KERNEL_FULL_VERSION := $(shell uname -r)
KERNEL_VERSION := $(shell uname -r | grep -o "^[^-]*")
KERNEL_MAJOR := $(shell uname -r | cut -d. -f1)
KERNEL_MINOR := $(shell uname -r | cut -d. -f2)
MANUFACTURER = "Solo"
SERIAL = "1234567890"
IDVENDOR = "0x0483"
IDPRODUCT = "0xa2ca"
PRODUCT = "Solo Software Authenticator"
CONFIGFS = /sys/kernel/config
CONFIGFS_FIDO2 = $(CONFIGFS)/usb_gadget/fido2
obj-m := dummy_hcd.o
KVERSION := $(shell uname -r)
SHELL := /bin/bash
all: dummy_hcd.ko
install: dummy_hcd.ko
modprobe libcomposite
insmod dummy_hcd.ko
mkdir -p $(CONFIGFS_FIDO2)
mkdir -p $(CONFIGFS_FIDO2)/configs/c.1
mkdir -p $(CONFIGFS_FIDO2)/functions/hid.usb0
echo 0 > $(CONFIGFS_FIDO2)/functions/hid.usb0/protocol
echo 0 > $(CONFIGFS_FIDO2)/functions/hid.usb0/subclass
echo 64 > $(CONFIGFS_FIDO2)/functions/hid.usb0/report_length
echo -ne "\x06\xd0\xf1\x09\x01\xa1\x01\x09\x20\x15\x00\x26\xff\x00\x75\x08\x95\x40\x81\x02\x09\x21\x15\x00\x26\xff\x00\x75\x08\x95\x40\x91\x02\xc0" > $(CONFIGFS_FIDO2)/functions/hid.usb0/report_desc
mkdir $(CONFIGFS_FIDO2)/strings/0x409
mkdir $(CONFIGFS_FIDO2)/configs/c.1/strings/0x409
echo $(IDPRODUCT) > $(CONFIGFS_FIDO2)/idProduct
echo $(IDVENDOR) > $(CONFIGFS_FIDO2)/idVendor
echo $(SERIAL) > $(CONFIGFS_FIDO2)/strings/0x409/serialnumber
echo $(MANUFACTURER) > $(CONFIGFS_FIDO2)/strings/0x409/manufacturer
echo $(PRODUCT) > $(CONFIGFS_FIDO2)/strings/0x409/product
echo "Configuration 1" > $(CONFIGFS_FIDO2)/configs/c.1/strings/0x409/configuration
echo 120 > $(CONFIGFS_FIDO2)/configs/c.1/MaxPower
ln -s $(CONFIGFS_FIDO2)/functions/hid.usb0 $(CONFIGFS_FIDO2)/configs/c.1
echo "dummy_udc.0" > $(CONFIGFS_FIDO2)/UDC
uninstall:
echo "" > $(CONFIGFS_FIDO2)/UDC
rm $(CONFIGFS_FIDO2)/configs/c.1/hid.usb0
rmdir $(CONFIGFS_FIDO2)/configs/c.1/strings/0x409
rmdir $(CONFIGFS_FIDO2)/configs/c.1
rmdir $(CONFIGFS_FIDO2)/functions/hid.usb0
rmdir $(CONFIGFS_FIDO2)/strings/0x409
rmdir $(CONFIGFS_FIDO2)
rmmod dummy_hcd.ko
dummy_hcd.ko: dummy_hcd.c
$(MAKE) -C /lib/modules/$(KERNEL_FULL_VERSION)/build M=$(TOP) modules
dummy_hcd.c: /usr/src/linux-source-$(KERNEL_VERSION).tar.bz2
tar -xvf $^ linux-source-$(KERNEL_VERSION)/drivers/usb/gadget/udc/dummy_hcd.c
cp linux-source-$(KERNEL_VERSION)/drivers/usb/gadget/udc/dummy_hcd.c $@
clean:
$(MAKE) -C /lib/modules/$(KERNEL_FULL_VERSION)/build M=$(TOP) clean
rm -rf linux-source-$(KERNEL_VERSION)
rm -f dummy_hcd.c

View File

@ -1009,8 +1009,8 @@ class FIDO2Tests(Tester):
key = res[1]
assert "Is public key" and key[1] == 2
assert "Is P256" and key[-1] == 1
if key[3] != -7:
print("WARNING: algorithm returned is not for ES256 (-7): ", key[3])
assert "Is ALG_ECDH_ES_HKDF_256" and key[3] == -25
assert "Right key" and len(key[-3]) == 32 and isinstance(key[-3], bytes)
with Test("Test setting a new pin"):
@ -1069,9 +1069,45 @@ class FIDO2Tests(Tester):
self.testReset()
with Test("Test sending zero-length pin_auth, expect PIN_NOT_SET"):
self.testMC(
"Send MC request with new pin auth",
cdh,
rp,
user,
key_params,
other={"pin_auth": b"", "pin_protocol": pin_protocol},
expectedError=CtapError.ERR.PIN_NOT_SET,
)
self.testGA(
"Send MC request with new pin auth",
rp["id"],
cdh,
other={"pin_auth": b"", "pin_protocol": pin_protocol},
expectedError=CtapError.ERR.PIN_NOT_SET,
)
with Test("Setting pin code, expect SUCCESS"):
self.client.pin_protocol.set_pin(pin1)
with Test("Test sending zero-length pin_auth, expect PIN_INVALID"):
self.testMC(
"Send MC request with new pin auth",
cdh,
rp,
user,
key_params,
other={"pin_auth": b"", "pin_protocol": pin_protocol},
expectedError=CtapError.ERR.PIN_INVALID,
)
self.testGA(
"Send MC request with new pin auth",
rp["id"],
cdh,
other={"pin_auth": b"", "pin_protocol": pin_protocol},
expectedError=CtapError.ERR.PIN_INVALID,
)
self.testReset()
with Test("Setting pin code >63 bytes, expect POLICY_VIOLATION "):
try:

View File

@ -1,6 +1,8 @@
from solo.client import SoloClient
from solo.commands import SoloExtension
from fido2.ctap1 import ApduError
from fido2.utils import sha256
from .util import shannon_entropy
from .tester import Tester, Test
@ -44,6 +46,17 @@ class SoloTests(Tester):
pass
sc.exchange = sc.exchange_fido2
req = SoloClient.format_request(SoloExtension.version, 0, b"A" * 16)
a = sc.ctap2.get_assertion(
sc.host, b"B" * 32, [{"id": req, "type": "public-key"}]
)
with Test("Test custom command returned valid assertion"):
assert a.auth_data.rp_id_hash == sha256(sc.host.encode("utf8"))
assert a.credential["id"] == req
assert (a.auth_data.flags & 0x5) == 0x5
with Test("Test Solo version and random commands with fido2 layer"):
assert len(sc.solo_version()) == 3
sc.get_rng()

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDxXjGjdpW8N2/3
s1pYCe1LgVEJlnv0sz/iUaVsd2Z6hv5khaJPVc/LBl5uR5IzotLVw/fp9CU4r7wL
xkwezpH+zyo7VjlulRxUZ2zaclYyiKuE1LpdYmuLetzkDI/e2CF26eCQv3vbqxPy
dS9wplbgplBax6s8IlfjEhpvIxb4JW1+g7U+zyYGTYVakJYzvzTTl3IFqtO34BBE
vSJFDtBuZViwfEctFsinjMHXBo6Nb7OoCQq0ih0gZJwKcojYTbpbm2YhNR2i8mHy
6jpsv3bJTygJHIEDGILgu5T6uNY5nwMC4Js/w4YnI+kO4kmwAEEFzxZXa666UxZV
VbkWMUezAgMBAAECggEBANr5yuzzC9uLLAC8ba1LrEYBF0Usil6xNLcdvYePN6DX
0fnepyd0waT+rTM9qW1BPigDt2pAPniULnU8PRkB7cAPsM/OZSJnEyGcB0yTyJHm
Hj3PRRitzsXm/HnEz82rpYidnEeWAUeEiP9Bw31e25jKKkKBvV88BSIT7XmAizkp
8H/ya+qm3R8Kxsr/t19UbkrBJ0V9OIM6GXsCZHul2EsRoY7d4om90IvKCHW5MIuT
MPkIR2msntgNIhYh+mxDrcZ2qq/AZO/GGf3GpxiDN4imuNvgd6HVg0qwxIpblhjK
qX1AeiLQ+ljlz3ktY4TVpt2vgPToekKbme6neCFD3FkCgYEA+4E5UegMTcLce6jR
6JMxA5PeING7zRLLKwNsgZvwdpEXh5OBMxWu674tg+OTWG3fPNaIdz+PN+DJFtoV
/dDNkOG5TCwBQJPipN2Y5bSmjbBDI4jb/rDLeUvFFF/Hp81bMDYe+o1DNomzkC+A
5/uGeNXET90D5NpgRU8Jk0gKlNcCgYEA9a6WmBHWZctXNqD9Sx9pw5i5RQyy2s/D
2PmZ01VcE6uMZGghHw4gJIzpD1bY2nP9g8yD5v/VD0bz+GTBp3eKlw/9E1aYfMwN
gUdP2sqgnYI7gareI/DTONBVQEmDcRTCH6fewgnwg9wuCwoqgxGAoi9IX4vBWFJt
YNlOYQErLIUCgYAVtJlV0Ej/jQmqQm+bOtjIDkLlYjRrBmwyUiFTLjoagXseYESO
PBjUj50t/L4Cq7jQb1NntzyM/gFcz4WGWjbjgheT01hoUlsFD3ramDSnlca1kmIq
IOful/NyRrHccYSlLIaP6REb69ZrYy4k1zhLxWcj3VcwsQgN8zxIUbdYEQKBgQCL
APUDpVQA1EPMDNpDHsrgeBCbGMw5MURGBzMZdzpZhr4wMRpMT9mv1Goo26JmNypA
3/3hPO53blWrPJa1AdXQEqPFxUERmwIpGwf7apnlhEHW4647944KnxUdAnr0CCKt
dnV6o9UJRhJm/KGA9u4o1UfFh3UlW723BjxqdhbPYQKBgF4KQtcw/Y2kF2BIerD6
LyO/tbS90VMQiZYZjGmuLG0K351RqW/l5U9RaEK2hpb10Qu7x/inmSM7sBY7NWE8
YOJLH/ArubTCLwiFzkQp24NFZt19xj2qlAunydhgus+q+r5mCp18QyjhTdMYVFqk
3biBiaWbI3HoS3qyCf7ezWvw
-----END PRIVATE KEY-----

View File

@ -1 +0,0 @@
D7564E117010E076

View File

@ -1,23 +0,0 @@
<html>
<head>
</head>
<body>
<h1>U2F Bridge Demo</h1>
<h3 id="progress"></h3>
<h3 id="errors" style="color:red;"></h3>
<p>To update firmware, upload JSON file here.</p>
<input type="file" id="input" onchange="handleFirmware(this.files)">
<br>
<br>
<button onclick="run_tests()">Run tests</button>
</body>
<script src="js/u2f-api.js"></script>
<script src="js/elliptic.js"></script>
<script src="js/sha256.min.js"></script>
<script src="js/aes.js"></script>
<script src="js/intel-hex.js"></script>
<script src="js/wallet.js"></script>
</html>

View File

@ -1,803 +0,0 @@
"use strict";
(function(root) {
function checkInt(value) {
return (parseInt(value) === value);
}
function checkInts(arrayish) {
if (!checkInt(arrayish.length)) { return false; }
for (var i = 0; i < arrayish.length; i++) {
if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
return false;
}
}
return true;
}
function coerceArray(arg, copy) {
// ArrayBuffer view
if (arg.buffer && ArrayBuffer.isView(arg) && arg.name === 'Uint8Array') {
if (copy) {
if (arg.slice) {
arg = arg.slice();
} else {
arg = Array.prototype.slice.call(arg);
}
}
return arg;
}
// It's an array; check it is a valid representation of a byte
if (Array.isArray(arg)) {
if (!checkInts(arg)) {
throw new Error('Array contains invalid value: ' + arg);
}
return new Uint8Array(arg);
}
// Something else, but behaves like an array (maybe a Buffer? Arguments?)
if (checkInt(arg.length) && checkInts(arg)) {
return new Uint8Array(arg);
}
throw new Error('unsupported array-like object');
}
function createArray(length) {
return new Uint8Array(length);
}
function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
if (sourceStart != null || sourceEnd != null) {
if (sourceArray.slice) {
sourceArray = sourceArray.slice(sourceStart, sourceEnd);
} else {
sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
}
}
targetArray.set(sourceArray, targetStart);
}
var convertUtf8 = (function() {
function toBytes(text) {
var result = [], i = 0;
text = encodeURI(text);
while (i < text.length) {
var c = text.charCodeAt(i++);
// if it is a % sign, encode the following 2 bytes as a hex value
if (c === 37) {
result.push(parseInt(text.substr(i, 2), 16))
i += 2;
// otherwise, just the actual byte
} else {
result.push(c)
}
}
return coerceArray(result);
}
function fromBytes(bytes) {
var result = [], i = 0;
while (i < bytes.length) {
var c = bytes[i];
if (c < 128) {
result.push(String.fromCharCode(c));
i++;
} else if (c > 191 && c < 224) {
result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
i += 2;
} else {
result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
i += 3;
}
}
return result.join('');
}
return {
toBytes: toBytes,
fromBytes: fromBytes,
}
})();
var convertHex = (function() {
function toBytes(text) {
var result = [];
for (var i = 0; i < text.length; i += 2) {
result.push(parseInt(text.substr(i, 2), 16));
}
return result;
}
// http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
var Hex = '0123456789abcdef';
function fromBytes(bytes) {
var result = [];
for (var i = 0; i < bytes.length; i++) {
var v = bytes[i];
result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
}
return result.join('');
}
return {
toBytes: toBytes,
fromBytes: fromBytes,
}
})();
// Number of rounds by keysize
var numberOfRounds = {16: 10, 24: 12, 32: 14}
// Round constant words
var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91];
// S-box and Inverse S-box (S is for Substitution)
var S = [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];
var Si =[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];
// Transformations for encryption
var T1 = [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];
var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616];
var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16];
var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c];
// Transformations for decryption
var T5 = [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];
var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857];
var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8];
var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0];
// Transformations for decryption key expansion
var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3];
var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697];
var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46];
var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d];
function convertToInt32(bytes) {
var result = [];
for (var i = 0; i < bytes.length; i += 4) {
result.push(
(bytes[i ] << 24) |
(bytes[i + 1] << 16) |
(bytes[i + 2] << 8) |
bytes[i + 3]
);
}
return result;
}
var AES = function(key) {
if (!(this instanceof AES)) {
throw Error('AES must be instanitated with `new`');
}
Object.defineProperty(this, 'key', {
value: coerceArray(key, true)
});
this._prepare();
}
AES.prototype._prepare = function() {
var rounds = numberOfRounds[this.key.length];
if (rounds == null) {
throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
}
// encryption round keys
this._Ke = [];
// decryption round keys
this._Kd = [];
for (var i = 0; i <= rounds; i++) {
this._Ke.push([0, 0, 0, 0]);
this._Kd.push([0, 0, 0, 0]);
}
var roundKeyCount = (rounds + 1) * 4;
var KC = this.key.length / 4;
// convert the key into ints
var tk = convertToInt32(this.key);
// copy values into round key arrays
var index;
for (var i = 0; i < KC; i++) {
index = i >> 2;
this._Ke[index][i % 4] = tk[i];
this._Kd[rounds - index][i % 4] = tk[i];
}
// key expansion (fips-197 section 5.2)
var rconpointer = 0;
var t = KC, tt;
while (t < roundKeyCount) {
tt = tk[KC - 1];
tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^
(S[(tt >> 8) & 0xFF] << 16) ^
(S[ tt & 0xFF] << 8) ^
S[(tt >> 24) & 0xFF] ^
(rcon[rconpointer] << 24));
rconpointer += 1;
// key expansion (for non-256 bit)
if (KC != 8) {
for (var i = 1; i < KC; i++) {
tk[i] ^= tk[i - 1];
}
// key expansion for 256-bit keys is "slightly different" (fips-197)
} else {
for (var i = 1; i < (KC / 2); i++) {
tk[i] ^= tk[i - 1];
}
tt = tk[(KC / 2) - 1];
tk[KC / 2] ^= (S[ tt & 0xFF] ^
(S[(tt >> 8) & 0xFF] << 8) ^
(S[(tt >> 16) & 0xFF] << 16) ^
(S[(tt >> 24) & 0xFF] << 24));
for (var i = (KC / 2) + 1; i < KC; i++) {
tk[i] ^= tk[i - 1];
}
}
// copy values into round key arrays
var i = 0, r, c;
while (i < KC && t < roundKeyCount) {
r = t >> 2;
c = t % 4;
this._Ke[r][c] = tk[i];
this._Kd[rounds - r][c] = tk[i++];
t++;
}
}
// inverse-cipher-ify the decryption round key (fips-197 section 5.3)
for (var r = 1; r < rounds; r++) {
for (var c = 0; c < 4; c++) {
tt = this._Kd[r][c];
this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^
U2[(tt >> 16) & 0xFF] ^
U3[(tt >> 8) & 0xFF] ^
U4[ tt & 0xFF]);
}
}
}
AES.prototype.encrypt = function(plaintext) {
if (plaintext.length != 16) {
throw new Error('invalid plaintext size (must be 16 bytes)');
}
var rounds = this._Ke.length - 1;
var a = [0, 0, 0, 0];
// convert plaintext to (ints ^ key)
var t = convertToInt32(plaintext);
for (var i = 0; i < 4; i++) {
t[i] ^= this._Ke[0][i];
}
// apply round transforms
for (var r = 1; r < rounds; r++) {
for (var i = 0; i < 4; i++) {
a[i] = (T1[(t[ i ] >> 24) & 0xff] ^
T2[(t[(i + 1) % 4] >> 16) & 0xff] ^
T3[(t[(i + 2) % 4] >> 8) & 0xff] ^
T4[ t[(i + 3) % 4] & 0xff] ^
this._Ke[r][i]);
}
t = a.slice();
}
// the last round is special
var result = createArray(16), tt;
for (var i = 0; i < 4; i++) {
tt = this._Ke[rounds][i];
result[4 * i ] = (S[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
result[4 * i + 2] = (S[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
result[4 * i + 3] = (S[ t[(i + 3) % 4] & 0xff] ^ tt ) & 0xff;
}
return result;
}
AES.prototype.decrypt = function(ciphertext) {
if (ciphertext.length != 16) {
throw new Error('invalid ciphertext size (must be 16 bytes)');
}
var rounds = this._Kd.length - 1;
var a = [0, 0, 0, 0];
// convert plaintext to (ints ^ key)
var t = convertToInt32(ciphertext);
for (var i = 0; i < 4; i++) {
t[i] ^= this._Kd[0][i];
}
// apply round transforms
for (var r = 1; r < rounds; r++) {
for (var i = 0; i < 4; i++) {
a[i] = (T5[(t[ i ] >> 24) & 0xff] ^
T6[(t[(i + 3) % 4] >> 16) & 0xff] ^
T7[(t[(i + 2) % 4] >> 8) & 0xff] ^
T8[ t[(i + 1) % 4] & 0xff] ^
this._Kd[r][i]);
}
t = a.slice();
}
// the last round is special
var result = createArray(16), tt;
for (var i = 0; i < 4; i++) {
tt = this._Kd[rounds][i];
result[4 * i ] = (Si[(t[ i ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
result[4 * i + 2] = (Si[(t[(i + 2) % 4] >> 8) & 0xff] ^ (tt >> 8)) & 0xff;
result[4 * i + 3] = (Si[ t[(i + 1) % 4] & 0xff] ^ tt ) & 0xff;
}
return result;
}
/**
* Mode Of Operation - Electonic Codebook (ECB)
*/
var ModeOfOperationECB = function(key) {
if (!(this instanceof ModeOfOperationECB)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Electronic Code Block";
this.name = "ecb";
this._aes = new AES(key);
}
ModeOfOperationECB.prototype.encrypt = function(plaintext) {
plaintext = coerceArray(plaintext);
if ((plaintext.length % 16) !== 0) {
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
}
var ciphertext = createArray(plaintext.length);
var block = createArray(16);
for (var i = 0; i < plaintext.length; i += 16) {
copyArray(plaintext, block, 0, i, i + 16);
block = this._aes.encrypt(block);
copyArray(block, ciphertext, i);
}
return ciphertext;
}
ModeOfOperationECB.prototype.decrypt = function(ciphertext) {
ciphertext = coerceArray(ciphertext);
if ((ciphertext.length % 16) !== 0) {
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
}
var plaintext = createArray(ciphertext.length);
var block = createArray(16);
for (var i = 0; i < ciphertext.length; i += 16) {
copyArray(ciphertext, block, 0, i, i + 16);
block = this._aes.decrypt(block);
copyArray(block, plaintext, i);
}
return plaintext;
}
/**
* Mode Of Operation - Cipher Block Chaining (CBC)
*/
var ModeOfOperationCBC = function(key, iv) {
if (!(this instanceof ModeOfOperationCBC)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Cipher Block Chaining";
this.name = "cbc";
if (!iv) {
iv = createArray(16);
} else if (iv.length != 16) {
throw new Error('invalid initialation vector size (must be 16 bytes)');
}
this._lastCipherblock = coerceArray(iv, true);
this._aes = new AES(key);
}
ModeOfOperationCBC.prototype.encrypt = function(plaintext) {
plaintext = coerceArray(plaintext);
if ((plaintext.length % 16) !== 0) {
throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
}
var ciphertext = createArray(plaintext.length);
var block = createArray(16);
for (var i = 0; i < plaintext.length; i += 16) {
copyArray(plaintext, block, 0, i, i + 16);
for (var j = 0; j < 16; j++) {
block[j] ^= this._lastCipherblock[j];
}
this._lastCipherblock = this._aes.encrypt(block);
copyArray(this._lastCipherblock, ciphertext, i);
}
return ciphertext;
}
ModeOfOperationCBC.prototype.decrypt = function(ciphertext) {
ciphertext = coerceArray(ciphertext);
if ((ciphertext.length % 16) !== 0) {
throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
}
var plaintext = createArray(ciphertext.length);
var block = createArray(16);
for (var i = 0; i < ciphertext.length; i += 16) {
copyArray(ciphertext, block, 0, i, i + 16);
block = this._aes.decrypt(block);
for (var j = 0; j < 16; j++) {
plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
}
copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
}
return plaintext;
}
/**
* Mode Of Operation - Cipher Feedback (CFB)
*/
var ModeOfOperationCFB = function(key, iv, segmentSize) {
if (!(this instanceof ModeOfOperationCFB)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Cipher Feedback";
this.name = "cfb";
if (!iv) {
iv = createArray(16);
} else if (iv.length != 16) {
throw new Error('invalid initialation vector size (must be 16 size)');
}
if (!segmentSize) { segmentSize = 1; }
this.segmentSize = segmentSize;
this._shiftRegister = coerceArray(iv, true);
this._aes = new AES(key);
}
ModeOfOperationCFB.prototype.encrypt = function(plaintext) {
if ((plaintext.length % this.segmentSize) != 0) {
throw new Error('invalid plaintext size (must be segmentSize bytes)');
}
var encrypted = coerceArray(plaintext, true);
var xorSegment;
for (var i = 0; i < encrypted.length; i += this.segmentSize) {
xorSegment = this._aes.encrypt(this._shiftRegister);
for (var j = 0; j < this.segmentSize; j++) {
encrypted[i + j] ^= xorSegment[j];
}
// Shift the register
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
}
return encrypted;
}
ModeOfOperationCFB.prototype.decrypt = function(ciphertext) {
if ((ciphertext.length % this.segmentSize) != 0) {
throw new Error('invalid ciphertext size (must be segmentSize bytes)');
}
var plaintext = coerceArray(ciphertext, true);
var xorSegment;
for (var i = 0; i < plaintext.length; i += this.segmentSize) {
xorSegment = this._aes.encrypt(this._shiftRegister);
for (var j = 0; j < this.segmentSize; j++) {
plaintext[i + j] ^= xorSegment[j];
}
// Shift the register
copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
}
return plaintext;
}
/**
* Mode Of Operation - Output Feedback (OFB)
*/
var ModeOfOperationOFB = function(key, iv) {
if (!(this instanceof ModeOfOperationOFB)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Output Feedback";
this.name = "ofb";
if (!iv) {
iv = createArray(16);
} else if (iv.length != 16) {
throw new Error('invalid initialation vector size (must be 16 bytes)');
}
this._lastPrecipher = coerceArray(iv, true);
this._lastPrecipherIndex = 16;
this._aes = new AES(key);
}
ModeOfOperationOFB.prototype.encrypt = function(plaintext) {
var encrypted = coerceArray(plaintext, true);
for (var i = 0; i < encrypted.length; i++) {
if (this._lastPrecipherIndex === 16) {
this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
this._lastPrecipherIndex = 0;
}
encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
}
return encrypted;
}
// Decryption is symetric
ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
/**
* Counter object for CTR common mode of operation
*/
var Counter = function(initialValue) {
if (!(this instanceof Counter)) {
throw Error('Counter must be instanitated with `new`');
}
// We allow 0, but anything false-ish uses the default 1
if (initialValue !== 0 && !initialValue) { initialValue = 1; }
if (typeof(initialValue) === 'number') {
this._counter = createArray(16);
this.setValue(initialValue);
} else {
this.setBytes(initialValue);
}
}
Counter.prototype.setValue = function(value) {
if (typeof(value) !== 'number' || parseInt(value) != value) {
throw new Error('invalid counter value (must be an integer)');
}
// We cannot safely handle numbers beyond the safe range for integers
if (value > Number.MAX_SAFE_INTEGER) {
throw new Error('integer value out of safe range');
}
for (var index = 15; index >= 0; --index) {
this._counter[index] = value % 256;
value = parseInt(value / 256);
}
}
Counter.prototype.setBytes = function(bytes) {
bytes = coerceArray(bytes, true);
if (bytes.length != 16) {
throw new Error('invalid counter bytes size (must be 16 bytes)');
}
this._counter = bytes;
};
Counter.prototype.increment = function() {
for (var i = 15; i >= 0; i--) {
if (this._counter[i] === 255) {
this._counter[i] = 0;
} else {
this._counter[i]++;
break;
}
}
}
/**
* Mode Of Operation - Counter (CTR)
*/
var ModeOfOperationCTR = function(key, counter) {
if (!(this instanceof ModeOfOperationCTR)) {
throw Error('AES must be instanitated with `new`');
}
this.description = "Counter";
this.name = "ctr";
if (!(counter instanceof Counter)) {
counter = new Counter(counter)
}
this._counter = counter;
this._remainingCounter = null;
this._remainingCounterIndex = 16;
this._aes = new AES(key);
}
ModeOfOperationCTR.prototype.encrypt = function(plaintext) {
var encrypted = coerceArray(plaintext, true);
for (var i = 0; i < encrypted.length; i++) {
if (this._remainingCounterIndex === 16) {
this._remainingCounter = this._aes.encrypt(this._counter._counter);
this._remainingCounterIndex = 0;
this._counter.increment();
}
encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
}
return encrypted;
}
// Decryption is symetric
ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;
///////////////////////
// Padding
// See:https://tools.ietf.org/html/rfc2315
function pkcs7pad(data) {
data = coerceArray(data, true);
var padder = 16 - (data.length % 16);
var result = createArray(data.length + padder);
copyArray(data, result);
for (var i = data.length; i < result.length; i++) {
result[i] = padder;
}
return result;
}
function pkcs7strip(data) {
data = coerceArray(data, true);
if (data.length < 16) { throw new Error('PKCS#7 invalid length'); }
var padder = data[data.length - 1];
if (padder > 16) { throw new Error('PKCS#7 padding byte out of range'); }
var length = data.length - padder;
for (var i = 0; i < padder; i++) {
if (data[length + i] !== padder) {
throw new Error('PKCS#7 invalid padding byte');
}
}
var result = createArray(length);
copyArray(data, result, 0, 0, length);
return result;
}
///////////////////////
// Exporting
// The block cipher
var aesjs = {
AES: AES,
Counter: Counter,
ModeOfOperation: {
ecb: ModeOfOperationECB,
cbc: ModeOfOperationCBC,
cfb: ModeOfOperationCFB,
ofb: ModeOfOperationOFB,
ctr: ModeOfOperationCTR
},
utils: {
hex: convertHex,
utf8: convertUtf8
},
padding: {
pkcs7: {
pad: pkcs7pad,
strip: pkcs7strip
}
},
_arrayTest: {
coerceArray: coerceArray,
createArray: createArray,
copyArray: copyArray,
}
};
// node.js
if (typeof exports !== 'undefined') {
module.exports = aesjs
// RequireJS/AMD
// http://www.requirejs.org/docs/api.html
// https://github.com/amdjs/amdjs-api/wiki/AMD
} else if (typeof(define) === 'function' && define.amd) {
define(aesjs);
// Web Browsers
} else {
// If there was an existing library at "aesjs" make sure it's still available
if (root.aesjs) {
aesjs._aesjs = root.aesjs;
}
root.aesjs = aesjs;
}
})(this);

File diff suppressed because it is too large Load Diff

View File

@ -1,977 +0,0 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.MemoryMap = factory());
}(this, (function () { 'use strict';
/**
* Parser/writer for the "Intel hex" format.
*/
/*
* A regexp that matches lines in a .hex file.
*
* One hexadecimal character is matched by "[0-9A-Fa-f]".
* Two hex characters are matched by "[0-9A-Fa-f]{2}"
* Eight or more hex characters are matched by "[0-9A-Fa-f]{8,}"
* A capture group of two hex characters is "([0-9A-Fa-f]{2})"
*
* Record mark :
* 8 or more hex chars ([0-9A-Fa-f]{8,})
* Checksum ([0-9A-Fa-f]{2})
* Optional newline (?:\r\n|\r|\n|)
*/
var hexLineRegexp = /:([0-9A-Fa-f]{8,})([0-9A-Fa-f]{2})(?:\r\n|\r|\n|)/g;
// Takes a Uint8Array as input,
// Returns an integer in the 0-255 range.
function checksum(bytes) {
return (-bytes.reduce(function (sum, v){ return sum + v; }, 0)) & 0xFF;
}
// Takes two Uint8Arrays as input,
// Returns an integer in the 0-255 range.
function checksumTwo(array1, array2) {
var partial1 = array1.reduce(function (sum, v){ return sum + v; }, 0);
var partial2 = array2.reduce(function (sum, v){ return sum + v; }, 0);
return -( partial1 + partial2 ) & 0xFF;
}
// Trivial utility. Converts a number to hex and pads with zeroes up to 2 characters.
function hexpad(number) {
return number.toString(16).toUpperCase().padStart(2, '0');
}
// Polyfill as per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
Number.isInteger = Number.isInteger || function(value) {
return typeof value === 'number' &&
isFinite(value) &&
Math.floor(value) === value;
};
/**
* @class MemoryMap
*
* Represents the contents of a memory layout, with main focus into (possibly sparse) blocks of data.
*<br/>
* A {@linkcode MemoryMap} acts as a subclass of
* {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map|Map}.
* In every entry of it, the key is the starting address of a data block (an integer number),
* and the value is the <tt>Uint8Array</tt> with the data for that block.
*<br/>
* The main rationale for this is that a .hex file can contain a single block of contiguous
* data starting at memory address 0 (and it's the common case for simple .hex files),
* but complex files with several non-contiguous data blocks are also possible, thus
* the need for a data structure on top of the <tt>Uint8Array</tt>s.
*<br/>
* In order to parse <tt>.hex</tt> files, use the {@linkcode MemoryMap.fromHex} <em>static</em> factory
* method. In order to write <tt>.hex</tt> files, create a new {@linkcode MemoryMap} and call
* its {@linkcode MemoryMap.asHexString} method.
*
* @extends Map
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let memMap1 = new MemoryMap();
* let memMap2 = new MemoryMap([[0, new Uint8Array(1,2,3,4)]]);
* let memMap3 = new MemoryMap({0: new Uint8Array(1,2,3,4)});
* let memMap4 = new MemoryMap({0xCF0: new Uint8Array(1,2,3,4)});
*/
var MemoryMap = function MemoryMap(blocks) {
var this$1 = this;
this._blocks = new Map();
if (blocks && typeof blocks[Symbol.iterator] === 'function') {
for (var tuple of blocks) {
if (!(tuple instanceof Array) || tuple.length !== 2) {
throw new Error('First parameter to MemoryMap constructor must be an iterable of [addr, bytes] or undefined');
}
this$1.set(tuple[0], tuple[1]);
}
} else if (typeof blocks === 'object') {
// Try iterating through the object's keys
var addrs = Object.keys(blocks);
for (var addr of addrs) {
this$1.set(parseInt(addr), blocks[addr]);
}
} else if (blocks !== undefined && blocks !== null) {
throw new Error('First parameter to MemoryMap constructor must be an iterable of [addr, bytes] or undefined');
}
};
var prototypeAccessors = { size: { configurable: true } };
MemoryMap.prototype.set = function set (addr, value) {
if (!Number.isInteger(addr)) {
throw new Error('Address passed to MemoryMap is not an integer');
}
if (addr < 0) {
throw new Error('Address passed to MemoryMap is negative');
}
if (!(value instanceof Uint8Array)) {
throw new Error('Bytes passed to MemoryMap are not an Uint8Array');
}
return this._blocks.set(addr, value);
};
// Delegate the following to the 'this._blocks' Map:
MemoryMap.prototype.get = function get (addr){ return this._blocks.get(addr);};
MemoryMap.prototype.clear = function clear () { return this._blocks.clear(); };
MemoryMap.prototype.delete = function delete$1 (addr) { return this._blocks.delete(addr); };
MemoryMap.prototype.entries = function entries (){ return this._blocks.entries();};
MemoryMap.prototype.forEach = function forEach (callback, that) { return this._blocks.forEach(callback, that); };
MemoryMap.prototype.has = function has (addr){ return this._blocks.has(addr);};
MemoryMap.prototype.keys = function keys () { return this._blocks.keys(); };
MemoryMap.prototype.values = function values () { return this._blocks.values(); };
prototypeAccessors.size.get = function () { return this._blocks.size; };
MemoryMap.prototype[Symbol.iterator] = function () { return this._blocks[Symbol.iterator](); };
/**
* Parses a string containing data formatted in "Intel HEX" format, and
* returns an instance of {@linkcode MemoryMap}.
*<br/>
* The insertion order of keys in the {@linkcode MemoryMap} is guaranteed to be strictly
* ascending. In other words, when iterating through the {@linkcode MemoryMap}, the addresses
* will be ordered in ascending order.
*<br/>
* The parser has an opinionated behaviour, and will throw a descriptive error if it
* encounters some malformed input. Check the project's
* {@link https://github.com/NordicSemiconductor/nrf-intel-hex#Features|README file} for details.
*<br/>
* If <tt>maxBlockSize</tt> is given, any contiguous data block larger than that will
* be split in several blocks.
*
* @param {String} hexText The contents of a .hex file.
* @param {Number} [maxBlockSize=Infinity] Maximum size of the returned <tt>Uint8Array</tt>s.
*
* @return {MemoryMap}
*
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let intelHexString =
* ":100000000102030405060708090A0B0C0D0E0F1068\n" +
* ":00000001FF";
*
* let memMap = MemoryMap.fromHex(intelHexString);
*
* for (let [address, dataBlock] of memMap) {
* console.log('Data block at ', address, ', bytes: ', dataBlock);
* }
*/
MemoryMap.fromHex = function fromHex (hexText, maxBlockSize) {
if ( maxBlockSize === void 0 ) maxBlockSize = Infinity;
var blocks = new MemoryMap();
var lastCharacterParsed = 0;
var matchResult;
var recordCount = 0;
// Upper Linear Base Address, the 16 most significant bits (2 bytes) of
// the current 32-bit (4-byte) address
// In practice this is a offset that is summed to the "load offset" of the
// data records
var ulba = 0;
hexLineRegexp.lastIndex = 0; // Reset the regexp, if not it would skip content when called twice
while ((matchResult = hexLineRegexp.exec(hexText)) !== null) {
recordCount++;
// By default, a regexp loop ignores gaps between matches, but
// we want to be aware of them.
if (lastCharacterParsed !== matchResult.index) {
throw new Error(
'Malformed hex file: Could not parse between characters ' +
lastCharacterParsed +
' and ' +
matchResult.index +
' ("' +
hexText.substring(lastCharacterParsed, Math.min(matchResult.index, lastCharacterParsed + 16)).trim() +
'")');
}
lastCharacterParsed = hexLineRegexp.lastIndex;
// Give pretty names to the match's capture groups
var recordStr = matchResult[1];
var recordChecksum = matchResult[2];
// String to Uint8Array - https://stackoverflow.com/questions/43131242/how-to-convert-a-hexademical-string-of-data-to-an-arraybuffer-in-javascript
var recordBytes = new Uint8Array(recordStr.match(/[\da-f]{2}/gi).map(function (h){ return parseInt(h, 16); }));
var recordLength = recordBytes[0];
if (recordLength + 4 !== recordBytes.length) {
throw new Error('Mismatched record length at record ' + recordCount + ' (' + matchResult[0].trim() + '), expected ' + (recordLength) + ' data bytes but actual length is ' + (recordBytes.length - 4));
}
var cs = checksum(recordBytes);
if (parseInt(recordChecksum, 16) !== cs) {
throw new Error('Checksum failed at record ' + recordCount + ' (' + matchResult[0].trim() + '), should be ' + cs.toString(16) );
}
var offset = (recordBytes[1] << 8) + recordBytes[2];
var recordType = recordBytes[3];
var data = recordBytes.subarray(4);
if (recordType === 0) {
// Data record, contains data
// Create a new block, at (upper linear base address + offset)
if (blocks.has(ulba + offset)) {
throw new Error('Duplicated data at record ' + recordCount + ' (' + matchResult[0].trim() + ')');
}
if (offset + data.length > 0x10000) {
throw new Error(
'Data at record ' +
recordCount +
' (' +
matchResult[0].trim() +
') wraps over 0xFFFF. This would trigger ambiguous behaviour. Please restructure your data so that for every record the data offset plus the data length do not exceed 0xFFFF.');
}
blocks.set( ulba + offset, data );
} else {
// All non-data records must have a data offset of zero
if (offset !== 0) {
throw new Error('Record ' + recordCount + ' (' + matchResult[0].trim() + ') must have 0000 as data offset.');
}
switch (recordType) {
case 1: // EOF
if (lastCharacterParsed !== hexText.length) {
// This record should be at the very end of the string
throw new Error('There is data after an EOF record at record ' + recordCount);
}
return blocks.join(maxBlockSize);
case 2: // Extended Segment Address Record
// Sets the 16 most significant bits of the 20-bit Segment Base
// Address for the subsequent data.
ulba = ((data[0] << 8) + data[1]) << 4;
break;
case 3: // Start Segment Address Record
// Do nothing. Record type 3 only applies to 16-bit Intel CPUs,
// where it should reset the program counter (CS+IP CPU registers)
break;
case 4: // Extended Linear Address Record
// Sets the 16 most significant (upper) bits of the 32-bit Linear Address
// for the subsequent data
ulba = ((data[0] << 8) + data[1]) << 16;
break;
case 5: // Start Linear Address Record
// Do nothing. Record type 5 only applies to 32-bit Intel CPUs,
// where it should reset the program counter (EIP CPU register)
// It might have meaning for other CPU architectures
// (see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka9903.html )
// but will be ignored nonetheless.
break;
default:
throw new Error('Invalid record type 0x' + hexpad(recordType) + ' at record ' + recordCount + ' (should be between 0x00 and 0x05)');
}
}
}
if (recordCount) {
throw new Error('No EOF record at end of file');
} else {
throw new Error('Malformed .hex file, could not parse any registers');
}
};
/**
* Returns a <strong>new</strong> instance of {@linkcode MemoryMap}, containing
* the same data, but concatenating together those memory blocks that are adjacent.
*<br/>
* The insertion order of keys in the {@linkcode MemoryMap} is guaranteed to be strictly
* ascending. In other words, when iterating through the {@linkcode MemoryMap}, the addresses
* will be ordered in ascending order.
*<br/>
* If <tt>maxBlockSize</tt> is given, blocks will be concatenated together only
* until the joined block reaches this size in bytes. This means that the output
* {@linkcode MemoryMap} might have more entries than the input one.
*<br/>
* If there is any overlap between blocks, an error will be thrown.
*<br/>
* The returned {@linkcode MemoryMap} will use newly allocated memory.
*
* @param {Number} [maxBlockSize=Infinity] Maximum size of the <tt>Uint8Array</tt>s in the
* returned {@linkcode MemoryMap}.
*
* @return {MemoryMap}
*/
MemoryMap.prototype.join = function join (maxBlockSize) {
var this$1 = this;
if ( maxBlockSize === void 0 ) maxBlockSize = Infinity;
// First pass, create a Map of address→length of contiguous blocks
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
var blockSizes = new Map();
var lastBlockAddr = -1;
var lastBlockEndAddr = -1;
for (var i=0,l=sortedKeys.length; i<l; i++) {
var blockAddr = sortedKeys[i];
var blockLength = this$1.get(sortedKeys[i]).length;
if (lastBlockEndAddr === blockAddr && (lastBlockEndAddr - lastBlockAddr) < maxBlockSize) {
// Grow when the previous end address equals the current,
// and we don't go over the maximum block size.
blockSizes.set(lastBlockAddr, blockSizes.get(lastBlockAddr) + blockLength);
lastBlockEndAddr += blockLength;
} else if (lastBlockEndAddr <= blockAddr) {
// Else mark a new block.
blockSizes.set(blockAddr, blockLength);
lastBlockAddr = blockAddr;
lastBlockEndAddr = blockAddr + blockLength;
} else {
throw new Error('Overlapping data around address 0x' + blockAddr.toString(16));
}
}
// Second pass: allocate memory for the contiguous blocks and copy data around.
var mergedBlocks = new MemoryMap();
var mergingBlock;
var mergingBlockAddr = -1;
for (var i$1=0,l$1=sortedKeys.length; i$1<l$1; i$1++) {
var blockAddr$1 = sortedKeys[i$1];
if (blockSizes.has(blockAddr$1)) {
mergingBlock = new Uint8Array(blockSizes.get(blockAddr$1));
mergedBlocks.set(blockAddr$1, mergingBlock);
mergingBlockAddr = blockAddr$1;
}
mergingBlock.set(this$1.get(blockAddr$1), blockAddr$1 - mergingBlockAddr);
}
return mergedBlocks;
};
/**
* Given a {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map|<tt>Map</tt>}
* of {@linkcode MemoryMap}s, indexed by a alphanumeric ID,
* returns a <tt>Map</tt> of address to tuples (<tt>Arrays</tt>s of length 2) of the form
* <tt>(id, Uint8Array)</tt>s.
*<br/>
* The scenario for using this is having several {@linkcode MemoryMap}s, from several calls to
* {@link module:nrf-intel-hex~hexToArrays|hexToArrays}, each having a different identifier.
* This function locates where those memory block sets overlap, and returns a <tt>Map</tt>
* containing addresses as keys, and arrays as values. Each array will contain 1 or more
* <tt>(id, Uint8Array)</tt> tuples: the identifier of the memory block set that has
* data in that region, and the data itself. When memory block sets overlap, there will
* be more than one tuple.
*<br/>
* The <tt>Uint8Array</tt>s in the output are
* {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray|subarrays}
* of the input data; new memory is <strong>not</strong> allocated for them.
*<br/>
* The insertion order of keys in the output <tt>Map</tt> is guaranteed to be strictly
* ascending. In other words, when iterating through the <tt>Map</tt>, the addresses
* will be ordered in ascending order.
*<br/>
* When two blocks overlap, the corresponding array of tuples will have the tuples ordered
* in the insertion order of the input <tt>Map</tt> of block sets.
*<br/>
*
* @param {Map.MemoryMap} memoryMaps The input memory block sets
*
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let memMap1 = MemoryMap.fromHex( hexdata1 );
* let memMap2 = MemoryMap.fromHex( hexdata2 );
* let memMap3 = MemoryMap.fromHex( hexdata3 );
*
* let maps = new Map([
* ['file A', blocks1],
* ['file B', blocks2],
* ['file C', blocks3]
* ]);
*
* let overlappings = MemoryMap.overlapMemoryMaps(maps);
*
* for (let [address, tuples] of overlappings) {
* // if 'tuples' has length > 1, there is an overlap starting at 'address'
*
* for (let [address, tuples] of overlappings) {
* let [id, bytes] = tuple;
* // 'id' in this example is either 'file A', 'file B' or 'file C'
* }
* }
* @return {Map.Array<mixed,Uint8Array>} The map of possibly overlapping memory blocks
*/
MemoryMap.overlapMemoryMaps = function overlapMemoryMaps (memoryMaps) {
// First pass: create a list of addresses where any block starts or ends.
var cuts = new Set();
for (var [, blocks] of memoryMaps) {
for (var [address, block] of blocks) {
cuts.add(address);
cuts.add(address + block.length);
}
}
var orderedCuts = Array.from(cuts.values()).sort(function (a,b){ return a-b; });
var overlaps = new Map();
// Second pass: iterate through the cuts, get slices of every intersecting blockset
var loop = function ( i, l ) {
var cut = orderedCuts[i];
var nextCut = orderedCuts[i+1];
var tuples = [];
for (var [setId, blocks$1] of memoryMaps) {
// Find the block with the highest address that is equal or lower to
// the current cut (if any)
var blockAddr = Array.from(blocks$1.keys()).reduce(function (acc, val){
if (val > cut) {
return acc;
}
return Math.max( acc, val );
}, -1);
if (blockAddr !== -1) {
var block$1 = blocks$1.get(blockAddr);
var subBlockStart = cut - blockAddr;
var subBlockEnd = nextCut - blockAddr;
if (subBlockStart < block$1.length) {
tuples.push([ setId, block$1.subarray(subBlockStart, subBlockEnd) ]);
}
}
}
if (tuples.length) {
overlaps.set(cut, tuples);
}
};
for (var i=0, l=orderedCuts.length-1; i<l; i++) loop( i, l );
return overlaps;
};
/**
* Given the output of the {@linkcode MemoryMap.overlapMemoryMaps|overlapMemoryMaps}
* (a <tt>Map</tt> of address to an <tt>Array</tt> of <tt>(id, Uint8Array)</tt> tuples),
* returns a {@linkcode MemoryMap}. This discards the IDs in the process.
*<br/>
* The output <tt>Map</tt> contains as many entries as the input one (using the same addresses
* as keys), but the value for each entry will be the <tt>Uint8Array</tt> of the <b>last</b>
* tuple for each address in the input data.
*<br/>
* The scenario is wanting to join together several parsed .hex files, not worrying about
* their overlaps.
*<br/>
*
* @param {Map.Array<mixed,Uint8Array>} overlaps The (possibly overlapping) input memory blocks
* @return {MemoryMap} The flattened memory blocks
*/
MemoryMap.flattenOverlaps = function flattenOverlaps (overlaps) {
return new MemoryMap(
Array.from(overlaps.entries()).map(function (ref) {
var address = ref[0];
var tuples = ref[1];
return [address, tuples[tuples.length - 1][1] ];
})
);
};
/**
* Returns a new instance of {@linkcode MemoryMap}, where:
*
* <ul>
* <li>Each key (the start address of each <tt>Uint8Array</tt>) is a multiple of
*<tt>pageSize</tt></li>
* <li>The size of each <tt>Uint8Array</tt> is exactly <tt>pageSize</tt></li>
* <li>Bytes from the input map to bytes in the output</li>
* <li>Bytes not in the input are replaced by a padding value</li>
* </ul>
*<br/>
* The scenario is wanting to prepare pages of bytes for a write operation, where the write
* operation affects a whole page/sector at once.
*<br/>
* The insertion order of keys in the output {@linkcode MemoryMap} is guaranteed
* to be strictly ascending. In other words, when iterating through the
* {@linkcode MemoryMap}, the addresses will be ordered in ascending order.
*<br/>
* The <tt>Uint8Array</tt>s in the output will be newly allocated.
*<br/>
*
* @param {Number} [pageSize=1024] The size of the output pages, in bytes
* @param {Number} [pad=0xFF] The byte value to use for padding
* @return {MemoryMap}
*/
MemoryMap.prototype.paginate = function paginate ( pageSize, pad) {
var this$1 = this;
if ( pageSize === void 0 ) pageSize=1024;
if ( pad === void 0 ) pad=0xFF;
if (pageSize <= 0) {
throw new Error('Page size must be greater than zero');
}
var outPages = new MemoryMap();
var page;
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
for (var i=0,l=sortedKeys.length; i<l; i++) {
var blockAddr = sortedKeys[i];
var block = this$1.get(blockAddr);
var blockLength = block.length;
var blockEnd = blockAddr + blockLength;
for (var pageAddr = blockAddr - (blockAddr % pageSize); pageAddr < blockEnd; pageAddr += pageSize) {
page = outPages.get(pageAddr);
if (!page) {
page = new Uint8Array(pageSize);
page.fill(pad);
outPages.set(pageAddr, page);
}
var offset = pageAddr - blockAddr;
var subBlock = (void 0);
if (offset <= 0) {
// First page which intersects the block
subBlock = block.subarray(0, Math.min(pageSize + offset, blockLength));
page.set(subBlock, -offset);
} else {
// Any other page which intersects the block
subBlock = block.subarray(offset, offset + Math.min(pageSize, blockLength - offset));
page.set(subBlock, 0);
}
}
}
return outPages;
};
/**
* Locates the <tt>Uint8Array</tt> which contains the given offset,
* and returns the four bytes held at that offset, as a 32-bit unsigned integer.
*
*<br/>
* Behaviour is similar to {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32|DataView.prototype.getUint32},
* except that this operates over a {@linkcode MemoryMap} instead of
* over an <tt>ArrayBuffer</tt>, and that this may return <tt>undefined</tt> if
* the address is not <em>entirely</em> contained within one of the <tt>Uint8Array</tt>s.
*<br/>
*
* @param {Number} offset The memory offset to read the data
* @param {Boolean} [littleEndian=false] Whether to fetch the 4 bytes as a little- or big-endian integer
* @return {Number|undefined} An unsigned 32-bit integer number
*/
MemoryMap.prototype.getUint32 = function getUint32 (offset, littleEndian) {
var this$1 = this;
var keys = Array.from(this.keys());
for (var i=0,l=keys.length; i<l; i++) {
var blockAddr = keys[i];
var block = this$1.get(blockAddr);
var blockLength = block.length;
var blockEnd = blockAddr + blockLength;
if (blockAddr <= offset && (offset+4) <= blockEnd) {
return (new DataView(block.buffer, offset - blockAddr, 4)).getUint32(0, littleEndian);
}
}
return;
};
/**
* Returns a <tt>String</tt> of text representing a .hex file.
* <br/>
* The writer has an opinionated behaviour. Check the project's
* {@link https://github.com/NordicSemiconductor/nrf-intel-hex#Features|README file} for details.
*
* @param {Number} [lineSize=16] Maximum number of bytes to be encoded in each data record.
* Must have a value between 1 and 255, as per the specification.
*
* @return {String} String of text with the .hex representation of the input binary data
*
* @example
* import MemoryMap from 'nrf-intel-hex';
*
* let memMap = new MemoryMap();
* let bytes = new Uint8Array(....);
* memMap.set(0x0FF80000, bytes); // The block with 'bytes' will start at offset 0x0FF80000
*
* let string = memMap.asHexString();
*/
MemoryMap.prototype.asHexString = function asHexString (lineSize) {
var this$1 = this;
if ( lineSize === void 0 ) lineSize = 16;
var lowAddress = 0;// 16 least significant bits of the current addr
var highAddress = -1 << 16; // 16 most significant bits of the current addr
var records = [];
if (lineSize <=0) {
throw new Error('Size of record must be greater than zero');
} else if (lineSize > 255) {
throw new Error('Size of record must be less than 256');
}
// Placeholders
var offsetRecord = new Uint8Array(6);
var recordHeader = new Uint8Array(4);
var sortedKeys = Array.from(this.keys()).sort(function (a,b){ return a-b; });
for (var i=0,l=sortedKeys.length; i<l; i++) {
var blockAddr = sortedKeys[i];
var block = this$1.get(blockAddr);
// Sanity checks
if (!(block instanceof Uint8Array)) {
throw new Error('Block at offset ' + blockAddr + ' is not an Uint8Array');
}
if (blockAddr < 0) {
throw new Error('Block at offset ' + blockAddr + ' has a negative thus invalid address');
}
var blockSize = block.length;
if (!blockSize) { continue; } // Skip zero-length blocks
if (blockAddr > (highAddress + 0xFFFF)) {
// Insert a new 0x04 record to jump to a new 64KiB block
// Round up the least significant 16 bits - no bitmasks because they trigger
// base-2 negative numbers, whereas subtracting the modulo maintains precision
highAddress = blockAddr - blockAddr % 0x10000;
lowAddress = 0;
offsetRecord[0] = 2;// Length
offsetRecord[1] = 0;// Load offset, high byte
offsetRecord[2] = 0;// Load offset, low byte
offsetRecord[3] = 4;// Record type
offsetRecord[4] = highAddress >> 24;// new address offset, high byte
offsetRecord[5] = highAddress >> 16;// new address offset, low byte
records.push(
':' +
Array.prototype.map.call(offsetRecord, hexpad).join('') +
hexpad(checksum(offsetRecord))
);
}
if (blockAddr < (highAddress + lowAddress)) {
throw new Error(
'Block starting at 0x' +
blockAddr.toString(16) +
' overlaps with a previous block.');
}
lowAddress = blockAddr % 0x10000;
var blockOffset = 0;
var blockEnd = blockAddr + blockSize;
if (blockEnd > 0xFFFFFFFF) {
throw new Error('Data cannot be over 0xFFFFFFFF');
}
// Loop for every 64KiB memory segment that spans this block
while (highAddress + lowAddress < blockEnd) {
if (lowAddress > 0xFFFF) {
// Insert a new 0x04 record to jump to a new 64KiB block
highAddress += 1 << 16; // Increase by one
lowAddress = 0;
offsetRecord[0] = 2;// Length
offsetRecord[1] = 0;// Load offset, high byte
offsetRecord[2] = 0;// Load offset, low byte
offsetRecord[3] = 4;// Record type
offsetRecord[4] = highAddress >> 24;// new address offset, high byte
offsetRecord[5] = highAddress >> 16;// new address offset, low byte
records.push(
':' +
Array.prototype.map.call(offsetRecord, hexpad).join('') +
hexpad(checksum(offsetRecord))
);
}
var recordSize = -1;
// Loop for every record for that spans the current 64KiB memory segment
while (lowAddress < 0x10000 && recordSize) {
recordSize = Math.min(
lineSize, // Normal case
blockEnd - highAddress - lowAddress, // End of block
0x10000 - lowAddress // End of low addresses
);
if (recordSize) {
recordHeader[0] = recordSize; // Length
recordHeader[1] = lowAddress >> 8;// Load offset, high byte
recordHeader[2] = lowAddress;// Load offset, low byte
recordHeader[3] = 0;// Record type
var subBlock = block.subarray(blockOffset, blockOffset + recordSize); // Data bytes for this record
records.push(
':' +
Array.prototype.map.call(recordHeader, hexpad).join('') +
Array.prototype.map.call(subBlock, hexpad).join('') +
hexpad(checksumTwo(recordHeader, subBlock))
);
blockOffset += recordSize;
lowAddress += recordSize;
}
}
}
}
records.push(':00000001FF');// EOF record
return records.join('\n');
};
/**
* Performs a deep copy of the current {@linkcode MemoryMap}, returning a new one
* with exactly the same contents, but allocating new memory for each of its
* <tt>Uint8Array</tt>s.
*
* @return {MemoryMap}
*/
MemoryMap.prototype.clone = function clone () {
var this$1 = this;
var cloned = new MemoryMap();
for (var [addr, value] of this$1) {
cloned.set(addr, new Uint8Array(value));
}
return cloned;
};
/**
* Given one <tt>Uint8Array</tt>, looks through its contents and returns a new
* {@linkcode MemoryMap}, stripping away those regions where there are only
* padding bytes.
* <br/>
* The start of the input <tt>Uint8Array</tt> is assumed to be offset zero for the output.
* <br/>
* The use case here is dumping memory from a working device and try to see the
* "interesting" memory regions it has. This assumes that there is a constant,
* predefined padding byte value being used in the "non-interesting" regions.
* In other words: this will work as long as the dump comes from a flash memory
* which has been previously erased (thus <tt>0xFF</tt>s for padding), or from a
* previously blanked HDD (thus <tt>0x00</tt>s for padding).
* <br/>
* This method uses <tt>subarray</tt> on the input data, and thus does not allocate memory
* for the <tt>Uint8Array</tt>s.
*
* @param {Uint8Array} bytes The input data
* @param {Number} [padByte=0xFF] The value of the byte assumed to be used as padding
* @param {Number} [minPadLength=64] The minimum number of consecutive pad bytes to
* be considered actual padding
*
* @return {MemoryMap}
*/
MemoryMap.fromPaddedUint8Array = function fromPaddedUint8Array (bytes, padByte, minPadLength) {
if ( padByte === void 0 ) padByte=0xFF;
if ( minPadLength === void 0 ) minPadLength=64;
if (!(bytes instanceof Uint8Array)) {
throw new Error('Bytes passed to fromPaddedUint8Array are not an Uint8Array');
}
// The algorithm used is naïve and checks every byte.
// An obvious optimization would be to implement Boyer-Moore
// (see https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm )
// or otherwise start skipping up to minPadLength bytes when going through a non-pad
// byte.
// Anyway, we could expect a lot of cases where there is a majority of pad bytes,
// and the algorithm should check most of them anyway, so the perf gain is questionable.
var memMap = new MemoryMap();
var consecutivePads = 0;
var lastNonPad = -1;
var firstNonPad = 0;
var skippingBytes = false;
var l = bytes.length;
for (var addr = 0; addr < l; addr++) {
var byte = bytes[addr];
if (byte === padByte) {
consecutivePads++;
if (consecutivePads >= minPadLength) {
// Edge case: ignore writing a zero-length block when skipping
// bytes at the beginning of the input
if (lastNonPad !== -1) {
/// Add the previous block to the result memMap
memMap.set(firstNonPad, bytes.subarray(firstNonPad, lastNonPad+1));
}
skippingBytes = true;
}
} else {
if (skippingBytes) {
skippingBytes = false;
firstNonPad = addr;
}
lastNonPad = addr;
consecutivePads = 0;
}
}
// At EOF, add the last block if not skipping bytes already (and input not empty)
if (!skippingBytes && lastNonPad !== -1) {
memMap.set(firstNonPad, bytes.subarray(firstNonPad, l));
}
return memMap;
};
/**
* Returns a new instance of {@linkcode MemoryMap}, containing only data between
* the addresses <tt>address</tt> and <tt>address + length</tt>.
* Behaviour is similar to {@linkcode https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/slice|Array.prototype.slice},
* in that the return value is a portion of the current {@linkcode MemoryMap}.
*
* <br/>
* The returned {@linkcode MemoryMap} might be empty.
*
* <br/>
* Internally, this uses <tt>subarray</tt>, so new memory is not allocated.
*
* @param {Number} address The start address of the slice
* @param {Number} length The length of memory map to slice out
* @return {MemoryMap}
*/
MemoryMap.prototype.slice = function slice (address, length){
var this$1 = this;
if ( length === void 0 ) length = Infinity;
if (length < 0) {
throw new Error('Length of the slice cannot be negative');
}
var sliced = new MemoryMap();
for (var [blockAddr, block] of this$1) {
var blockLength = block.length;
if ((blockAddr + blockLength) >= address && blockAddr < (address + length)) {
var sliceStart = Math.max(address, blockAddr);
var sliceEnd = Math.min(address + length, blockAddr + blockLength);
var sliceLength = sliceEnd - sliceStart;
var relativeSliceStart = sliceStart - blockAddr;
if (sliceLength > 0) {
sliced.set(sliceStart, block.subarray(relativeSliceStart, relativeSliceStart + sliceLength));
}
}
}
return sliced;
};
/**
* Returns a new instance of {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32|Uint8Array}, containing only data between
* the addresses <tt>address</tt> and <tt>address + length</tt>. Any byte without a value
* in the input {@linkcode MemoryMap} will have a value of <tt>padByte</tt>.
*
* <br/>
* This method allocates new memory.
*
* @param {Number} address The start address of the slice
* @param {Number} length The length of memory map to slice out
* @param {Number} [padByte=0xFF] The value of the byte assumed to be used as padding
* @return {MemoryMap}
*/
MemoryMap.prototype.slicePad = function slicePad (address, length, padByte){
var this$1 = this;
if ( padByte === void 0 ) padByte=0xFF;
if (length < 0) {
throw new Error('Length of the slice cannot be negative');
}
var out = (new Uint8Array(length)).fill(padByte);
for (var [blockAddr, block] of this$1) {
var blockLength = block.length;
if ((blockAddr + blockLength) >= address && blockAddr < (address + length)) {
var sliceStart = Math.max(address, blockAddr);
var sliceEnd = Math.min(address + length, blockAddr + blockLength);
var sliceLength = sliceEnd - sliceStart;
var relativeSliceStart = sliceStart - blockAddr;
if (sliceLength > 0) {
out.set(block.subarray(relativeSliceStart, relativeSliceStart + sliceLength), sliceStart - address);
}
}
}
return out;
};
/**
* Checks whether the current memory map contains the one given as a parameter.
*
* <br/>
* "Contains" means that all the offsets that have a byte value in the given
* memory map have a value in the current memory map, and that the byte values
* are the same.
*
* <br/>
* An empty memory map is always contained in any other memory map.
*
* <br/>
* Returns boolean <tt>true</tt> if the memory map is contained, <tt>false</tt>
* otherwise.
*
* @param {MemoryMap} memMap The memory map to check
* @return {Boolean}
*/
MemoryMap.prototype.contains = function contains (memMap) {
var this$1 = this;
for (var [blockAddr, block] of memMap) {
var blockLength = block.length;
var slice = this$1.slice(blockAddr, blockLength).join().get(blockAddr);
if ((!slice) || slice.length !== blockLength ) {
return false;
}
for (var i in block) {
if (block[i] !== slice[i]) {
return false;
}
}
}
return true;
};
Object.defineProperties( MemoryMap.prototype, prototypeAccessors );
return MemoryMap;
})));
//# sourceMappingURL=intel-hex.browser.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,753 +0,0 @@
//Copyright 2014-2015 Google Inc. All rights reserved.
//Use of this source code is governed by a BSD-style
//license that can be found in the LICENSE file or at
//https://developers.google.com/open-source/licenses/bsd
/**
* @fileoverview The U2F api.
*/
'use strict';
/**
* Namespace for the U2F api.
* @type {Object}
*/
var u2f = u2f || {};
/**
* FIDO U2F Javascript API Version
* @number
*/
var js_api_version;
/**
* The U2F extension id
* @const {string}
*/
// The Chrome packaged app extension ID.
// Uncomment this if you want to deploy a server instance that uses
// the package Chrome app and does not require installing the U2F Chrome extension.
u2f.EXTENSION_ID = 'kmendfapggjehodndflmmgagdbamhnfd';
// The U2F Chrome extension ID.
// Uncomment this if you want to deploy a server instance that uses
// the U2F Chrome extension to authenticate.
// u2f.EXTENSION_ID = 'pfboblefjcgdjicmnffhdgionmgcdmne';
/**
* Message types for messsages to/from the extension
* @const
* @enum {string}
*/
u2f.MessageTypes = {
'U2F_REGISTER_REQUEST': 'u2f_register_request',
'U2F_REGISTER_RESPONSE': 'u2f_register_response',
'U2F_SIGN_REQUEST': 'u2f_sign_request',
'U2F_SIGN_RESPONSE': 'u2f_sign_response',
'U2F_GET_API_VERSION_REQUEST': 'u2f_get_api_version_request',
'U2F_GET_API_VERSION_RESPONSE': 'u2f_get_api_version_response'
};
/**
* Response status codes
* @const
* @enum {number}
*/
u2f.ErrorCodes = {
'OK': 0,
'OTHER_ERROR': 1,
'BAD_REQUEST': 2,
'CONFIGURATION_UNSUPPORTED': 3,
'DEVICE_INELIGIBLE': 4,
'TIMEOUT': 5
};
/**
* A message for registration requests
* @typedef {{
* type: u2f.MessageTypes,
* appId: ?string,
* timeoutSeconds: ?number,
* requestId: ?number
* }}
*/
u2f.U2fRequest;
/**
* A message for registration responses
* @typedef {{
* type: u2f.MessageTypes,
* responseData: (u2f.Error | u2f.RegisterResponse | u2f.SignResponse),
* requestId: ?number
* }}
*/
u2f.U2fResponse;
/**
* An error object for responses
* @typedef {{
* errorCode: u2f.ErrorCodes,
* errorMessage: ?string
* }}
*/
u2f.Error;
/**
* Data object for a single sign request.
* @typedef {enum {BLUETOOTH_RADIO, BLUETOOTH_LOW_ENERGY, USB, NFC}}
*/
u2f.Transport;
/**
* Data object for a single sign request.
* @typedef {Array<u2f.Transport>}
*/
u2f.Transports;
/**
* Data object for a single sign request.
* @typedef {{
* version: string,
* challenge: string,
* keyHandle: string,
* appId: string
* }}
*/
u2f.SignRequest;
/**
* Data object for a sign response.
* @typedef {{
* keyHandle: string,
* signatureData: string,
* clientData: string
* }}
*/
u2f.SignResponse;
/**
* Data object for a registration request.
* @typedef {{
* version: string,
* challenge: string
* }}
*/
u2f.RegisterRequest;
/**
* Data object for a registration response.
* @typedef {{
* version: string,
* keyHandle: string,
* transports: Transports,
* appId: string
* }}
*/
u2f.RegisterResponse;
/**
* Data object for a registered key.
* @typedef {{
* version: string,
* keyHandle: string,
* transports: ?Transports,
* appId: ?string
* }}
*/
u2f.RegisteredKey;
/**
* Data object for a get API register response.
* @typedef {{
* js_api_version: number
* }}
*/
u2f.GetJsApiVersionResponse;
//Low level MessagePort API support
/**
* Sets up a MessagePort to the U2F extension using the
* available mechanisms.
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
*/
u2f.getMessagePort = function(callback) {
if (typeof chrome != 'undefined' && chrome.runtime) {
// The actual message here does not matter, but we need to get a reply
// for the callback to run. Thus, send an empty signature request
// in order to get a failure response.
var msg = {
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
signRequests: []
};
chrome.runtime.sendMessage(u2f.EXTENSION_ID, msg, function() {
if (!chrome.runtime.lastError) {
// We are on a whitelisted origin and can talk directly
// with the extension.
u2f.getChromeRuntimePort_(callback);
} else {
// chrome.runtime was available, but we couldn't message
// the extension directly, use iframe
u2f.getIframePort_(callback);
}
});
} else if (u2f.isAndroidChrome_()) {
u2f.getAuthenticatorPort_(callback);
} else if (u2f.isIosChrome_()) {
u2f.getIosPort_(callback);
} else {
// chrome.runtime was not available at all, which is normal
// when this origin doesn't have access to any extensions.
u2f.getIframePort_(callback);
}
};
/**
* Detect chrome running on android based on the browser's useragent.
* @private
*/
u2f.isAndroidChrome_ = function() {
var userAgent = navigator.userAgent;
return userAgent.indexOf('Chrome') != -1 &&
userAgent.indexOf('Android') != -1;
};
/**
* Detect chrome running on iOS based on the browser's platform.
* @private
*/
u2f.isIosChrome_ = function() {
return ["iPhone", "iPad", "iPod"].indexOf(navigator.platform) > -1;
};
/**
* Connects directly to the extension via chrome.runtime.connect.
* @param {function(u2f.WrappedChromeRuntimePort_)} callback
* @private
*/
u2f.getChromeRuntimePort_ = function(callback) {
var port = chrome.runtime.connect(u2f.EXTENSION_ID,
{'includeTlsChannelId': true});
setTimeout(function() {
callback(new u2f.WrappedChromeRuntimePort_(port));
}, 0);
};
/**
* Return a 'port' abstraction to the Authenticator app.
* @param {function(u2f.WrappedAuthenticatorPort_)} callback
* @private
*/
u2f.getAuthenticatorPort_ = function(callback) {
setTimeout(function() {
callback(new u2f.WrappedAuthenticatorPort_());
}, 0);
};
/**
* Return a 'port' abstraction to the iOS client app.
* @param {function(u2f.WrappedIosPort_)} callback
* @private
*/
u2f.getIosPort_ = function(callback) {
setTimeout(function() {
callback(new u2f.WrappedIosPort_());
}, 0);
};
/**
* A wrapper for chrome.runtime.Port that is compatible with MessagePort.
* @param {Port} port
* @constructor
* @private
*/
u2f.WrappedChromeRuntimePort_ = function(port) {
this.port_ = port;
};
/**
* Format and return a sign request compliant with the JS API version supported by the extension.
* @param {Array<u2f.SignRequest>} signRequests
* @param {number} timeoutSeconds
* @param {number} reqId
* @return {Object}
*/
u2f.formatSignRequest_ =
function(appId, challenge, registeredKeys, timeoutSeconds, reqId) {
if (js_api_version === undefined || js_api_version < 1.1) {
// Adapt request to the 1.0 JS API
var signRequests = [];
for (var i = 0; i < registeredKeys.length; i++) {
signRequests[i] = {
version: registeredKeys[i].version,
challenge: challenge,
keyHandle: registeredKeys[i].keyHandle,
appId: appId
};
}
return {
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
signRequests: signRequests,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
}
// JS 1.1 API
return {
type: u2f.MessageTypes.U2F_SIGN_REQUEST,
appId: appId,
challenge: challenge,
registeredKeys: registeredKeys,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
};
/**
* Format and return a register request compliant with the JS API version supported by the extension..
* @param {Array<u2f.SignRequest>} signRequests
* @param {Array<u2f.RegisterRequest>} signRequests
* @param {number} timeoutSeconds
* @param {number} reqId
* @return {Object}
*/
u2f.formatRegisterRequest_ =
function(appId, registeredKeys, registerRequests, timeoutSeconds, reqId) {
if (js_api_version === undefined || js_api_version < 1.1) {
// Adapt request to the 1.0 JS API
for (var i = 0; i < registerRequests.length; i++) {
registerRequests[i].appId = appId;
}
var signRequests = [];
for (var i = 0; i < registeredKeys.length; i++) {
signRequests[i] = {
version: registeredKeys[i].version,
challenge: registerRequests[0],
keyHandle: registeredKeys[i].keyHandle,
appId: appId
};
}
return {
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
signRequests: signRequests,
registerRequests: registerRequests,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
}
// JS 1.1 API
return {
type: u2f.MessageTypes.U2F_REGISTER_REQUEST,
appId: appId,
registerRequests: registerRequests,
registeredKeys: registeredKeys,
timeoutSeconds: timeoutSeconds,
requestId: reqId
};
};
/**
* Posts a message on the underlying channel.
* @param {Object} message
*/
u2f.WrappedChromeRuntimePort_.prototype.postMessage = function(message) {
this.port_.postMessage(message);
};
/**
* Emulates the HTML 5 addEventListener interface. Works only for the
* onmessage event, which is hooked up to the chrome.runtime.Port.onMessage.
* @param {string} eventName
* @param {function({data: Object})} handler
*/
u2f.WrappedChromeRuntimePort_.prototype.addEventListener =
function(eventName, handler) {
var name = eventName.toLowerCase();
if (name == 'message' || name == 'onmessage') {
this.port_.onMessage.addListener(function(message) {
// Emulate a minimal MessageEvent object
handler({'data': message});
});
} else {
console.error('WrappedChromeRuntimePort only supports onMessage');
}
};
/**
* Wrap the Authenticator app with a MessagePort interface.
* @constructor
* @private
*/
u2f.WrappedAuthenticatorPort_ = function() {
this.requestId_ = -1;
this.requestObject_ = null;
}
/**
* Launch the Authenticator intent.
* @param {Object} message
*/
u2f.WrappedAuthenticatorPort_.prototype.postMessage = function(message) {
var intentUrl =
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ +
';S.request=' + encodeURIComponent(JSON.stringify(message)) +
';end';
document.location = intentUrl;
};
/**
* Tells what type of port this is.
* @return {String} port type
*/
u2f.WrappedAuthenticatorPort_.prototype.getPortType = function() {
return "WrappedAuthenticatorPort_";
};
/**
* Emulates the HTML 5 addEventListener interface.
* @param {string} eventName
* @param {function({data: Object})} handler
*/
u2f.WrappedAuthenticatorPort_.prototype.addEventListener = function(eventName, handler) {
var name = eventName.toLowerCase();
if (name == 'message') {
var self = this;
/* Register a callback to that executes when
* chrome injects the response. */
window.addEventListener(
'message', self.onRequestUpdate_.bind(self, handler), false);
} else {
console.error('WrappedAuthenticatorPort only supports message');
}
};
/**
* Callback invoked when a response is received from the Authenticator.
* @param function({data: Object}) callback
* @param {Object} message message Object
*/
u2f.WrappedAuthenticatorPort_.prototype.onRequestUpdate_ =
function(callback, message) {
var messageObject = JSON.parse(message.data);
var intentUrl = messageObject['intentURL'];
var errorCode = messageObject['errorCode'];
var responseObject = null;
if (messageObject.hasOwnProperty('data')) {
responseObject = /** @type {Object} */ (
JSON.parse(messageObject['data']));
}
callback({'data': responseObject});
};
/**
* Base URL for intents to Authenticator.
* @const
* @private
*/
u2f.WrappedAuthenticatorPort_.INTENT_URL_BASE_ =
'intent:#Intent;action=com.google.android.apps.authenticator.AUTHENTICATE';
/**
* Wrap the iOS client app with a MessagePort interface.
* @constructor
* @private
*/
u2f.WrappedIosPort_ = function() {};
/**
* Launch the iOS client app request
* @param {Object} message
*/
u2f.WrappedIosPort_.prototype.postMessage = function(message) {
var str = JSON.stringify(message);
var url = "u2f://auth?" + encodeURI(str);
location.replace(url);
};
/**
* Tells what type of port this is.
* @return {String} port type
*/
u2f.WrappedIosPort_.prototype.getPortType = function() {
return "WrappedIosPort_";
};
/**
* Emulates the HTML 5 addEventListener interface.
* @param {string} eventName
* @param {function({data: Object})} handler
*/
u2f.WrappedIosPort_.prototype.addEventListener = function(eventName, handler) {
var name = eventName.toLowerCase();
if (name !== 'message') {
console.error('WrappedIosPort only supports message');
}
};
/**
* Sets up an embedded trampoline iframe, sourced from the extension.
* @param {function(MessagePort)} callback
* @private
*/
u2f.getIframePort_ = function(callback) {
// Create the iframe
var iframeOrigin = 'chrome-extension://' + u2f.EXTENSION_ID;
var iframe = document.createElement('iframe');
iframe.src = iframeOrigin + '/u2f-comms.html';
iframe.setAttribute('style', 'display:none');
document.body.appendChild(iframe);
var channel = new MessageChannel();
var ready = function(message) {
if (message.data == 'ready') {
channel.port1.removeEventListener('message', ready);
callback(channel.port1);
} else {
console.error('First event on iframe port was not "ready"');
}
};
channel.port1.addEventListener('message', ready);
channel.port1.start();
iframe.addEventListener('load', function() {
// Deliver the port to the iframe and initialize
//
setTimeout(function(){
iframe.contentWindow.postMessage('init', iframeOrigin, [channel.port2]);
},1000);
});
};
//High-level JS API
/**
* Default extension response timeout in seconds.
* @const
*/
u2f.EXTENSION_TIMEOUT_SEC = 30;
/**
* A singleton instance for a MessagePort to the extension.
* @type {MessagePort|u2f.WrappedChromeRuntimePort_}
* @private
*/
u2f.port_ = null;
/**
* Callbacks waiting for a port
* @type {Array<function((MessagePort|u2f.WrappedChromeRuntimePort_))>}
* @private
*/
u2f.waitingForPort_ = [];
/**
* A counter for requestIds.
* @type {number}
* @private
*/
u2f.reqCounter_ = 0;
/**
* A map from requestIds to client callbacks
* @type {Object.<number,(function((u2f.Error|u2f.RegisterResponse))
* |function((u2f.Error|u2f.SignResponse)))>}
* @private
*/
u2f.callbackMap_ = {};
/**
* Creates or retrieves the MessagePort singleton to use.
* @param {function((MessagePort|u2f.WrappedChromeRuntimePort_))} callback
* @private
*/
u2f.getPortSingleton_ = function(callback) {
if (u2f.port_) {
callback(u2f.port_);
} else {
if (u2f.waitingForPort_.length == 0) {
u2f.getMessagePort(function(port) {
u2f.port_ = port;
u2f.port_.addEventListener('message',
/** @type {function(Event)} */ (u2f.responseHandler_));
// Careful, here be async callbacks. Maybe.
while (u2f.waitingForPort_.length)
u2f.waitingForPort_.shift()(u2f.port_);
});
}
u2f.waitingForPort_.push(callback);
}
};
/**
* Handles response messages from the extension.
* @param {MessageEvent.<u2f.Response>} message
* @private
*/
u2f.responseHandler_ = function(message) {
var response = message.data;
var reqId = response['requestId'];
if (!reqId || !u2f.callbackMap_[reqId]) {
console.error('Unknown or missing requestId in response.');
return;
}
var cb = u2f.callbackMap_[reqId];
delete u2f.callbackMap_[reqId];
cb(response['responseData']);
};
/**
* Dispatches an array of sign requests to available U2F tokens.
* If the JS API version supported by the extension is unknown, it first sends a
* message to the extension to find out the supported API version and then it sends
* the sign request.
* @param {string=} appId
* @param {string=} challenge
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.SignResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.sign = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
if (js_api_version === undefined) {
// Send a message to get the extension to JS API version, then send the actual sign request.
u2f.getApiVersion(
function (response) {
js_api_version = response['js_api_version'] === undefined ? 0 : response['js_api_version'];
u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
});
} else {
// We know the JS API version. Send the actual sign request in the supported API version.
u2f.sendSignRequest(appId, challenge, registeredKeys, callback, opt_timeoutSeconds);
}
};
/**
* Dispatches an array of sign requests to available U2F tokens.
* @param {string=} appId
* @param {string=} challenge
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.SignResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.sendSignRequest = function(appId, challenge, registeredKeys, callback, opt_timeoutSeconds) {
u2f.getPortSingleton_(function(port) {
var reqId = ++u2f.reqCounter_;
u2f.callbackMap_[reqId] = callback;
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
var req = u2f.formatSignRequest_(appId, challenge, registeredKeys, timeoutSeconds, reqId);
port.postMessage(req);
});
};
/**
* Dispatches register requests to available U2F tokens. An array of sign
* requests identifies already registered tokens.
* If the JS API version supported by the extension is unknown, it first sends a
* message to the extension to find out the supported API version and then it sends
* the register request.
* @param {string=} appId
* @param {Array<u2f.RegisterRequest>} registerRequests
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.register = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
if (js_api_version === undefined) {
// Send a message to get the extension to JS API version, then send the actual register request.
u2f.getApiVersion(
function (response) {
js_api_version = response['js_api_version'] === undefined ? 0: response['js_api_version'];
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
callback, opt_timeoutSeconds);
});
} else {
// We know the JS API version. Send the actual register request in the supported API version.
u2f.sendRegisterRequest(appId, registerRequests, registeredKeys,
callback, opt_timeoutSeconds);
}
};
/**
* Dispatches register requests to available U2F tokens. An array of sign
* requests identifies already registered tokens.
* @param {string=} appId
* @param {Array<u2f.RegisterRequest>} registerRequests
* @param {Array<u2f.RegisteredKey>} registeredKeys
* @param {function((u2f.Error|u2f.RegisterResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.sendRegisterRequest = function(appId, registerRequests, registeredKeys, callback, opt_timeoutSeconds) {
u2f.getPortSingleton_(function(port) {
var reqId = ++u2f.reqCounter_;
u2f.callbackMap_[reqId] = callback;
var timeoutSeconds = (typeof opt_timeoutSeconds !== 'undefined' ?
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC);
var req = u2f.formatRegisterRequest_(
appId, registeredKeys, registerRequests, timeoutSeconds, reqId);
port.postMessage(req);
});
};
/**
* Dispatches a message to the extension to find out the supported
* JS API version.
* If the user is on a mobile phone and is thus using Google Authenticator instead
* of the Chrome extension, don't send the request and simply return 0.
* @param {function((u2f.Error|u2f.GetJsApiVersionResponse))} callback
* @param {number=} opt_timeoutSeconds
*/
u2f.getApiVersion = function(callback, opt_timeoutSeconds) {
u2f.getPortSingleton_(function(port) {
// If we are using Android Google Authenticator or iOS client app,
// do not fire an intent to ask which JS API version to use.
if (port.getPortType) {
var apiVersion;
switch (port.getPortType()) {
case 'WrappedIosPort_':
case 'WrappedAuthenticatorPort_':
apiVersion = 1.1;
break;
default:
apiVersion = 0;
break;
}
callback({ 'js_api_version': apiVersion });
return;
}
var reqId = ++u2f.reqCounter_;
u2f.callbackMap_[reqId] = callback;
var req = {
type: u2f.MessageTypes.U2F_GET_API_VERSION_REQUEST,
timeoutSeconds: (typeof opt_timeoutSeconds !== 'undefined' ?
opt_timeoutSeconds : u2f.EXTENSION_TIMEOUT_SEC),
requestId: reqId
};
port.postMessage(req);
});
};

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIC5TCCAc2gAwIBAgIJAPAHlj0PtV3iMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xODA3MDgwMTUzMTlaFw0xODA4MDcwMTUzMTlaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAM9uac6J8FOOaVFtQIWmjZKQbUs1k2SBHHYASE5hOqCw9sBjlGix2VC/yuk7
1Nn+N+QM59CkZxR33v2kEK1PTA5Ock/nOHljmnEQrDsKumpp6Jh/L3lB0uwHbTra
RTl7giJ4CRPdMc7EYZnK+TecKMaq5O7X8RG55UJoHZFxVIkc2LoTupW13NfAs09G
JdDSfMvLVy2ZqELlKQ4qR3kLE7RpDss7WKN0bqG3NebI5liG2cDdObP4yd4gBpKk
Dg8nwB4hrDaJaf4MzvrTefnd06Fkx2Fl/H0rZx7HOFMHEX+08fXcby7LMO3VbwEx
ChOlDvHiBUHJDLow67JzNq5lv+8CAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
AQsFAAOCAQEAa5rPgtqb8PJ/ZEE62T6kKaSZeT+ebFqdRvv24WYulbLbZGwryfhq
utzS2peNBq57LlwvI+bOFmPUdIJjHBX2EM9NV3B9nJliM22ri6HuiXFpNcD2ehJV
W2wMCNXZRyHySgM+7JIz/0TYEpJi3gq9P+IHtz4aXBZ5T2M+HsCBdnBci1n9EqTF
9rmt/2RPQxNDIWeAGMlq19cQmmGwVfjxtKIPBGJd7RxkgT3itSjVxb1wyQTlDsZc
0dJaMNGJDtGgpRtOlpC6Xv0SZ66GfsFwa8x5d/mty4m1kKYPu+77A6g9lBhjo8r6
5MFFjFoq0XZuW5bBLbxQUC6qR8+mRI7gNg==
-----END CERTIFICATE-----

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDPbmnOifBTjmlR
bUCFpo2SkG1LNZNkgRx2AEhOYTqgsPbAY5RosdlQv8rpO9TZ/jfkDOfQpGcUd979
pBCtT0wOTnJP5zh5Y5pxEKw7CrpqaeiYfy95QdLsB2062kU5e4IieAkT3THOxGGZ
yvk3nCjGquTu1/ERueVCaB2RcVSJHNi6E7qVtdzXwLNPRiXQ0nzLy1ctmahC5SkO
Kkd5CxO0aQ7LO1ijdG6htzXmyOZYhtnA3Tmz+MneIAaSpA4PJ8AeIaw2iWn+DM76
03n53dOhZMdhZfx9K2cexzhTBxF/tPH13G8uyzDt1W8BMQoTpQ7x4gVByQy6MOuy
czauZb/vAgMBAAECggEBAJpxv14E3meYNVpnWg8o/2gNGRNShLfkWeiSNUQGqLxU
hqSggDOjmAbDhrU50zWsTrkB0uhKQGXBCssXiUNBM4PDbOGJa0cWnjtpCpO7XSk2
SfShXhuOoxkSPoX/VmOCTlaTwU6E9zzYg0MbGYwKPWIOg/5B0kA8hTJ4iutr0mFz
Pc8xu4CkEnyNirDpkCuFvC5sVe9xaLgN6AvhvmKteDYxA/deJhpTx21IwOVyoCt1
axhfqUf8RnQJAShxuGyM0k9f1eErvFipU9DOYlyBWI1F44esfT8EoC2Muo/P8fWH
qgz7UU4SMHgGRj78nq2d+Xh4J+5o+UCLxWFmikzgA+ECgYEA6Jp3xVS868PDkh2U
q14sRo3x+m8QPqFjRV7fYnYKFkikQLZ8NwVXCPAaUeK4+clw1CDawaE/J0exp88z
r7ETKiQWQFhVBXPI4xHVu5GvZ5Cl3Bl5nog6nXA8XFRf2dwQmQWYsx3RLFl/WR1G
v4rqth/Al5fZep0MI8oJ2SesCYMCgYEA5EvDxiZ4XoNmVUROUSSdpsObWUhp7wxE
VsJGXu8sahWzlfF669nJh9oykU0X+VP7pqgzUG8IC2L/urFK4l/QzyklOgslkbLM
2Si2SwkTzqQHa03iDvMa43t6+Q575PMFXWJQCoCO6pvlx/cUrDrj0Zl7jul+4Z6I
qTM+5IHIICUCgYEAhORz128hfMXc/6cc4IeuXiUNAhrgOhg0wlaA5B0yiCa6iSp5
b2oCnt2m+++/5R5c8yVONmBs14FTk9+C1TcjDumNOPf+o13SgUEYTtDeWRpAWK7J
WHTWoEqWgd3G4Y6kgPi8lGFNt7vuUTizyg2RKbqcaz/bhf0iCXSOCd5roQcCgYEA
l5uA/Zwan5lfYgykfdp1H4QTLG2ce/WHhJ4roWvv8NtMkaz11p6g6SkMH9Y0r40I
N5rHyQiTLQyni1Gly0OaJZjLcgpo/qLgyu3wcVAr6TShAK+OCiODncIo3jCyMk75
JxTc1rydLEwvgmYhQmcxV8Y3eaaybh6b3tF7WMCfKEkCgYAqySNWMDbmAkPyCFAV
1EsbYB9Hh8zLJEDfQCwAbCH4BLzRwFx5SpvEQsNML/kf0i/iBPfPoC4cvnMTVzk7
1LY247FCFrtEV5TxObWQJPN45SjOiQeqhpHzBcCu+kh1fz99+VcqZRbmrGV4E4NY
reVBSr7HxTaQzJqfg0Ss/y5J0A==
-----END PRIVATE KEY-----

View File

@ -1,4 +0,0 @@
# Run this to make cert you can import in chrome, then run localhost with HTTPS
openssl req -x509 -out localhost.crt -keyout localhost.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

View File

@ -1,15 +0,0 @@
# https://blog.anvileight.com/posts/simple-python-http-server/#python-3-x
from http.server import HTTPServer, SimpleHTTPRequestHandler
import ssl
host =('localhost', 4443)
httpd = HTTPServer(('localhost', 4443), SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket (httpd.socket,
keyfile="localhost.key",
certfile='localhost.crt', server_side=True)
print('serving on ', host)
httpd.serve_forever()